[
  {
    "path": ".gitignore",
    "content": "__pycache__/\n**/__pycache__/\nbuild/\ninstall/\nlog/\n**.pcd\nuav_vln_data/\nenv_seg_info/\n\nimage/\ntest/\nenv_*/\nuav_vln_data/\ngoogle/\ndatagen/\n\nextlibs/\nbuild/\ninstall/\nsrc/projects/*\n\nextlibs/*\n"
  },
  {
    "path": ".vscode/c_cpp_properties.json",
    "content": "{\n    \"configurations\": [\n        {\n            \"name\": \"Linux\",\n            \"includePath\": [\n                \"${workspaceFolder}/**\",\n                \"/usr/include/eigen3\",\n                \"/usr/include/**\",\n                \"/opt/ros/humble/include/**\"\n            ],\n            \"defines\": [],\n            \"compilerPath\": \"/usr/bin/gcc\",\n            \"cStandard\": \"c17\",\n            \"cppStandard\": \"gnu++17\",\n            \"intelliSenseMode\": \"linux-gcc-x64\"\n        }\n    ],\n    \"version\": 4\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"files.associations\": {\n        \"eigen\": \"cpp\",\n        \"iostream\": \"cpp\",\n        \"cctype\": \"cpp\",\n        \"clocale\": \"cpp\",\n        \"cmath\": \"cpp\",\n        \"csignal\": \"cpp\",\n        \"cstdarg\": \"cpp\",\n        \"cstddef\": \"cpp\",\n        \"cstdio\": \"cpp\",\n        \"cstdlib\": \"cpp\",\n        \"cstring\": \"cpp\",\n        \"ctime\": \"cpp\",\n        \"cwchar\": \"cpp\",\n        \"cwctype\": \"cpp\",\n        \"any\": \"cpp\",\n        \"array\": \"cpp\",\n        \"atomic\": \"cpp\",\n        \"strstream\": \"cpp\",\n        \"bit\": \"cpp\",\n        \"*.tcc\": \"cpp\",\n        \"bitset\": \"cpp\",\n        \"chrono\": \"cpp\",\n        \"codecvt\": \"cpp\",\n        \"compare\": \"cpp\",\n        \"complex\": \"cpp\",\n        \"concepts\": \"cpp\",\n        \"condition_variable\": \"cpp\",\n        \"cstdint\": \"cpp\",\n        \"deque\": \"cpp\",\n        \"forward_list\": \"cpp\",\n        \"list\": \"cpp\",\n        \"map\": \"cpp\",\n        \"set\": \"cpp\",\n        \"string\": \"cpp\",\n        \"unordered_map\": \"cpp\",\n        \"unordered_set\": \"cpp\",\n        \"vector\": \"cpp\",\n        \"exception\": \"cpp\",\n        \"algorithm\": \"cpp\",\n        \"functional\": \"cpp\",\n        \"iterator\": \"cpp\",\n        \"memory\": \"cpp\",\n        \"memory_resource\": \"cpp\",\n        \"numeric\": \"cpp\",\n        \"optional\": \"cpp\",\n        \"random\": \"cpp\",\n        \"ratio\": \"cpp\",\n        \"string_view\": \"cpp\",\n        \"system_error\": \"cpp\",\n        \"tuple\": \"cpp\",\n        \"type_traits\": \"cpp\",\n        \"utility\": \"cpp\",\n        \"fstream\": \"cpp\",\n        \"future\": \"cpp\",\n        \"initializer_list\": \"cpp\",\n        \"iomanip\": \"cpp\",\n        \"iosfwd\": \"cpp\",\n        \"istream\": \"cpp\",\n        \"limits\": \"cpp\",\n        \"mutex\": \"cpp\",\n        \"new\": \"cpp\",\n        \"numbers\": \"cpp\",\n        \"ostream\": \"cpp\",\n        \"semaphore\": \"cpp\",\n        \"shared_mutex\": \"cpp\",\n        \"sstream\": \"cpp\",\n        \"stdexcept\": \"cpp\",\n        \"stop_token\": \"cpp\",\n        \"streambuf\": \"cpp\",\n        \"thread\": \"cpp\",\n        \"cfenv\": \"cpp\",\n        \"cinttypes\": \"cpp\",\n        \"typeindex\": \"cpp\",\n        \"typeinfo\": \"cpp\",\n        \"valarray\": \"cpp\",\n        \"variant\": \"cpp\",\n        \"core\": \"cpp\",\n        \"*.ipp\": \"cpp\",\n        \"coroutine\": \"cpp\",\n        \"source_location\": \"cpp\",\n        \"ranges\": \"cpp\",\n        \"__nullptr\": \"cpp\",\n        \"filesystem\": \"cpp\",\n        \"regex\": \"cpp\",\n        \"hash_map\": \"cpp\",\n        \"*.inc\": \"cpp\",\n        \"adolcforward\": \"cpp\",\n        \"specialfunctions\": \"cpp\",\n        \"alignedvector3\": \"cpp\"\n    },\n    \"idf.pythonInstallPath\": \"/usr/bin/python3\"\n}"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 Shanghai Artificial Intelligence Laboratory\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "\n# OpenFly-Platform\n[![Python](https://img.shields.io/badge/python-3.10-4B8BBE.svg)](https://docs.python.org/3/whatsnew/3.10.html)\n[![ROS2](https://img.shields.io/badge/ROS2-Humble-F39C12.svg)](https://docs.ros.org/en/humble/index.html)\n[![WebSite](https://img.shields.io/badge/Website-online-27AE60.svg)](https://shailab-ipec.github.io/openfly/)\n[![Arxiv](https://img.shields.io/badge/Arxiv-Preprint-A42C24.svg)](https://arxiv.org/abs/2502.18041)\n\n## Introduction\nOpenFly is an innovative platform designed to advance research in outdoor aerial Vision-Language Navigation (VLN) by providing a highly automated toolchain for data collection and a large-scale benchmark dataset. It includes 100k diverse flight trajectories, high-quality visual data generated using cutting-edge rendering techniques, and OpenFly-Agent, a keyframe-aware VLN model, all of which will be open-sourced to support the community.\n\n![cover](images/cover.png)\n\n## Table of Contents\n1. [Prerequisites](#prerequisites)\n2. [Installation](#installation)\n3. [Simulation](#Simulation)\n4. [Toolchain](#Toolchain)\n5. [Training](#Training)\n6. [Evaluation](#Evaluation)\n7. [Test](#Test)\n8. [License](#license)\n\n## Prerequisites\n- Operating System: Linux (Ubuntu 22.04 recommended) Windows (For GTAV)\n- CUDA\n- Python 3.8 or higher (3.10 recommended)\n- ROS2(Humble)\n\n## Installation\n\n### Step 1: Clone the Repository\n```bash\ngit clone https://github.com/SHAILAB-IPEC/OpenFly-Platform.git\ncd OpenFly-Platform\n```\n\n### Step 2: Set Up Python Environment\nIt is recommended to use a conda environment to manage dependencies.\n```bash\nconda create -n openfly python=3.10 -y\nconda activate openfly\npip install -r requirements.txt\npip install packaging ninja\npip install \"flash-attn==2.5.5\" --no-build-isolation\ngit clone https://github.com/kvablack/dlimp\ncd dlimp\npip install -e .\n```\n\n### Step 3: Install Dependencies\n```bash\nsudo apt install xvfb # for headless rendering\nsudo apt install libgoogle-glog-dev \nsudo apt install ros-humble-pcl-ros\nsudo apt install nlohmann-json3-dev\n```\n\n### Step 4: Build\n```bash\ncd tool_ws\ncolcon build --cmake-args -DPython3_EXECUTABLE=/usr/bin/python3\n```\n\n## Simulation\n\n- ​**Simulation Configuration**: \n\n  All configuration files for simulation scenes are located in: [`/OpenFly-Platform/configs/`](./configs/)  \n  Files are ​**named after their corresponding scenario** (e.g., `env_airsim_16.yaml`). ​These YAML files define simulation parameters including IP addresses, ports for scene orchestration, and communication ports for toolchain integration.\n\n1. UE (env_ue_xxx)  \n    * Download files from Huggingface [link](https://huggingface.co/datasets/IPEC-COMMUNITY/OpenFly_DataGen/tree/main/ue)  and unzip, or For custom UE scenes, configure them through UnrealCV plugin integration by following the [doc](http://docs.unrealcv.org/en/master/tutorials/getting_started.html)\n    * Move env_ue_xxx/ folders to OpenFly-Platform/envs/ue/\n\n2. AirSim (env_airsim_xxx)\n    * Download files from Huggingface [link](https://huggingface.co/datasets/IPEC-COMMUNITY/OpenFly_DataGen/tree/main/airsim)  and unzip\n    * Move env_airsim_xxx/ folders to OpenFly-Platform/envs/airsim/\n\n3. 3DGS (env_gs_xxx)\n    * Prepare SIBR_viewers [Refer to this project](https://github.com/graphdeco-inria/hierarchical-3d-gaussians?tab=readme-ov-file#compiling-the-real-time-viewer)\n        ```bash\n        # For Ubuntu 22.04, install dependencies:\n        sudo apt install -y cmake libglew-dev libassimp-dev libboost-all-dev libgtk-3-dev libopencv-dev libglfw3-dev libavdevice-dev libavcodec-dev libeigen3-dev libxxf86vm-dev libembree-dev\n\n        #​ return to the ./OpenFly-Platform directory​\n        cd envs/gs/SIBR_viewers\n\n        cmake . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_IBR_HIERARCHYVIEWER=ON -DBUILD_IBR_ULR=OFF -DBUILD_IBR_DATASET_TOOLS=OFF -DBUILD_IBR_GAUSSIANVIEWER=OFF \n        cmake --build build -j --target install --config Release\n        ```\n\n    * Download files from Huggingface link (Coming soon), or you can generate custom env following [Hierarchial 3DGS](https://github.com/graphdeco-inria/hierarchical-3d-gaussians)\n    * Move env_gs_xxx/ folders to OpenFly-Platform/envs/gs/\n\n4. GTAV (env_game_xxx) ( **! You should configure it under Windows** )\n\n    - Installation of DeepGTAV following this [repo](https://github.com/David0tt/DeepGTAV)\n    - Set the game in windowed mode\n    - In the graphics setting MSAA has to be disabled, otherwise no objects are detected. The correct settings should be loaded by replacing the files in `Documents/Rockstar Games/GTA V/Profiles/`, but keep this in mind if you modify the game settings\n    - If you have a 4k screen and want to capture 4k data (very hardware hungry, but runs smooth on an RTX3090): Set the screen resolution to \"7680x4320DSR\" in NVIDIA GeForce Experience. This increases the buffer sizes for pixel perfect 4k segmentation data.\n\n- ​**Simulation Usage**: \n\n1. For UE, AirSim and 3DGS, launch the env_bridge, and wait for around 20 seconds until you see 'ready to be connected'.\n    ```bash\n    #​ return to the ./OpenFly-Platform directory​\n    conda activate openfly\n    python scripts/sim/env_bridge.py --env env_xx_xxx #such as env_airsim_16b\n    # wait for 20s\n    ```\n    Due to the GPU-intensive nature of rendering in UE projects, we start it in headless mode. If you want to view the UE rendering screen, please comment out the headless-related code in `envs/ue/env_ue_xxx/CitySample.sh`. Note that this may cause the rendering interface to crash.\n\n2. For GTAV\n   - ```pip install pyautogui```\n   - Clone the DeepGTAV repo, put the compiled plugin to the root directory of GTAV.\n   - Start GTA V and enter story mode. Change camera to First-per view (by pressing V) (preferred)\n   - In settings, set the screen resolution to \"7680x4320DSR\" and place the window on top-left corner of your screen\n   - Run [eval_gtav.py](https://github.com/SHAILAB-IPEC/OpenFly-Platform/blob/main/train/eval_gtav.py) for evaluation.\n\n## Toolchain\n\n- ​**Toolchain Configuration**: \n\n  All configuration files for Toolchain are located in: [`/OpenFly-Platform/configs/`](./configs/)  \n  Files are ​**named after their corresponding scenario** (e.g., `env_airsim_16.yaml`). ​For each scenario, the simulation and toolchain share the same configuration file. For the toolchain, the configuration file includes the occupancy map information, trajectory generation parameters, etc. The specific meaning of each parameter can be found in the configuration file [`/OpenFly-Platform/configs/env_airsim_16.yaml`](./configs/env_airsim_16.yaml) .\n\n1. Scene data files\n    * Download files from Huggingface [link](https://huggingface.co/datasets/IPEC-COMMUNITY/OpenFly_DataGen/tree/main/pcd_map) and unzip,or generate point cloud data by following the toolchain configuration.\n    * Move env_xx_xx.pcd files to OpenFly-Platform/scene_data/pcd_map.\n    Raw point cloud files (PCD format) for scenes are stored in: [`/OpenFly-Platform/scene_data/pcd_map/`](./scene_data/pcd_map/). \n    Processed segmentation data (e.g., semantic labels, region partitions) is stored in: [`/OpenFly-Platform/scene_data/seg_map/`](./scene_data/seg_map/).\n\n\n- ​**Toolchain Usage**:  \n\n1. point cloud generation:\n\n    UE:\n    * We utilized the dataset provided by [this project](https://github.com/city-super/MatrixCity), using depth maps and pose information, and synthesized point clouds according to [this code](https://github.com/city-super/MatrixCity/blob/main/scripts/rgbd2pc.py).\n\n    AirSim:\n    * We traversed the simulated environment through a grid, collected lidar point clouds, and transformed the coordinates to the world coordinate system. Finally, we merged all the point clouds to obtain the point cloud of the entire scene. \n    You can adjust the scanning range *MapBound* and interval *LidarDelta* in the corresponding configs/env_airsim_xx.yaml.\n    ```bash\n    #​ return to the ./OpenFly-Platform directory​\n    conda activate openfly\n    bash scripts/toolchain/pcdgen_tool.sh env_xx_xxx #such as env_airsim_16\n    ```\n\n    3DGS:\n    * We directly utilized the sparse point cloud synthesized by colmap, with the file path at <3dgs_dataset>/camera_calibration/aligned/sparse/0/points3D.ply.\n\n    GTAV(Coming soon)\n\n\n2. segmentation generation:\n\n    UE, AirSim, and 3DGS:\n    * Executing the simulation, run the shell script responsible for generation the segmentation.We provide two selectable modes: ​​BEV generation​​ and ​​manual annotation​​.\n    ```bash\n    #​ return to the ./OpenFly-Platform directory​\n    #BEV generation\n    bash scripts/toolchain/seggen_tool.sh env_xx_xxx bev #such as env_airsim_16\n    ​#manual annotation​\n    bash scripts/toolchain/seggen_tool.sh env_xx_xxx manual #such as env_airsim_16\n    ```\n\n    Due to the high GPU requirements for rendering urban scenes in UE, to avoid program crashes, the uploaded binary files are configured to use the lowest rendering settings by default (we found that this does not affect the quality of the generated images). If you wish to render images with higher quality, you can refer to `env_ue_xxx/City_UE52/Saved/Config/Linux/GameUserSettings_best.ini` to modify `GameUserSettings.ini`.\n    \n    GTAV(Coming soon)\n\n\n3. trajectory generation: \n\n    UE, AirSim, and 3DGS:\n    * Executing the simulation, run the shell script responsible for generating the trajectory.\n    ```bash\n    #​ return to the ./OpenFly-Platform directory​\n    bash scripts/toolchain/trajgen_tool.sh env_xx_xxx #such as env_airsim_16\n    ```\n\n    GTAV(Coming soon)\n\n4. instruction generation: \n\n    Our OpenFly dataset has been converted to parquet format. If you want to use this code, you need to convert parquet back to uncompressed format. So we recommend that you use this code to generate your own trajectory instructions.\n    You need to prepare: 1. OpenAI's API  2. Traj in the same format as the OpenFly dataset (a series of images and a jsonl file that records actions)\n    * When using GPT to generate instructions, first configure the \"key\" and \"model\" in the [`/OpenFly-Platform/tool_ws/src/ins_gen/gpt_api_config.json`](./tool_ws/src/ins_gen/gpt_api_config.json) file, modify the data directory in [`/OpenFly-Platform/tool_ws/src/ins_gen/gpt_generation.py`](./tool_ws/src/ins_gen/gpt_generation.py). And use a json file to store all the traj directories you want to generate instructions\n     Then run the shell script responsible for generating the instructions.\n    ```bash\n    #​ return to the ./OpenFly-Platform directory​\n    conda activate openfly\n    python3 tool_ws/src/ins_gen/gpt_generation.py --json Your json PATH --type train/test #\n    ```\n    \n\n## Training:\n\n<!-- #### Clone this repository and navigate to train folder\n\n```bash\ngit clone https://github.com/XXXXX.git\n```\n\nInstall Packages\n\n```Shell\nconda create -n openfly python=3.10 -y\nconda activate openfly\npip install --upgrade pip \npip install -r requirements.txt\n\npip install packaging ninja\npip install \"flash-attn==2.5.5\" --no-build-isolation\n``` -->\n\n#### Data Preparation\n\nThe dataset we use for finetuning openfly-agent is available at [here](https://huggingface.co/datasets/IPEC-COMMUNITY/OpenFly/tree/main).\n\nFor your custom datasets, make sure that you pre-build all the required [TensorFlow Datasets](https://www.tensorflow.org/datasets) (TFDS) datasets.\n\n1. Firstly, prepare the train.json with following format:\n\n   ~~~json\n   [\n       {\n           'image_path': '', \n           'gpt_instruction': 'Proceed towards a medium ...', \n           'action': [9, ...], \n           'index_list': ['image_2', ...], \n           'pos': [[-93.81, 536.11, 74.86], ... ], \n           'yaw': [1.57, 1.57, 1.57, 1.57]\n        }, \n       ...\n   ]\n   ~~~\n\n2. Now you can run [`train/datasets_builder/vln/vln_dataset_builder.py`](./train/dataset_builder/vln/vln_dataset_builder.py) which is used to transfer the generated data format to rlds format.\n\n3. ~~~\n   cd train/datasets/vln\n   tfds build --data_dir <TFDS_DATA_DIR>\n   ~~~\n\n4. For custom dataset, you need to change the data mixture after transferring the data format in [`train/datasets/dataset.py`](./train/datasets/dataset.py) to specify the names of datasets.\n\n```python\n        OXE_NAMED_MIXTURES: Dict[str, List[Tuple[str, float]]] = {\n            \"vln_mix\" : [                                  \n                (\"vln_scene1\", 1.0),                            \n                (\"vln_scene2\", 1.0),\n            ],\n        }\n```\n\n#### Download Pretrained Weights\n\nWe released our pretrained weights of Openfly-Agent which is trained by full fine-tuning [OpenVLA model checkpoint](https://huggingface.co/openvla/openvla-7b-prismatic). Now you can download the weights and directly finetuning your data.\n\n| Model         | Link                                                    |\n| ------------- | ------------------------------------------------------- |\n| Openfly-Agent | [huggingface](https://huggingface.co/IPEC-COMMUNITY/openfly-agent-7b) |\n\n\n#### Train\n\nThe training script are [`train/train.sh`](./train/train.sh). And you need to change following parameters:\n\n1. **grid_size** :refers to the token compress ratio.\n2. **history_frames** :refers to the number of frame as history information, which should be corresponded to your custom dataset setting.\n3. **model_name_or_path** :path to the pretrained VLM weights\n\nOther hyperparameters like \"batch_size\", \"save_steps\" could be customized according to your computation resources.\n\n## Evaluation \nFor ease of comparison, the evaluation results for each scene are shown in the table below.\n\n| Scene        | NE/m  | SR/%  | OSR/% | SPL/% |\n| ------------ | ----- | ----- | ----- | ----- |\n| airsim_16    | 178.9 | 10.6  | 57.9  | 8.30 |\n| airsim_18    | 77.4  | 39.4  | 65.5  | 27.0 |\n| airsim_26    | 113.9 | 30.7  | 55.9  | 15.2 |\n| airsim_gz    | 89.9  | 32.0  | 63.5  | 17.2 |\n| airsim_sh    | 100.2 | 30.0  | 57.1  | 16.5 |\n| airsim_23    | 138.6 | 24.0  | 63.5  | 21.0 |\n| ue_bigcity   | 133.9 | 35.2  | 80.0  | 26.5 |\n| nwpu01       | 75.16 | 33.3  | 62.3  | 30.2 |\n| nwpu02       | 66.9  | 43.8  | 70.4  | 40.8 |\n| sjtu01       | 85.09 | 31.7  | 59.8  | 31.9 |\n| ecust        | 67.13 | 35.1  | 72.0  | 30.5 |\n| stju02       | 129.4 | 22.5  | 53.9  | 22.4 |\n| ue_smallcity | 164.4 | 26.2  | 68.8  | 18.0 |\n| GTA          | 167.2 | 19.0  | 46.0  | 17.0 |\n\nYou can refer to our evaluation script `train/eval.py` to evaluate your openfly-agent.\n\nWe use the `eval_test.json` file as a demonstration to configure the environments that need to be evaluated. You can customize an evaluation configuration file based on the json files available at https://huggingface.co/datasets/IPEC-COMMUNITY/OpenFly/tree/main/Annotation\n\n## Test\n\nMake sure your trained checkpoint dir has two files: \"data_statistics.json\". If not, please copy them from downloaded openfly-agent weights or this [link](https://huggingface.co/IPEC-COMMUNITY/spatialvla-4b-224-sft-fractal).\n\n```python\n\nfrom typing import Dict, List, Optional, Union\nfrom pathlib import Path\nimport numpy as np\nimport torch\nimport cv2\nfrom PIL import Image\nfrom transformers import LlamaTokenizerFast\nfrom transformers import AutoConfig, AutoImageProcessor, AutoModelForVision2Seq, AutoProcessor\nimport os, json\nfrom model.prismatic import PrismaticVLM\nfrom model.overwatch import initialize_overwatch\nfrom model.action_tokenizer import ActionTokenizer\nfrom model.vision_backbone import DinoSigLIPViTBackbone, DinoSigLIPImageTransform\nfrom model.llm_backbone import LLaMa2LLMBackbone\nfrom extern.hf.configuration_prismatic import OpenFlyConfig\nfrom extern.hf.modeling_prismatic import OpenVLAForActionPrediction\nfrom extern.hf.processing_prismatic import PrismaticImageProcessor, PrismaticProcessor\n\n\nAutoConfig.register(\"openvla\", OpenFlyConfig)\nAutoImageProcessor.register(OpenFlyConfig, PrismaticImageProcessor)\nAutoProcessor.register(OpenFlyConfig, PrismaticProcessor)\nAutoModelForVision2Seq.register(OpenFlyConfig, OpenVLAForActionPrediction)\n\n\nmodel_name_or_path=\"IPEC-COMMUNITY/openfly-agent-7b\"\nprocessor = AutoProcessor.from_pretrained(model_name_or_path)\nmodel = AutoModelForVision2Seq.from_pretrained(\n    model_name_or_path, \n    attn_implementation=\"flash_attention_2\",  # [Optional] Requires `flash_attn`\n    torch_dtype=torch.bfloat16, \n    low_cpu_mem_usage=True, \n    trust_remote_code=True,\n).to(\"cuda:0\")\n\nimage = Image.fromarray(cv2.imread(\"example.png\"))\nprompt = \"Take off, go straight pass the river\"\ninputs = processor(prompt, [image, image, image]).to(\"cuda:0\", dtype=torch.bfloat16)\naction = model.predict_action(**inputs, unnorm_key=\"vln_norm\", do_sample=False)\nprint(action)\n```\n## Citation\n```\n@article{OpenFly,\n  author       = {Yunpeng Gao and\n                  Chenhui Li and\n                  Zhongrui You and\n                  Junli Liu and\n                  Zhen Li and\n                  Pengan Chen and\n                  Qizhi Chen and\n                  Zhonghan Tang and\n                  Liansheng Wang and\n                  Penghui Yang and\n                  Yiwen Tang and\n                  Yuhang Tang and\n                  Shuai Liang and\n                  Songyi Zhu and\n                  Ziqin Xiong and\n                  Yifei Su and\n                  Xinyi Ye and\n                  Jianan Li and\n                  Yan Ding and\n                  Dong Wang and\n                  Zhigang Wang and\n                  Bin Zhao and\n                  Xuelong Li},\n  title        = {OpenFly: A Comprehensive Platform for Aerial Vision-Language Navigation},\n  journal      = {CoRR},\n  volume       = {abs/2502.18041},\n  year         = {2025}\n}\n```\n\n\n\n## License\n\n\n\n\n"
  },
  {
    "path": "configs/env_airsim_16.yaml",
    "content": "datagen:\n  env:                        \"env_airsim_16\"                          # Environment name, please keep it the same as the file name\n  data_type:                  \"low_long\"                               # Trajectory type, create a folder with this type as the folder name to store trajectory\n  freq:                       2                                        # Image sampling frequency\n\ntraj_map:\n  DilateRadius:               3                                        # Map dilation radius\n  VoxelWidth:                 1.5                                      # Voxel map resolution\n  MapBound:                   [-1300, 1000, -600, 1000, -200, 200]     # Global voxel map range, within which trajectory generation is also performed.\n  LidarDelta:                 [200, 200, 50]                           # Step size of gridded point cloud sampling\n  pcd_scale_ratio:            1                                        # The scale between the point cloud and the unified coordinate system.\n  traj_scale_ratio:           1                                        # The scale between motion actions and the unified coordinate system.\n  map_elevation:              0                                        # The horizontal height of the ground in the environment\n  min_height_thresh:          6                                        # Minimum altitude threshold for drone flight distance from the ground\n\nseg_map:\n  bev_voxel_size:             4.0                                      # Voxel resolution during semantic segmentation\n\nthread_params:                                                         # Multi-threaded configuration, which allows setting up multiple threads for concurrent data collection and supports parallel simulation scenarios on either a single PC or across multiple PCs.\n  - name: \"thread_1\"                                                   # Thread ID\n    nums: 1000                                                         # Number of collected trajectories\n    min_dis: 150                                                       # The shortest distance from the landmark, multiple landmarks will accumulate\n    max_dis: 180                                                       # The longest distance from the landmark, multiple landmarks will accumulate\n    height_min: 5                                                      # The minimum ground clearance of the generated trajectory.\n    height_max: 10                                                     # The maximum ground clearance of the generated trajectory.\n    aim_port: 9999                                                     # The port that receives commands from the trajectory planner.\n    listen_port: 9998                                                  # The port that receives commands from the simulator.\n    sim_ip: \"127.0.0.1\"                                                # The IP address where the simulation is located can be local or other PCs within the local area network.\n    sim_port: 9000                                                     # The port for connecting to the simulation.\n    aimlandmark_nums: 1                                                # The number of destinations in a single trajectory.\n    add_takeoff_land: true                                             # Whether to include takeoff and landing phases (not applicable in GS scenarios).\n    with_turn: true                                                    # Whether to enforce mandatory turns within a trajectory segment.\n\n  # - name: \"thread_2\"\n  #   nums: 1000\n  #   min_dis: 150\n  #   max_dis: 180\n  #   height_min: 36\n  #   height_max: 66\n  #   aim_port: 9997\n  #   listen_port: 9996\n  #   sim_ip: \"192.168.31.100\"\n  #   ue_port: 9000\n  #   aimmark_nums: 1\n  #   add_takeoff_land: true"
  },
  {
    "path": "configs/env_airsim_18.yaml",
    "content": "datagen:\n  env:                        \"env_airsim_18\"\n  data_type:                  \"test\"\n  freq:                       2   \n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-1000, 2000, -1000, 1500, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            1\n  traj_scale_ratio:           1\n  map_elevation:              -48\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             4.0\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 1000\n    min_dis: 150\n    max_dis: 180\n    height_min: 20\n    height_max: 50\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: true\n    with_turn: true"
  },
  {
    "path": "configs/env_airsim_23.yaml",
    "content": "datagen:\n  env:                        \"env_airsim_23\"\n  data_type:                  \"low_long\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-400, 300, -300, 400, -250, 300]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            1\n  traj_scale_ratio:           1\n  map_elevation:              -6\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             4.0\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 500\n    min_dis: 150\n    max_dis: 180\n    height_min: 20\n    height_max: 50\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: true"
  },
  {
    "path": "configs/env_airsim_26.yaml",
    "content": "datagen:\n  env:                        \"env_airsim_26\"\n  data_type:                  \"low_short\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-1500, 1500, -4000, -1000, -300, 250]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            1\n  traj_scale_ratio:           1\n  map_elevation:              -315\n  min_height_thresh:          10\n\nseg_map:\n  bev_voxel_size:             4.0\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 2000\n    min_dis: 60\n    max_dis: 100\n    height_min: 20\n    height_max: 50\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_airsim_gz.yaml",
    "content": "datagen:\n  env:                        \"env_airsim_gz\"\n  data_type:                  \"low_average\"\n  freq:                       2   \n\ntraj_map:\n  DilateRadius:               4\n  VoxelWidth:                 2\n  MapBound:                   [-3000, 5000, -3000, 2500, -50, 300]\n  LidarDelta:                 [50, 50, 30]\n  pcd_scale_ratio:            1\n  traj_scale_ratio:           1\n  map_elevation:              10\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             4.0\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 4000\n    min_dis: 100\n    max_dis: 150\n    height_min: 20\n    height_max: 50\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: true"
  },
  {
    "path": "configs/env_airsim_sh.yaml",
    "content": "datagen:\n  env:                        \"env_airsim_sh\"\n  data_type:                  \"low_long\"\n  freq:                       2   \n\ntraj_map:\n  DilateRadius:               4\n  VoxelWidth:                 2\n  MapBound:                   [-1800, 1800, -1800, 1800, -50, 100]\n  LidarDelta:                 [100, 100, 50]\n  pcd_scale_ratio:            1\n  traj_scale_ratio:           1\n  map_elevation:              12\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             4.0\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 4000\n    min_dis: 150\n    max_dis: 180\n    height_min: 50\n    height_max: 80\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: true"
  },
  {
    "path": "configs/env_game_gtav.yaml",
    "content": "datagen:\n  env:                        \"env_game_gtav\"\n  data_type:                  \"low_long\"\n  freq:                       2   \n\ntraj_map:\n  DilateRadius:               4\n  VoxelWidth:                 2\n  MapBound:                   [-1800, 1800, -1800, 1800, -50, 200]\n  LidarDelta:                 [100, 100, 50]\n  pcd_scale_ratio:            1\n  traj_scale_ratio:           1\n  map_elevation:              50\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             4.0\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 4000\n    min_dis: 150\n    max_dis: 180\n    height_min: 0\n    height_max: 50\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"192.168.31.143\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_gs_ecust.yaml",
    "content": "datagen:\n  env:                        \"env_gs_ecust\"\n  data_type:                  \"long\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-1000, 500, -600, 800, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            5.6  \n  traj_scale_ratio:           1  \n  map_elevation:              -50\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             5\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 5000\n    min_dis: 50\n    max_dis: 80\n    height_min: 60\n    height_max: 70\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 2\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_gs_nwpu01.yaml",
    "content": "datagen:\n  env:                        \"env_gs_nwpu01\"\n  data_type:                  \"short\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-1000, 500, -600, 800, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            6.65\n  traj_scale_ratio:           1  \n  map_elevation:              -50\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             5\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 2000\n    min_dis: 50\n    max_dis: 80\n    height_min: 70\n    height_max: 80\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_gs_nwpu02.yaml",
    "content": "datagen:\n  env:                        \"env_gs_nwpu02\"\n  data_type:                  \"long\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-1000, 2000, -1000, 1000, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            5.15\n  traj_scale_ratio:           1  \n  map_elevation:              -50\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             5\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 4000\n    min_dis: 80\n    max_dis: 120\n    height_min: 60\n    height_max: 70\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_gs_sjtu01.yaml",
    "content": "datagen:\n  env:                        \"env_gs_sjtu01\"\n  data_type:                  \"long\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-1000, 500, -600, 800, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            5.42  \n  traj_scale_ratio:           1  \n  map_elevation:              -50\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             5\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 5000\n    min_dis: 50\n    max_dis: 80\n    height_min: 60\n    height_max: 70\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 2\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_gs_sjtu02.yaml",
    "content": "datagen:\n  env:                        \"env_gs_sjtu02\"\n  data_type:                  \"low_long\"\n  freq:                       2\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-2000, 2000, -2000, 2000, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            4.75  # sim2real 1:4.75  \n  traj_scale_ratio:           1  # sim2real 1:4.75\n  map_elevation:              -20\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             5\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 5000\n    min_dis: 60\n    max_dis: 120\n    height_min: 55\n    height_max: 60\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: false\n    with_turn: false"
  },
  {
    "path": "configs/env_ue_bigcity.yaml",
    "content": "datagen:\n  env:                        \"env_ue_bigcity\"\n  data_type:                  \"low_short\"\n  freq:                       0.5\n\ntraj_map:\n  DilateRadius:               4\n  VoxelWidth:                 2\n  MapBound:                   [-2000, 2000, -2000, 2000, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            100  # sim2real 1:4.75  \n  traj_scale_ratio:           100  # sim2real 1:4.75\n  map_elevation:              0\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             3\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 6000\n    min_dis: 60\n    max_dis: 100\n    height_min: 50\n    height_max: 70\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: true\n    with_turn: false"
  },
  {
    "path": "configs/env_ue_smallcity.yaml",
    "content": "datagen:\n  env:                        \"env_ue_smallcity\"\n  data_type:                  \"low_short\"\n  freq:                       0.5\n\ntraj_map:\n  DilateRadius:               3\n  VoxelWidth:                 1.5\n  MapBound:                   [-2000, 2000, -2000, 2000, -100, 100]\n  LidarDelta:                 [30, 30, 50]\n  pcd_scale_ratio:            100  # sim2real 1:4.75  \n  traj_scale_ratio:           100  # sim2real 1:4.75\n  map_elevation:              0\n  min_height_thresh:          6\n\nseg_map:\n  bev_voxel_size:             3\n\nthread_params:\n  - name: \"thread_1\"\n    nums: 4000\n    min_dis: 60\n    max_dis: 100\n    height_min: 5\n    height_max: 25\n    aim_port: 9999\n    listen_port: 9998\n    sim_ip: \"127.0.0.1\"\n    sim_port: 9000\n    aimlandmark_nums: 1\n    add_takeoff_land: true\n    with_turn: false"
  },
  {
    "path": "configs/eval_test.json",
    "content": "[\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_13-18-9_387451659\",\n        \"gpt_instruction\": \"Proceed in a straight line toward the large brown building with rectangular windows . Then , slightly turn right and advance forward to the gray building adorned with numerous large rectangular windows . Finally , make a slight left and continue straight to the gray office building characterized by horizontal window blinds and described as mid-rise .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_131812_2\",\n            \"20250109_131813_5\",\n            \"20250109_131815_8\",\n            \"20250109_131816_11\",\n            \"20250109_131818_14\",\n            \"20250109_131819_17\",\n            \"20250109_131820_20\",\n            \"20250109_131821_21\",\n            \"20250109_131822_24\",\n            \"20250109_131823_25\",\n            \"20250109_131823_26\",\n            \"20250109_131824_29\",\n            \"20250109_131826_32\",\n            \"20250109_131827_35\",\n            \"20250109_131828_38\",\n            \"20250109_131829_39\"\n        ],\n        \"pos\": [\n            [\n                -1081.489625285631,\n                -209.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -200.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -191.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -182.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -173.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -164.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -155.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1081.489625285631,\n                -152.81771694551878,\n                30.45824585413169\n            ],\n            [\n                -1078.489625285631,\n                -147.62156452281215,\n                30.45824585413169\n            ],\n            [\n                -1076.989625285631,\n                -145.02348831145883,\n                30.45824585413169\n            ],\n            [\n                -1075.489625285631,\n                -142.42541210010552,\n                30.45824585413169\n            ],\n            [\n                -1075.489625285631,\n                -136.42541210010552,\n                30.45824585413169\n            ],\n            [\n                -1075.489625285631,\n                -127.42541210010552,\n                30.45824585413169\n            ],\n            [\n                -1075.489625285631,\n                -118.42541210010552,\n                30.45824585413169\n            ],\n            [\n                -1075.489625285631,\n                -109.42541210010552,\n                30.45824585413169\n            ],\n            [\n                -1075.489625285631,\n                -106.42541210010552,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_average/2025-1-1_0-49-51_970925433\",\n        \"gpt_instruction\": \"Proceed directly towards the tall gray skyscraper featuring a clock tower on top , then make a slight left turn and advance towards the medium - sized beige commercial building with symmetrical windows and a large billboard .\",\n        \"action\": [\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_004954_2\",\n            \"20250101_004955_3\",\n            \"20250101_004956_5\",\n            \"20250101_004956_6\"\n        ],\n        \"pos\": [\n            [\n                -184.52495401797705,\n                -594.2453859630077,\n                23.016633069974613\n            ],\n            [\n                -183.02495401797705,\n                -596.8434621743611,\n                23.016633069974613\n            ],\n            [\n                -180.42687780662374,\n                -598.3434621743611,\n                23.016633069974613\n            ],\n            [\n                -177.82880159527042,\n                -599.8434621743611,\n                23.016633069974613\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_long/2025-1-9_16-34-14_1894519218\",\n        \"gpt_instruction\": \"Walk directly towards the beige cylindrical mast with a supporting frame on a rooftop , which is a large telecommunications tower . Then , slightly turn left and move forward to a dark grey tall building with antennas on top , categorized as a large skyscraper . Next , slightly turn right and proceed straight to it . Afterward , slightly turn right again and continue straight towards a gray commercial building with medium - sized dimensions , featuring windows with an arch design and consisting of multiple stories .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            8,\n            3,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_163418_2\",\n            \"20250109_163419_5\",\n            \"20250109_163420_8\",\n            \"20250109_163421_9\",\n            \"20250109_163422_12\",\n            \"20250109_163423_14\",\n            \"20250109_163423_15\",\n            \"20250109_163424_18\",\n            \"20250109_163426_21\",\n            \"20250109_163426_22\",\n            \"20250109_163427_25\",\n            \"20250109_163429_28\",\n            \"20250109_163430_31\",\n            \"20250109_163431_34\",\n            \"20250109_163432_37\",\n            \"20250109_163433_39\",\n            \"20250109_163434_40\"\n        ],\n        \"pos\": [\n            [\n                -1055.2651545146757,\n                -220.7180007023675,\n                36.85503417715102\n            ],\n            [\n                -1063.0593831487358,\n                -225.2180007023675,\n                36.85503417715102\n            ],\n            [\n                -1070.853611782796,\n                -229.7180007023675,\n                36.85503417715102\n            ],\n            [\n                -1073.4516879941493,\n                -231.2180007023675,\n                36.85503417715102\n            ],\n            [\n                -1076.4516879941493,\n                -236.41415312507414,\n                36.85503417715102\n            ],\n            [\n                -1079.4516879941493,\n                -241.61030554778077,\n                36.85503417715102\n            ],\n            [\n                -1080.9516879941493,\n                -244.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1086.147840416856,\n                -247.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1093.9420690509162,\n                -251.70838175913408,\n                36.85503417715102\n            ],\n            [\n                -1096.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1102.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1111.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1120.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1129.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1138.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1144.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ],\n            [\n                -1147.5401452622696,\n                -253.20838175913408,\n                36.85503417715102\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_long_updown/2025-1-14_5-10-53_402903177\",\n        \"gpt_instruction\": \"\\\" Ascend to a large gray skyscraper characterized by a tall structure with arched windows and a tiered design . Head straight to it . Slightly turn left and move forward to it . Slightly turn right and proceed straight to a large gray building adorned with architectural details , featuring multiple rectangular windows and decorative patterns . Slightly turn right again and proceed straight to it . Lastly , slightly stop and drop near a medium gray building with arched windows featuring dark frames and frosted glass , identified as a commercial or residential building . \\\"\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            8,\n            3,\n            9,\n            9,\n            8,\n            3,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_051053_0\",\n            \"20250114_051056_1\",\n            \"20250114_051057_2\",\n            \"20250114_051057_3\",\n            \"20250114_051058_4\",\n            \"20250114_051058_5\",\n            \"20250114_051059_6\",\n            \"20250114_051059_7\",\n            \"20250114_051059_8\",\n            \"20250114_051100_9\",\n            \"20250114_051100_10\",\n            \"20250114_051101_11\",\n            \"20250114_051101_12\",\n            \"20250114_051101_13\",\n            \"20250114_051102_14\",\n            \"20250114_051102_15\",\n            \"20250114_051103_16\",\n            \"20250114_051104_19\",\n            \"20250114_051105_22\",\n            \"20250114_051107_25\",\n            \"20250114_051108_28\",\n            \"20250114_051109_31\",\n            \"20250114_051110_34\",\n            \"20250114_051112_37\",\n            \"20250114_051113_40\",\n            \"20250114_051114_43\",\n            \"20250114_051115_46\",\n            \"20250114_051117_49\",\n            \"20250114_051117_50\",\n            \"20250114_051118_51\",\n            \"20250114_051118_53\",\n            \"20250114_051119_54\",\n            \"20250114_051120_57\",\n            \"20250114_051121_60\",\n            \"20250114_051122_62\",\n            \"20250114_051123_63\",\n            \"20250114_051123_65\",\n            \"20250114_051124_66\",\n            \"20250114_051124_67\",\n            \"20250114_051125_68\",\n            \"20250114_051125_69\",\n            \"20250114_051126_70\",\n            \"20250114_051126_71\",\n            \"20250114_051126_72\",\n            \"20250114_051127_73\",\n            \"20250114_051127_74\",\n            \"20250114_051128_75\",\n            \"20250114_051128_76\",\n            \"20250114_051128_77\",\n            \"20250114_051129_78\",\n            \"20250114_051129_79\",\n            \"20250114_051130_80\",\n            \"20250114_051130_81\",\n            \"20250114_051130_82\",\n            \"20250114_051131_83\"\n        ],\n        \"pos\": [\n            [\n                44.013595748004946,\n                -314.919942001743,\n                2.6043382712850516\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                5.604338271285052\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                8.604338271285052\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                11.604338271285052\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                14.604338271285052\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                17.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                20.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                23.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                26.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                29.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                32.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                35.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                38.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                41.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                44.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                47.60433827128505\n            ],\n            [\n                44.013595748004946,\n                -314.919942001743,\n                50.60433827128505\n            ],\n            [\n                47.013595748004946,\n                -309.72378957903635,\n                53.60433827128505\n            ],\n            [\n                51.513595748004946,\n                -301.9295609449764,\n                53.60433827128505\n            ],\n            [\n                56.013595748004946,\n                -294.13533231091645,\n                53.60433827128505\n            ],\n            [\n                60.513595748004946,\n                -286.3411036768565,\n                53.60433827128505\n            ],\n            [\n                65.01359574800495,\n                -278.54687504279656,\n                53.60433827128505\n            ],\n            [\n                69.51359574800495,\n                -270.7526464087366,\n                53.60433827128505\n            ],\n            [\n                74.01359574800495,\n                -262.95841777467666,\n                53.60433827128505\n            ],\n            [\n                78.51359574800495,\n                -255.1641891406167,\n                53.60433827128505\n            ],\n            [\n                83.01359574800495,\n                -247.36996050655677,\n                53.60433827128505\n            ],\n            [\n                87.51359574800495,\n                -239.57573187249682,\n                53.60433827128505\n            ],\n            [\n                92.01359574800495,\n                -231.78150323843687,\n                53.60433827128505\n            ],\n            [\n                93.51359574800495,\n                -229.18342702708355,\n                53.60433827128505\n            ],\n            [\n                95.01359574800495,\n                -226.58535081573024,\n                53.60433827128505\n            ],\n            [\n                95.01359574800495,\n                -223.58535081573024,\n                53.60433827128505\n            ],\n            [\n                95.01359574800495,\n                -220.58535081573024,\n                53.60433827128505\n            ],\n            [\n                98.01359574800495,\n                -215.3891983930236,\n                53.60433827128505\n            ],\n            [\n                102.51359574800495,\n                -207.59496975896366,\n                53.60433827128505\n            ],\n            [\n                105.51359574800495,\n                -202.39881733625703,\n                53.60433827128505\n            ],\n            [\n                107.01359574800495,\n                -199.8007411249037,\n                53.60433827128505\n            ],\n            [\n                109.61167195935826,\n                -198.3007411249037,\n                53.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                53.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                50.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                47.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                44.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                41.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                38.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                35.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                32.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                29.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                26.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                23.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                20.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                17.60433827128505\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                14.604338271285052\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                11.604338271285052\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                8.604338271285052\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                5.604338271285052\n            ],\n            [\n                112.20974817071158,\n                -196.8007411249037,\n                2.6043382712850516\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_average/2025-1-9_22-11-24_113974112\",\n        \"gpt_instruction\": \"Continue straight towards a light gray , medium - sized , flat - roofed building ; then , slightly turn left and proceed straight towards a large , grey building characterized by tall , narrow arched windows and a stepped design .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_221128_2\",\n            \"20250109_221129_5\",\n            \"20250109_221130_8\",\n            \"20250109_221132_11\",\n            \"20250109_221132_12\",\n            \"20250109_221133_15\",\n            \"20250109_221135_18\",\n            \"20250109_221136_21\",\n            \"20250109_221137_24\",\n            \"20250109_221139_27\",\n            \"20250109_221140_30\",\n            \"20250109_221141_33\",\n            \"20250109_221143_36\",\n            \"20250109_221144_39\",\n            \"20250109_221145_42\",\n            \"20250109_221146_43\",\n            \"20250109_221146_44\"\n        ],\n        \"pos\": [\n            [\n                -1124.5326141268692,\n                -159.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1115.5326141268692,\n                -159.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1106.5326141268692,\n                -159.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1097.5326141268692,\n                -159.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1094.5326141268692,\n                -159.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1089.3364617041625,\n                -156.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1081.5422330701024,\n                -152.36369002970173,\n                69.01067085987573\n            ],\n            [\n                -1073.7480044360423,\n                -147.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1065.9537758019821,\n                -143.36369002970173,\n                69.01067085987573\n            ],\n            [\n                -1058.159547167922,\n                -138.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1050.365318533862,\n                -134.36369002970173,\n                69.01067085987573\n            ],\n            [\n                -1042.5710898998018,\n                -129.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1034.7768612657417,\n                -125.36369002970173,\n                69.01067085987573\n            ],\n            [\n                -1026.9826326316816,\n                -120.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1019.1884039976214,\n                -116.36369002970173,\n                69.01067085987573\n            ],\n            [\n                -1016.5903277862681,\n                -114.86369002970173,\n                69.01067085987573\n            ],\n            [\n                -1013.9922515749147,\n                -113.36369002970173,\n                69.01067085987573\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_average/2025-01-18_22-38-15_466040\",\n        \"gpt_instruction\": \"Proceed onward to the large red brick building facade , then ascend to the medium - sized gray urban structure with a brick exterior and fire escape . Slightly turn left and advance to the larger gray mid-rise office building with numerous windows , then descend to the large gray commercial high - rise with multiple windows . Slightly turn right and continue to the medium - sized brown brick residential apartment building with balconies , before slightly going downward , moving straight , and veering right once more to the medium red - brown residential building with a brick texture and metal railings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            4,\n            4,\n            4,\n            4,\n            4,\n            2,\n            9,\n            9,\n            9,\n            1,\n            5,\n            5,\n            5,\n            3,\n            8,\n            5,\n            1,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250118_223818_2\",\n            \"20250118_223820_5\",\n            \"20250118_223823_8\",\n            \"20250118_223825_11\",\n            \"20250118_223828_14\",\n            \"20250118_223830_17\",\n            \"20250118_223832_20\",\n            \"20250118_223835_23\",\n            \"20250118_223836_24\",\n            \"20250118_223836_25\",\n            \"20250118_223837_26\",\n            \"20250118_223838_27\",\n            \"20250118_223839_28\",\n            \"20250118_223840_29\",\n            \"20250118_223842_32\",\n            \"20250118_223844_35\",\n            \"20250118_223847_38\",\n            \"20250118_223848_39\",\n            \"20250118_223848_40\",\n            \"20250118_223849_41\",\n            \"20250118_223850_42\",\n            \"20250118_223851_43\",\n            \"20250118_223852_45\",\n            \"20250118_223853_46\",\n            \"20250118_223854_47\",\n            \"20250118_223855_48\",\n            \"20250118_223856_49\",\n            \"20250118_223856_50\"\n        ],\n        \"pos\": [\n            [\n                163.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                172.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                181.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                190.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                199.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                208.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                217.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                226.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                229.59233464297984,\n                -129.63316193922265,\n                7.511555704926139\n            ],\n            [\n                229.59233464297984,\n                -129.63316193922265,\n                10.511555704926138\n            ],\n            [\n                229.59233464297984,\n                -129.63316193922265,\n                13.511555704926138\n            ],\n            [\n                229.59233464297984,\n                -129.63316193922265,\n                16.51155570492614\n            ],\n            [\n                229.59233464297984,\n                -129.63316193922265,\n                19.51155570492614\n            ],\n            [\n                229.59233464297984,\n                -129.63316193922265,\n                22.51155570492614\n            ],\n            [\n                234.78848706568647,\n                -126.63316193922265,\n                22.51155570492614\n            ],\n            [\n                242.58271569974642,\n                -122.13316193922265,\n                22.51155570492614\n            ],\n            [\n                250.37694433380636,\n                -117.63316193922265,\n                22.51155570492614\n            ],\n            [\n                252.97502054515968,\n                -116.13316193922265,\n                22.51155570492614\n            ],\n            [\n                255.573096756513,\n                -114.63316193922265,\n                22.51155570492614\n            ],\n            [\n                255.573096756513,\n                -114.63316193922265,\n                19.51155570492614\n            ],\n            [\n                255.573096756513,\n                -114.63316193922265,\n                16.51155570492614\n            ],\n            [\n                255.573096756513,\n                -114.63316193922265,\n                13.511555704926138\n            ],\n            [\n                258.573096756513,\n                -114.63316193922265,\n                13.511555704926138\n            ],\n            [\n                261.573096756513,\n                -114.63316193922265,\n                13.511555704926138\n            ],\n            [\n                261.573096756513,\n                -114.63316193922265,\n                10.511555704926138\n            ],\n            [\n                264.573096756513,\n                -114.63316193922265,\n                10.511555704926138\n            ],\n            [\n                264.573096756513,\n                -114.63316193922265,\n                10.511555704926138\n            ],\n            [\n                267.1711729678663,\n                -116.13316193922265,\n                10.511555704926138\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_short/2025-1-9_20-15-25_153162844\",\n        \"gpt_instruction\": \"Proceed straight to the large grey tall building , then slightly turn left and go directly ahead to the skyscraper with a dark grey color and a grid - like window pattern , which is the tallest in the vicinity .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_201528_2\",\n            \"20250109_201530_5\",\n            \"20250109_201531_8\",\n            \"20250109_201532_11\",\n            \"20250109_201534_14\",\n            \"20250109_201534_15\",\n            \"20250109_201535_16\",\n            \"20250109_201536_19\",\n            \"20250109_201537_20\"\n        ],\n        \"pos\": [\n            [\n                -955.9950417928972,\n                -207.11388681855937,\n                87.81275808263148\n            ],\n            [\n                -951.4950417928972,\n                -214.9081154526193,\n                87.81275808263148\n            ],\n            [\n                -946.9950417928972,\n                -222.70234408667926,\n                87.81275808263148\n            ],\n            [\n                -942.4950417928972,\n                -230.4965727207392,\n                87.81275808263148\n            ],\n            [\n                -937.9950417928972,\n                -238.29080135479916,\n                87.81275808263148\n            ],\n            [\n                -936.4950417928972,\n                -240.88887756615247,\n                87.81275808263148\n            ],\n            [\n                -934.9950417928972,\n                -243.4869537775058,\n                87.81275808263148\n            ],\n            [\n                -929.7988893701904,\n                -246.4869537775058,\n                87.81275808263148\n            ],\n            [\n                -927.200813158837,\n                -247.9869537775058,\n                87.81275808263148\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_short/2025-1-9_16-27-8_1857300821\",\n        \"gpt_instruction\": \"Walk straight to a medium - sized beige building , characterized by its multi -story structure with distinct rectangular windows . Slightly turn left and move ahead to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_162711_2\",\n            \"20250109_162713_5\",\n            \"20250109_162714_8\",\n            \"20250109_162715_11\",\n            \"20250109_162717_14\",\n            \"20250109_162718_17\",\n            \"20250109_162718_18\",\n            \"20250109_162719_20\",\n            \"20250109_162720_21\"\n        ],\n        \"pos\": [\n            [\n                -973.118262884755,\n                -566.0240726597523,\n                45.08563280908995\n            ],\n            [\n                -980.9124915188152,\n                -561.5240726597523,\n                45.08563280908995\n            ],\n            [\n                -988.7067201528753,\n                -557.0240726597523,\n                45.08563280908995\n            ],\n            [\n                -996.5009487869354,\n                -552.5240726597523,\n                45.08563280908995\n            ],\n            [\n                -1004.2951774209955,\n                -548.0240726597523,\n                45.08563280908995\n            ],\n            [\n                -1012.0894060550556,\n                -543.5240726597523,\n                45.08563280908995\n            ],\n            [\n                -1014.687482266409,\n                -542.0240726597523,\n                45.08563280908995\n            ],\n            [\n                -1017.687482266409,\n                -542.0240726597523,\n                45.08563280908995\n            ],\n            [\n                -1020.687482266409,\n                -542.0240726597523,\n                45.08563280908995\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_long/2025-1-9_21-25-50_331016259\",\n        \"gpt_instruction\": \"Proceed straight to the grey , tall structure featuring multiple floors and visible antennas on top ; it 's a large building . Slightly turn right and move straight towards the gray buildings , characterized as large urban structures . Then slightly turn left and head straight to the dark gray building , which has a large communication tower with an antenna , classified as a medium - sized commercial building .\",\n        \"action\": [\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_212554_2\",\n            \"20250109_212555_5\",\n            \"20250109_212556_6\",\n            \"20250109_212557_9\",\n            \"20250109_212558_12\",\n            \"20250109_212559_15\",\n            \"20250109_212600_18\",\n            \"20250109_212602_21\",\n            \"20250109_212603_24\",\n            \"20250109_212603_25\",\n            \"20250109_212604_28\",\n            \"20250109_212606_31\",\n            \"20250109_212607_34\",\n            \"20250109_212608_37\",\n            \"20250109_212609_40\",\n            \"20250109_212610_43\",\n            \"20250109_212612_46\",\n            \"20250109_212613_49\",\n            \"20250109_212614_52\",\n            \"20250109_212614_53\",\n            \"20250109_212615_54\"\n        ],\n        \"pos\": [\n            [\n                -1031.8480103343104,\n                118.53964547941229,\n                36.85503417715102\n            ],\n            [\n                -1036.3480103343104,\n                110.74541684535234,\n                36.85503417715102\n            ],\n            [\n                -1037.8480103343104,\n                108.14734063399902,\n                36.85503417715102\n            ],\n            [\n                -1043.0441627570171,\n                105.14734063399902,\n                36.85503417715102\n            ],\n            [\n                -1050.8383913910773,\n                100.64734063399902,\n                36.85503417715102\n            ],\n            [\n                -1058.6326200251374,\n                96.14734063399902,\n                36.85503417715102\n            ],\n            [\n                -1066.4268486591975,\n                91.64734063399902,\n                36.85503417715102\n            ],\n            [\n                -1074.2210772932576,\n                87.14734063399902,\n                36.85503417715102\n            ],\n            [\n                -1082.0153059273177,\n                82.64734063399902,\n                36.85503417715102\n            ],\n            [\n                -1084.613382138671,\n                81.14734063399902,\n                36.85503417715102\n            ],\n            [\n                -1087.613382138671,\n                75.95118821129239,\n                36.85503417715102\n            ],\n            [\n                -1092.113382138671,\n                68.15695957723244,\n                36.85503417715102\n            ],\n            [\n                -1096.613382138671,\n                60.362730943172494,\n                36.85503417715102\n            ],\n            [\n                -1101.113382138671,\n                52.568502309112546,\n                36.85503417715102\n            ],\n            [\n                -1105.613382138671,\n                44.7742736750526,\n                36.85503417715102\n            ],\n            [\n                -1110.113382138671,\n                36.98004504099265,\n                36.85503417715102\n            ],\n            [\n                -1114.613382138671,\n                29.1858164069327,\n                36.85503417715102\n            ],\n            [\n                -1119.113382138671,\n                21.391587772872754,\n                36.85503417715102\n            ],\n            [\n                -1123.613382138671,\n                13.597359138812806,\n                36.85503417715102\n            ],\n            [\n                -1125.113382138671,\n                10.99928292745949,\n                36.85503417715102\n            ],\n            [\n                -1126.613382138671,\n                8.401206716106174,\n                36.85503417715102\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_15-0-36_1930772757\",\n        \"gpt_instruction\": \"Move forward to the medium - sized gray building with rectangular windows and a flat rooftop . Then , slightly turn left and continue straight to another medium - sized gray structure that stands out with its large arched windows adorned with decorative trims , contrasting with the surrounding taller skyscrapers , identified as a historical - style building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_150040_2\",\n            \"20250109_150041_5\",\n            \"20250109_150043_8\",\n            \"20250109_150044_11\",\n            \"20250109_150045_14\",\n            \"20250109_150046_16\",\n            \"20250109_150047_17\",\n            \"20250109_150048_20\",\n            \"20250109_150049_23\",\n            \"20250109_150051_26\",\n            \"20250109_150052_29\",\n            \"20250109_150054_32\",\n            \"20250109_150055_35\",\n            \"20250109_150055_36\",\n            \"20250109_150056_37\"\n        ],\n        \"pos\": [\n            [\n                -1249.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1240.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1231.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1222.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1213.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1207.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1204.923451799462,\n                -120.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1199.7272993767554,\n                -117.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1191.9330707426952,\n                -113.39097272153214,\n                30.45824585413169\n            ],\n            [\n                -1184.1388421086351,\n                -108.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1176.344613474575,\n                -104.39097272153214,\n                30.45824585413169\n            ],\n            [\n                -1168.5503848405149,\n                -99.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1160.7561562064548,\n                -95.39097272153214,\n                30.45824585413169\n            ],\n            [\n                -1158.1580799951014,\n                -93.89097272153214,\n                30.45824585413169\n            ],\n            [\n                -1155.560003783748,\n                -92.39097272153214,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_19-10-56_2107654819\",\n        \"gpt_instruction\": \"Proceed straight towards a tall and prominent gray and beige high - rise building characterized by its rectangular structure with multiple floors and orange window shades , making it stand out among nearby structures . Slightly turn right and head straight to another building , this time light brown in color , featuring large rectangular windows . Then , slightly turn left and continue walking straight to reach it . Finally , slightly turn left again and move forward to an office building distinguished by its tall appearance and decorative window frames and cornices .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_191100_2\",\n            \"20250109_191101_5\",\n            \"20250109_191103_8\",\n            \"20250109_191104_11\",\n            \"20250109_191104_12\",\n            \"20250109_191105_13\",\n            \"20250109_191106_16\",\n            \"20250109_191108_19\",\n            \"20250109_191109_22\",\n            \"20250109_191111_25\",\n            \"20250109_191111_26\",\n            \"20250109_191113_29\",\n            \"20250109_191114_32\",\n            \"20250109_191115_35\",\n            \"20250109_191116_36\",\n            \"20250109_191117_39\",\n            \"20250109_191117_40\"\n        ],\n        \"pos\": [\n            [\n                -705.6041405073015,\n                -159.40623218583377,\n                30.45824585413169\n            ],\n            [\n                -713.3983691413616,\n                -163.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -721.1925977754217,\n                -168.40623218583377,\n                30.45824585413169\n            ],\n            [\n                -728.9868264094819,\n                -172.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -731.5849026208352,\n                -174.40623218583377,\n                30.45824585413169\n            ],\n            [\n                -734.1829788321886,\n                -175.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -740.1829788321886,\n                -175.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -749.1829788321886,\n                -175.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -758.1829788321886,\n                -175.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -767.1829788321886,\n                -175.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -770.1829788321886,\n                -175.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -775.3791312548954,\n                -178.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -783.1733598889555,\n                -183.40623218583377,\n                30.45824585413169\n            ],\n            [\n                -790.9675885230156,\n                -187.90623218583377,\n                30.45824585413169\n            ],\n            [\n                -793.565664734369,\n                -189.40623218583377,\n                30.45824585413169\n            ],\n            [\n                -796.565664734369,\n                -194.6023846085404,\n                30.45824585413169\n            ],\n            [\n                -798.065664734369,\n                -197.20046081989372,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_short/2025-1-9_12-21-53_1470503465\",\n        \"gpt_instruction\": \"Proceed directly ahead to the cream - colored skyscraper with tall , grid - like windows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_122157_2\",\n            \"20250109_122158_5\",\n            \"20250109_122159_8\",\n            \"20250109_122201_11\",\n            \"20250109_122202_14\",\n            \"20250109_122203_15\",\n            \"20250109_122203_16\"\n        ],\n        \"pos\": [\n            [\n                -703.9856063812126,\n                -296.25137659595214,\n                45.08563280908995\n            ],\n            [\n                -711.7798350152727,\n                -300.75137659595214,\n                45.08563280908995\n            ],\n            [\n                -719.5740636493329,\n                -305.25137659595214,\n                45.08563280908995\n            ],\n            [\n                -727.368292283393,\n                -309.75137659595214,\n                45.08563280908995\n            ],\n            [\n                -735.1625209174531,\n                -314.25137659595214,\n                45.08563280908995\n            ],\n            [\n                -737.7605971288065,\n                -315.75137659595214,\n                45.08563280908995\n            ],\n            [\n                -740.3586733401598,\n                -317.25137659595214,\n                45.08563280908995\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_15-26-9_395279207\",\n        \"gpt_instruction\": \"Start by walking directly towards a large gray skyscraper characterized by a pointed roof , then slightly turn right and continue moving forward to reach a gray commercial building featuring rectangular windows with dark frames arranged in a grid pattern .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_152613_2\",\n            \"20250109_152614_5\",\n            \"20250109_152616_8\",\n            \"20250109_152617_11\",\n            \"20250109_152618_14\",\n            \"20250109_152619_17\",\n            \"20250109_152621_20\",\n            \"20250109_152622_23\",\n            \"20250109_152624_26\",\n            \"20250109_152624_27\",\n            \"20250109_152625_30\",\n            \"20250109_152627_33\",\n            \"20250109_152628_36\",\n            \"20250109_152628_37\",\n            \"20250109_152629_38\"\n        ],\n        \"pos\": [\n            [\n                -962.6646818256595,\n                -341.6682681144626,\n                30.45824585413169\n            ],\n            [\n                -958.1646818256595,\n                -349.46249674852254,\n                30.45824585413169\n            ],\n            [\n                -953.6646818256595,\n                -357.2567253825825,\n                30.45824585413169\n            ],\n            [\n                -949.1646818256595,\n                -365.05095401664244,\n                30.45824585413169\n            ],\n            [\n                -944.6646818256595,\n                -372.8451826507024,\n                30.45824585413169\n            ],\n            [\n                -940.1646818256595,\n                -380.63941128476233,\n                30.45824585413169\n            ],\n            [\n                -935.6646818256595,\n                -388.4336399188223,\n                30.45824585413169\n            ],\n            [\n                -931.1646818256595,\n                -396.2278685528822,\n                30.45824585413169\n            ],\n            [\n                -926.6646818256595,\n                -404.0220971869422,\n                30.45824585413169\n            ],\n            [\n                -925.1646818256595,\n                -406.6201733982955,\n                30.45824585413169\n            ],\n            [\n                -925.1646818256595,\n                -412.6201733982955,\n                30.45824585413169\n            ],\n            [\n                -925.1646818256595,\n                -421.6201733982955,\n                30.45824585413169\n            ],\n            [\n                -925.1646818256595,\n                -430.6201733982955,\n                30.45824585413169\n            ],\n            [\n                -925.1646818256595,\n                -433.6201733982955,\n                30.45824585413169\n            ],\n            [\n                -925.1646818256595,\n                -436.6201733982955,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_average/2025-1-9_16-57-31_515530019\",\n        \"gpt_instruction\": \"Proceed directly to the grey urban rooftop featuring antennas and equipment on a medium - sized building , then slightly turn left and head straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_165735_2\",\n            \"20250109_165736_5\",\n            \"20250109_165737_8\",\n            \"20250109_165739_11\",\n            \"20250109_165740_14\",\n            \"20250109_165741_17\",\n            \"20250109_165743_20\",\n            \"20250109_165744_23\",\n            \"20250109_165745_26\",\n            \"20250109_165747_28\",\n            \"20250109_165747_29\",\n            \"20250109_165748_32\",\n            \"20250109_165750_35\",\n            \"20250109_165751_38\",\n            \"20250109_165753_41\",\n            \"20250109_165754_44\",\n            \"20250109_165755_46\",\n            \"20250109_165755_47\"\n        ],\n        \"pos\": [\n            [\n                -1076.1282696329586,\n                9.648794941843079,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                0.6487949418430787,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -8.351205058156921,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -17.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -26.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -35.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -44.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -53.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -62.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -68.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1076.1282696329586,\n                -71.35120505815692,\n                69.01067085987573\n            ],\n            [\n                -1073.1282696329586,\n                -76.54735748086355,\n                69.01067085987573\n            ],\n            [\n                -1068.6282696329586,\n                -84.3415861149235,\n                69.01067085987573\n            ],\n            [\n                -1064.1282696329586,\n                -92.13581474898345,\n                69.01067085987573\n            ],\n            [\n                -1059.6282696329586,\n                -99.9300433830434,\n                69.01067085987573\n            ],\n            [\n                -1055.1282696329586,\n                -107.72427201710335,\n                69.01067085987573\n            ],\n            [\n                -1052.1282696329586,\n                -112.92042443980998,\n                69.01067085987573\n            ],\n            [\n                -1050.6282696329586,\n                -115.5185006511633,\n                69.01067085987573\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_average_updown/2025-1-14_8-48-9_962980710\",\n        \"gpt_instruction\": \"Start by ascending to a skyscraper characterized by a brown color and a brick pattern featuring a large billboard . Continue by walking straight to a large grey building with prominent windows equipped with shutters . Then , slightly turn left slightly and proceed to it . Next , head right to a large skyscraper with a grey hue and a tall structure showcasing a grid pattern of windows and rooftop antennas . Keep moving straight to a brown building distinguished by its rectangular windows . Finally , slightly halt and drop - off at a commercial establishment with light grey tones , boasting arched entrances , large glass windows , brick patterns , and green and black accents .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            3,\n            3,\n            9,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_084809_0\",\n            \"20250114_084812_1\",\n            \"20250114_084813_2\",\n            \"20250114_084813_3\",\n            \"20250114_084814_4\",\n            \"20250114_084814_5\",\n            \"20250114_084815_6\",\n            \"20250114_084815_7\",\n            \"20250114_084815_8\",\n            \"20250114_084816_9\",\n            \"20250114_084817_12\",\n            \"20250114_084818_15\",\n            \"20250114_084820_18\",\n            \"20250114_084821_21\",\n            \"20250114_084822_24\",\n            \"20250114_084824_27\",\n            \"20250114_084825_30\",\n            \"20250114_084825_31\",\n            \"20250114_084827_34\",\n            \"20250114_084828_37\",\n            \"20250114_084829_40\",\n            \"20250114_084831_43\",\n            \"20250114_084831_44\",\n            \"20250114_084831_45\",\n            \"20250114_084833_48\",\n            \"20250114_084833_49\",\n            \"20250114_084834_50\",\n            \"20250114_084834_51\",\n            \"20250114_084834_52\",\n            \"20250114_084835_53\",\n            \"20250114_084835_54\",\n            \"20250114_084836_55\",\n            \"20250114_084836_56\",\n            \"20250114_084837_57\",\n            \"20250114_084837_58\",\n            \"20250114_084837_59\",\n            \"20250114_084838_60\"\n        ],\n        \"pos\": [\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                2.0330409747630256\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                5.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                8.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                11.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                14.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                17.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                20.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                23.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                26.033040974763026\n            ],\n            [\n                -938.4105234862673,\n                -301.219992641664,\n                29.033040974763026\n            ],\n            [\n                -941.4105234862673,\n                -306.41614506437065,\n                32.033040974763026\n            ],\n            [\n                -945.9105234862673,\n                -314.2103736984306,\n                32.033040974763026\n            ],\n            [\n                -950.4105234862673,\n                -322.00460233249055,\n                32.033040974763026\n            ],\n            [\n                -954.9105234862673,\n                -329.7988309665505,\n                32.033040974763026\n            ],\n            [\n                -959.4105234862673,\n                -337.59305960061045,\n                32.033040974763026\n            ],\n            [\n                -963.9105234862673,\n                -345.3872882346704,\n                32.033040974763026\n            ],\n            [\n                -968.4105234862673,\n                -353.18151686873034,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -355.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -361.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -370.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -379.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -388.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -391.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -969.9105234862673,\n                -391.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -975.106675908974,\n                -394.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -977.7047521203274,\n                -396.27959308008366,\n                32.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                32.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                29.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                26.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                23.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                20.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                17.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                14.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                11.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                8.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                5.033040974763026\n            ],\n            [\n                -980.3028283316808,\n                -397.77959308008366,\n                2.0330409747630256\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_long/2025-1-1_2-36-19_262692685\",\n        \"gpt_instruction\": \"Proceed ahead to a gray , multi - story apartment building featuring rectangular windows , medium - sized compared to the surrounding high - rise structures . Slightly turn left and go straight to a grey residential building with a rooftop and fire escapes , also medium - sized . Slightly turn right and continue directly ahead to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_023622_2\",\n            \"20250101_023624_5\",\n            \"20250101_023625_8\",\n            \"20250101_023626_11\",\n            \"20250101_023628_14\",\n            \"20250101_023629_17\",\n            \"20250101_023630_20\",\n            \"20250101_023631_23\",\n            \"20250101_023633_26\",\n            \"20250101_023634_29\",\n            \"20250101_023635_32\",\n            \"20250101_023637_35\",\n            \"20250101_023638_38\",\n            \"20250101_023639_39\",\n            \"20250101_023639_40\",\n            \"20250101_023640_43\",\n            \"20250101_023642_46\",\n            \"20250101_023643_49\",\n            \"20250101_023644_52\",\n            \"20250101_023645_53\",\n            \"20250101_023646_55\",\n            \"20250101_023646_56\"\n        ],\n        \"pos\": [\n            [\n                -1289.8284289240974,\n                100.24775133914227,\n                19.743318291823833\n            ],\n            [\n                -1285.3284289240974,\n                92.45352270508232,\n                19.743318291823833\n            ],\n            [\n                -1280.8284289240974,\n                84.65929407102237,\n                19.743318291823833\n            ],\n            [\n                -1276.3284289240974,\n                76.86506543696242,\n                19.743318291823833\n            ],\n            [\n                -1271.8284289240974,\n                69.07083680290248,\n                19.743318291823833\n            ],\n            [\n                -1267.3284289240974,\n                61.27660816884253,\n                19.743318291823833\n            ],\n            [\n                -1262.8284289240974,\n                53.48237953478258,\n                19.743318291823833\n            ],\n            [\n                -1258.3284289240974,\n                45.68815090072263,\n                19.743318291823833\n            ],\n            [\n                -1253.8284289240974,\n                37.893922266662685,\n                19.743318291823833\n            ],\n            [\n                -1249.3284289240974,\n                30.099693632602737,\n                19.743318291823833\n            ],\n            [\n                -1244.8284289240974,\n                22.30546499854279,\n                19.743318291823833\n            ],\n            [\n                -1240.3284289240974,\n                14.51123636448284,\n                19.743318291823833\n            ],\n            [\n                -1235.8284289240974,\n                6.717007730422893,\n                19.743318291823833\n            ],\n            [\n                -1234.3284289240974,\n                4.1189315190695766,\n                19.743318291823833\n            ],\n            [\n                -1232.8284289240974,\n                1.5208553077162605,\n                19.743318291823833\n            ],\n            [\n                -1227.6322765013906,\n                -1.479144692283739,\n                19.743318291823833\n            ],\n            [\n                -1219.8380478673305,\n                -5.979144692283739,\n                19.743318291823833\n            ],\n            [\n                -1212.0438192332704,\n                -10.479144692283738,\n                19.743318291823833\n            ],\n            [\n                -1204.2495905992103,\n                -14.979144692283738,\n                19.743318291823833\n            ],\n            [\n                -1201.6515143878569,\n                -16.479144692283736,\n                19.743318291823833\n            ],\n            [\n                -1200.1515143878569,\n                -19.077220903637052,\n                19.743318291823833\n            ],\n            [\n                -1198.6515143878569,\n                -21.675297114990368,\n                19.743318291823833\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982893,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982888,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average_updown/2025-1-14_2-2-16_1335354340\",\n        \"gpt_instruction\": \"Climb towards a light gray skyscraper with grid - like windows and balconies , then proceed straight to a large beige building featuring rectangular windows with arched designs on its lower floors . Slightly turn left and go directly ahead to it . Slightly turn right and advance forward to it . Slightly turn left again and go directly ahead to it . Finally , halt and descend towards a large stone - facade building with a gray color , detailed arched windows framed by stone , and a red interior glow visible behind some windows .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_020216_0\",\n            \"20250114_020219_1\",\n            \"20250114_020219_2\",\n            \"20250114_020220_3\",\n            \"20250114_020220_4\",\n            \"20250114_020221_5\",\n            \"20250114_020221_6\",\n            \"20250114_020222_7\",\n            \"20250114_020222_8\",\n            \"20250114_020222_9\",\n            \"20250114_020223_10\",\n            \"20250114_020223_11\",\n            \"20250114_020224_12\",\n            \"20250114_020224_13\",\n            \"20250114_020225_14\",\n            \"20250114_020225_15\",\n            \"20250114_020226_16\",\n            \"20250114_020226_17\",\n            \"20250114_020226_18\",\n            \"20250114_020227_19\",\n            \"20250114_020228_22\",\n            \"20250114_020230_25\",\n            \"20250114_020231_28\",\n            \"20250114_020231_29\",\n            \"20250114_020232_30\",\n            \"20250114_020233_33\",\n            \"20250114_020235_36\",\n            \"20250114_020236_39\",\n            \"20250114_020237_41\",\n            \"20250114_020237_42\",\n            \"20250114_020238_45\",\n            \"20250114_020240_48\",\n            \"20250114_020241_51\",\n            \"20250114_020242_54\",\n            \"20250114_020243_55\",\n            \"20250114_020244_58\",\n            \"20250114_020244_59\",\n            \"20250114_020245_60\",\n            \"20250114_020245_61\",\n            \"20250114_020246_62\",\n            \"20250114_020246_63\",\n            \"20250114_020246_64\",\n            \"20250114_020247_65\",\n            \"20250114_020247_66\",\n            \"20250114_020248_67\",\n            \"20250114_020248_68\",\n            \"20250114_020248_69\",\n            \"20250114_020249_70\",\n            \"20250114_020249_71\",\n            \"20250114_020250_72\",\n            \"20250114_020250_73\",\n            \"20250114_020251_74\",\n            \"20250114_020251_75\",\n            \"20250114_020252_76\",\n            \"20250114_020252_77\",\n            \"20250114_020253_78\",\n            \"20250114_020253_79\"\n        ],\n        \"pos\": [\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                2.3981494902854905\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                5.3981494902854905\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                8.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                11.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                14.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                17.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                20.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                23.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                26.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                29.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                32.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                35.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                38.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                41.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                44.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                47.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                50.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                53.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                56.39814949028549\n            ],\n            [\n                -777.4934132378748,\n                -429.8785842701283,\n                59.39814949028549\n            ],\n            [\n                -782.6895656605816,\n                -432.8785842701283,\n                62.39814949028549\n            ],\n            [\n                -790.4837942946417,\n                -437.3785842701283,\n                62.39814949028549\n            ],\n            [\n                -798.2780229287018,\n                -441.8785842701283,\n                62.39814949028549\n            ],\n            [\n                -800.8760991400552,\n                -443.3785842701283,\n                62.39814949028549\n            ],\n            [\n                -803.4741753514086,\n                -444.8785842701283,\n                62.39814949028549\n            ],\n            [\n                -806.4741753514086,\n                -450.0747366928349,\n                62.39814949028549\n            ],\n            [\n                -810.9741753514086,\n                -457.86896532689485,\n                62.39814949028549\n            ],\n            [\n                -815.4741753514086,\n                -465.6631939609548,\n                62.39814949028549\n            ],\n            [\n                -818.4741753514086,\n                -470.85934638366143,\n                62.39814949028549\n            ],\n            [\n                -819.9741753514086,\n                -473.45742259501475,\n                62.39814949028549\n            ],\n            [\n                -825.1703277741153,\n                -476.45742259501475,\n                62.39814949028549\n            ],\n            [\n                -832.9645564081754,\n                -480.95742259501475,\n                62.39814949028549\n            ],\n            [\n                -840.7587850422356,\n                -485.45742259501475,\n                62.39814949028549\n            ],\n            [\n                -848.5530136762957,\n                -489.95742259501475,\n                62.39814949028549\n            ],\n            [\n                -851.151089887649,\n                -491.45742259501475,\n                62.39814949028549\n            ],\n            [\n                -854.151089887649,\n                -496.6535750177214,\n                62.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                62.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                59.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                56.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                53.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                50.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                47.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                44.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                41.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                38.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                35.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                32.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                29.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                26.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                23.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                20.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                17.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                14.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                11.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                8.39814949028549\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                5.3981494902854905\n            ],\n            [\n                -855.651089887649,\n                -499.2516512290747,\n                2.3981494902854905\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_long_updown/2025-01-19_05-00-37_230566\",\n        \"gpt_instruction\": \"Proceed upwards to a light brown , large , rectangular building with numerous windows . Move forward to a gray , large commercial building equipped with roof devices , including antennas and ventilation systems . Ascend to another tall , gray skyscraper with large windows . Advance ahead to it . Slightly descend and turn left to continue straight to it . Descend to it . Briefly pause and then move downwards to a small , dark residential building with windows featuring horizontal blinds .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            1,\n            4,\n            4,\n            4,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            5,\n            2,\n            8,\n            5,\n            5,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250119_050039_0\",\n            \"20250119_050039_1\",\n            \"20250119_050040_2\",\n            \"20250119_050041_3\",\n            \"20250119_050042_4\",\n            \"20250119_050043_5\",\n            \"20250119_050044_6\",\n            \"20250119_050044_7\",\n            \"20250119_050045_8\",\n            \"20250119_050046_9\",\n            \"20250119_050047_10\",\n            \"20250119_050049_13\",\n            \"20250119_050052_16\",\n            \"20250119_050054_19\",\n            \"20250119_050055_20\",\n            \"20250119_050056_21\",\n            \"20250119_050057_22\",\n            \"20250119_050058_23\",\n            \"20250119_050100_26\",\n            \"20250119_050103_29\",\n            \"20250119_050105_32\",\n            \"20250119_050108_35\",\n            \"20250119_050110_38\",\n            \"20250119_050113_41\",\n            \"20250119_050115_44\",\n            \"20250119_050117_47\",\n            \"20250119_050120_50\",\n            \"20250119_050122_53\",\n            \"20250119_050125_56\",\n            \"20250119_050127_59\",\n            \"20250119_050130_62\",\n            \"20250119_050132_65\",\n            \"20250119_050133_66\",\n            \"20250119_050134_67\",\n            \"20250119_050135_68\",\n            \"20250119_050136_70\",\n            \"20250119_050137_71\",\n            \"20250119_050138_72\",\n            \"20250119_050139_73\",\n            \"20250119_050139_74\",\n            \"20250119_050140_75\",\n            \"20250119_050141_76\",\n            \"20250119_050142_77\",\n            \"20250119_050143_78\",\n            \"20250119_050143_79\",\n            \"20250119_050144_80\",\n            \"20250119_050145_81\",\n            \"20250119_050146_82\",\n            \"20250119_050147_83\",\n            \"20250119_050147_84\"\n        ],\n        \"pos\": [\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                0.7801447212512329,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                3.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                6.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                9.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                12.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                15.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                18.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                21.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                24.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                27.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -960.6722684826907,\n                -372.764736692222,\n                30.780144721251233,\n                -2.0943951023931953\n            ],\n            [\n                -963.6722684826907,\n                -377.96088911492865,\n                33.78014472125123\n            ],\n            [\n                -968.1722684826907,\n                -385.7551177489886,\n                33.78014472125123\n            ],\n            [\n                -972.6722684826907,\n                -393.54934638304854,\n                33.78014472125123\n            ],\n            [\n                -974.1722684826907,\n                -396.14742259440186,\n                33.78014472125123\n            ],\n            [\n                -975.6722684826907,\n                -398.7454988057552,\n                33.78014472125123\n            ],\n            [\n                -975.6722684826907,\n                -398.7454988057552,\n                36.78014472125123\n            ],\n            [\n                -975.6722684826907,\n                -398.7454988057552,\n                39.78014472125123\n            ],\n            [\n                -978.6722684826907,\n                -403.9416512284618,\n                42.78014472125123\n            ],\n            [\n                -983.1722684826907,\n                -411.73587986252176,\n                42.78014472125123\n            ],\n            [\n                -987.6722684826907,\n                -419.5301084965817,\n                42.78014472125123\n            ],\n            [\n                -992.1722684826907,\n                -427.32433713064165,\n                42.78014472125123\n            ],\n            [\n                -996.6722684826907,\n                -435.1185657647016,\n                42.78014472125123\n            ],\n            [\n                -1001.1722684826907,\n                -442.91279439876155,\n                42.78014472125123\n            ],\n            [\n                -1005.6722684826907,\n                -450.7070230328215,\n                42.78014472125123\n            ],\n            [\n                -1010.1722684826907,\n                -458.50125166688144,\n                42.78014472125123\n            ],\n            [\n                -1014.6722684826907,\n                -466.2954803009414,\n                42.78014472125123\n            ],\n            [\n                -1019.1722684826907,\n                -474.08970893500134,\n                42.78014472125123\n            ],\n            [\n                -1023.6722684826907,\n                -481.8839375690613,\n                42.78014472125123\n            ],\n            [\n                -1028.1722684826907,\n                -489.67816620312124,\n                42.78014472125123\n            ],\n            [\n                -1032.6722684826907,\n                -497.4723948371812,\n                42.78014472125123\n            ],\n            [\n                -1037.1722684826907,\n                -505.26662347124113,\n                42.78014472125123\n            ],\n            [\n                -1038.6722684826907,\n                -507.86469968259445,\n                42.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -510.46277589394776,\n                42.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -510.46277589394776,\n                39.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -513.4627758939478,\n                39.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                39.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                36.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                33.78014472125123\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                30.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                27.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                24.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                21.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                18.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                15.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                12.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                9.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                6.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                3.780144721251233,\n                -1.5707963267948966\n            ],\n            [\n                -1040.1722684826907,\n                -516.4627758939478,\n                0.7801447212512329,\n                -1.5707963267948966\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_average/2025-1-10_0-31-52_1017565625\",\n        \"gpt_instruction\": \"Walk directly to a light gray building , characterized by a flat roof with rooftop structures and medium size . Slightly turn left and advance forward to it .\",\n        \"action\": [\n            9,\n            2,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_003155_2\",\n            \"20250110_003156_3\",\n            \"20250110_003157_6\",\n            \"20250110_003157_7\",\n            \"20250110_003158_8\"\n        ],\n        \"pos\": [\n            [\n                322.3968767322324,\n                -584.7422052390032,\n                69.01067085987573\n            ],\n            [\n                320.8968767322324,\n                -587.3402814503565,\n                69.01067085987573\n            ],\n            [\n                320.8968767322324,\n                -593.3402814503565,\n                69.01067085987573\n            ],\n            [\n                320.8968767322324,\n                -596.3402814503565,\n                69.01067085987573\n            ],\n            [\n                320.8968767322324,\n                -599.3402814503565,\n                69.01067085987573\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_short/2025-1-9_17-25-53_846811127\",\n        \"gpt_instruction\": \"Walk directly towards the light gray large building characterized by its tall structure and numerous windows , then turn slightly right and continue straight towards it . Next , turn slightly left and proceed forward to reach another light gray building notable for its arched windows with dark outlines , decorative cornices , rooftop equipment or structures casting shadows , and a size larger and more prominent than surrounding buildings .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            9,\n            8,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_172557_2\",\n            \"20250109_172559_5\",\n            \"20250109_172600_7\",\n            \"20250109_172600_8\",\n            \"20250109_172601_11\",\n            \"20250109_172602_13\",\n            \"20250109_172603_14\",\n            \"20250109_172604_17\",\n            \"20250109_172606_20\",\n            \"20250109_172607_22\",\n            \"20250109_172607_23\"\n        ],\n        \"pos\": [\n            [\n                -972.8602211117143,\n                -311.114336858795,\n                87.81275808263148\n            ],\n            [\n                -980.6544497457744,\n                -315.614336858795,\n                87.81275808263148\n            ],\n            [\n                -985.8506021684811,\n                -318.614336858795,\n                87.81275808263148\n            ],\n            [\n                -988.4486783798345,\n                -320.114336858795,\n                87.81275808263148\n            ],\n            [\n                -994.4486783798345,\n                -320.114336858795,\n                87.81275808263148\n            ],\n            [\n                -1000.4486783798345,\n                -320.114336858795,\n                87.81275808263148\n            ],\n            [\n                -1003.4486783798345,\n                -320.114336858795,\n                87.81275808263148\n            ],\n            [\n                -1008.6448308025413,\n                -323.114336858795,\n                87.81275808263148\n            ],\n            [\n                -1016.4390594366014,\n                -327.614336858795,\n                87.81275808263148\n            ],\n            [\n                -1021.6352118593081,\n                -330.614336858795,\n                87.81275808263148\n            ],\n            [\n                -1024.2332880706615,\n                -332.114336858795,\n                87.81275808263148\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_long/2025-1-9_21-48-4_480298490\",\n        \"gpt_instruction\": \"Move forward to a large beige building characterized by tall structures with multiple rows of arched windows , then slightly turn right slightly and continue to it . Proceed by slightly turning left to reach a white and beige modern building with large windows and rooftop HVAC units , medium to large in size . Slightly turn left again and advance to a gray building distinguished by its tall rectangular structure with grid - like windows and vertical design elements . Finally , slightly turn right and move ahead to a light beige building notable for its distinct small size and symmetrical design compared to surrounding taller buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            2,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_214808_2\",\n            \"20250109_214809_5\",\n            \"20250109_214811_8\",\n            \"20250109_214812_11\",\n            \"20250109_214814_14\",\n            \"20250109_214815_17\",\n            \"20250109_214816_20\",\n            \"20250109_214818_23\",\n            \"20250109_214818_24\",\n            \"20250109_214819_25\",\n            \"20250109_214820_28\",\n            \"20250109_214822_31\",\n            \"20250109_214822_32\",\n            \"20250109_214824_35\",\n            \"20250109_214825_38\",\n            \"20250109_214826_39\",\n            \"20250109_214826_40\",\n            \"20250109_214828_43\",\n            \"20250109_214829_46\",\n            \"20250109_214829_47\",\n            \"20250109_214831_50\",\n            \"20250109_214831_51\"\n        ],\n        \"pos\": [\n            [\n                -773.3684313530193,\n                -464.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -781.1626599870795,\n                -469.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -788.9568886211396,\n                -473.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -796.7511172551997,\n                -478.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -804.5453458892598,\n                -482.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -812.3395745233199,\n                -487.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -820.13380315738,\n                -491.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -827.9280317914402,\n                -496.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -830.5261080027935,\n                -497.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -833.1241842141469,\n                -499.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -839.1241842141469,\n                -499.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -848.1241842141469,\n                -499.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -851.1241842141469,\n                -499.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -856.3203366368537,\n                -502.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -864.1145652709138,\n                -506.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -866.7126414822671,\n                -508.39620007708487,\n                83.59344083160843\n            ],\n            [\n                -869.3107176936205,\n                -509.89620007708487,\n                83.59344083160843\n            ],\n            [\n                -872.3107176936205,\n                -515.0923524997916,\n                83.59344083160843\n            ],\n            [\n                -876.8107176936205,\n                -522.8865811338517,\n                83.59344083160843\n            ],\n            [\n                -878.3107176936205,\n                -525.484657345205,\n                83.59344083160843\n            ],\n            [\n                -883.5068701163273,\n                -528.484657345205,\n                83.59344083160843\n            ],\n            [\n                -886.1049463276806,\n                -529.984657345205,\n                83.59344083160843\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_short_updown/2025-1-14_17-35-24_452456682\",\n        \"gpt_instruction\": \"Climb the medium - sized gray building with a tall rectangular structure and rooftop details , then move forward to the large steel - gray suspension bridge with twin towers . Make a right turn to the large red suspension bridge with cables , continue straight to it, and slightly turn right to reach the large dark gray building with arched windows and ornate trim . After a slight stop , fall to the medium - sized gray building with a symmetric facade of rectangular stone windows , visible two stories , and multiple windows , serving residential or commercial purposes .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            8,\n            3,\n            3,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_173524_0\",\n            \"20250114_173527_1\",\n            \"20250114_173528_2\",\n            \"20250114_173528_3\",\n            \"20250114_173528_4\",\n            \"20250114_173529_5\",\n            \"20250114_173529_6\",\n            \"20250114_173530_7\",\n            \"20250114_173530_8\",\n            \"20250114_173530_9\",\n            \"20250114_173531_10\",\n            \"20250114_173531_11\",\n            \"20250114_173532_12\",\n            \"20250114_173532_13\",\n            \"20250114_173532_14\",\n            \"20250114_173533_15\",\n            \"20250114_173533_16\",\n            \"20250114_173534_17\",\n            \"20250114_173534_18\",\n            \"20250114_173534_19\",\n            \"20250114_173535_20\",\n            \"20250114_173535_21\",\n            \"20250114_173536_22\",\n            \"20250114_173536_23\",\n            \"20250114_173537_25\",\n            \"20250114_173537_26\",\n            \"20250114_173538_27\",\n            \"20250114_173539_30\",\n            \"20250114_173540_33\",\n            \"20250114_173541_34\",\n            \"20250114_173542_37\",\n            \"20250114_173544_40\",\n            \"20250114_173545_43\",\n            \"20250114_173546_46\",\n            \"20250114_173548_49\",\n            \"20250114_173549_52\",\n            \"20250114_173550_55\",\n            \"20250114_173551_56\",\n            \"20250114_173551_57\",\n            \"20250114_173552_58\",\n            \"20250114_173552_59\",\n            \"20250114_173552_60\",\n            \"20250114_173553_61\",\n            \"20250114_173553_62\",\n            \"20250114_173554_63\",\n            \"20250114_173554_64\",\n            \"20250114_173555_65\",\n            \"20250114_173555_66\",\n            \"20250114_173555_67\",\n            \"20250114_173556_68\",\n            \"20250114_173556_69\",\n            \"20250114_173557_70\",\n            \"20250114_173557_71\",\n            \"20250114_173557_72\",\n            \"20250114_173558_73\",\n            \"20250114_173558_74\",\n            \"20250114_173559_75\",\n            \"20250114_173559_76\",\n            \"20250114_173600_77\",\n            \"20250114_173600_78\",\n            \"20250114_173600_79\",\n            \"20250114_173601_80\"\n        ],\n        \"pos\": [\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                2.7697231165269045\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                5.7697231165269045\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                8.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                11.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                14.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                17.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                20.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                23.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                26.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                29.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                32.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                35.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                38.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                41.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                44.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                47.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                50.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                53.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                56.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                59.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                62.769723116526905\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                65.7697231165269\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                68.7697231165269\n            ],\n            [\n                -875.3523641216178,\n                15.117258355155585,\n                71.7697231165269\n            ],\n            [\n                -872.3523641216178,\n                15.117258355155585,\n                74.7697231165269\n            ],\n            [\n                -869.3523641216178,\n                15.117258355155585,\n                74.7697231165269\n            ],\n            [\n                -869.3523641216178,\n                15.117258355155585,\n                74.7697231165269\n            ],\n            [\n                -866.3523641216178,\n                9.921105932448953,\n                74.7697231165269\n            ],\n            [\n                -861.8523641216178,\n                2.1268772983890045,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -0.4711989129643115,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -6.4711989129643115,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -15.471198912964311,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -24.47119891296431,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -33.47119891296431,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -42.47119891296431,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -51.47119891296431,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -60.47119891296431,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                74.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                71.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                68.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                65.7697231165269\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                62.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                59.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                56.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                53.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                50.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                47.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                44.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                41.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                38.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                35.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                32.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                29.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                26.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                23.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                20.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                17.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                14.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                11.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                8.769723116526905\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                5.7697231165269045\n            ],\n            [\n                -860.3523641216178,\n                -63.47119891296431,\n                2.7697231165269045\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_19-18-17_1005418816\",\n        \"gpt_instruction\": \"Veer left towards a gray , tall multi - story structure with numerous windows of size large , then walk straight to a dark gray building featuring rectangular windows in a tiered structure and a very large size . Next , take a right slightly towards it . Finally , move forward to a gray historic - style urban building with a prominent water tank on the roof and a mid-rise size .\",\n        \"action\": [\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_191817_0\",\n            \"20250109_191820_1\",\n            \"20250109_191821_4\",\n            \"20250109_191822_7\",\n            \"20250109_191824_10\",\n            \"20250109_191825_13\",\n            \"20250109_191826_16\",\n            \"20250109_191827_19\",\n            \"20250109_191828_21\",\n            \"20250109_191829_22\",\n            \"20250109_191829_23\",\n            \"20250109_191830_26\",\n            \"20250109_191832_29\",\n            \"20250109_191833_32\",\n            \"20250109_191834_35\",\n            \"20250109_191835_38\",\n            \"20250109_191836_40\",\n            \"20250109_191837_41\"\n        ],\n        \"pos\": [\n            [\n                -813.3054661715482,\n                -402.8664925739257,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -405.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -411.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -420.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -429.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -438.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -447.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -456.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -462.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -465.464568785279,\n                30.45824585413169\n            ],\n            [\n                -814.8054661715482,\n                -465.464568785279,\n                30.45824585413169\n            ],\n            [\n                -820.001618594255,\n                -468.464568785279,\n                30.45824585413169\n            ],\n            [\n                -827.7958472283151,\n                -472.964568785279,\n                30.45824585413169\n            ],\n            [\n                -835.5900758623752,\n                -477.464568785279,\n                30.45824585413169\n            ],\n            [\n                -843.3843044964353,\n                -481.964568785279,\n                30.45824585413169\n            ],\n            [\n                -851.1785331304955,\n                -486.464568785279,\n                30.45824585413169\n            ],\n            [\n                -856.3746855532022,\n                -489.464568785279,\n                30.45824585413169\n            ],\n            [\n                -858.9727617645556,\n                -490.964568785279,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_13-54-35_1023457753\",\n        \"gpt_instruction\": \"Walk straight to a tall , gray rectangular building with grid - like windows ; then take a right to a medium - sized gray building with a rectangular rooftop featuring a billboard . Continue walking straight to a large white and blue advertisement structure featuring a billboard with an illustration and text about satisfaction . After slightly turning left and continuing straight , you \\u2019ll reach a dark gray medium - sized structure with a rooftop billboard displaying a vintage - style advertisement and utility installations such as an antenna .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            3,\n            9,\n            8,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_135439_2\",\n            \"20250109_135440_5\",\n            \"20250109_135441_8\",\n            \"20250109_135442_11\",\n            \"20250109_135444_14\",\n            \"20250109_135445_17\",\n            \"20250109_135446_18\",\n            \"20250109_135446_19\",\n            \"20250109_135448_22\",\n            \"20250109_135448_24\",\n            \"20250109_135449_25\",\n            \"20250109_135449_26\",\n            \"20250109_135450_27\"\n        ],\n        \"pos\": [\n            [\n                -972.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -963.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -954.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -945.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -936.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -927.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -924.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -924.0752894125075,\n                -216.31515708398885,\n                30.45824585413169\n            ],\n            [\n                -921.0752894125075,\n                -221.51130950669548,\n                30.45824585413169\n            ],\n            [\n                -918.0752894125075,\n                -226.70746192940211,\n                30.45824585413169\n            ],\n            [\n                -916.5752894125075,\n                -229.30553814075543,\n                30.45824585413169\n            ],\n            [\n                -916.5752894125075,\n                -229.30553814075543,\n                30.45824585413169\n            ],\n            [\n                -913.9772132011542,\n                -230.80553814075543,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.523598775598299,\n            -0.523598775598299\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_short/2025-1-9_21-9-15_2125023787\",\n        \"gpt_instruction\": \"Walk straight toward a large , light beige rectangular building featuring arched windows and rooftop structures , occupying a central focus . Then , slightly turn right and move forward toward it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_210919_2\",\n            \"20250109_210920_5\",\n            \"20250109_210922_8\",\n            \"20250109_210923_11\",\n            \"20250109_210925_14\",\n            \"20250109_210925_15\",\n            \"20250109_210926_16\",\n            \"20250109_210928_19\",\n            \"20250109_210929_22\",\n            \"20250109_210930_25\",\n            \"20250109_210931_26\"\n        ],\n        \"pos\": [\n            [\n                219.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                210.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                201.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                192.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                183.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                180.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                177.53554239553478,\n                -180.33386380940985,\n                87.81275808263148\n            ],\n            [\n                172.33938997282814,\n                -177.33386380940985,\n                87.81275808263148\n            ],\n            [\n                164.5451613387682,\n                -172.83386380940985,\n                87.81275808263148\n            ],\n            [\n                156.75093270470825,\n                -168.33386380940985,\n                87.81275808263148\n            ],\n            [\n                154.15285649335493,\n                -166.83386380940985,\n                87.81275808263148\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_long_updown/2025-1-15_0-20-26_1584710873\",\n        \"gpt_instruction\": \"Climb to the dark gray large white advertising billboard featuring ' AMERICA ' and a red car image on a skyscraper building , then walk straight to the tall gray skyscraper with many windows . Afterward , slightly turn left and continue straight to the beige cylindrical building with vertical windows , and finally , slightly stop before going downwards to the modern large building with grey and brown colors , featuring arched windows with brown frames .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250115_002026_0\",\n            \"20250115_002029_1\",\n            \"20250115_002029_2\",\n            \"20250115_002030_3\",\n            \"20250115_002030_4\",\n            \"20250115_002031_5\",\n            \"20250115_002031_6\",\n            \"20250115_002031_7\",\n            \"20250115_002032_8\",\n            \"20250115_002032_9\",\n            \"20250115_002033_10\",\n            \"20250115_002033_11\",\n            \"20250115_002034_12\",\n            \"20250115_002034_13\",\n            \"20250115_002035_14\",\n            \"20250115_002035_15\",\n            \"20250115_002035_16\",\n            \"20250115_002036_17\",\n            \"20250115_002037_20\",\n            \"20250115_002038_23\",\n            \"20250115_002040_26\",\n            \"20250115_002041_29\",\n            \"20250115_002042_32\",\n            \"20250115_002043_35\",\n            \"20250115_002045_38\",\n            \"20250115_002046_41\",\n            \"20250115_002046_42\",\n            \"20250115_002047_43\",\n            \"20250115_002048_46\",\n            \"20250115_002049_49\",\n            \"20250115_002051_52\",\n            \"20250115_002052_55\",\n            \"20250115_002053_58\",\n            \"20250115_002055_61\",\n            \"20250115_002056_64\",\n            \"20250115_002057_66\",\n            \"20250115_002057_67\",\n            \"20250115_002058_68\",\n            \"20250115_002058_69\",\n            \"20250115_002058_70\",\n            \"20250115_002059_71\",\n            \"20250115_002059_72\",\n            \"20250115_002100_73\",\n            \"20250115_002100_74\",\n            \"20250115_002100_75\",\n            \"20250115_002101_76\",\n            \"20250115_002101_77\",\n            \"20250115_002102_78\",\n            \"20250115_002102_79\",\n            \"20250115_002102_80\",\n            \"20250115_002103_81\",\n            \"20250115_002103_82\",\n            \"20250115_002103_83\",\n            \"20250115_002104_84\",\n            \"20250115_002104_85\"\n        ],\n        \"pos\": [\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                2.5430082768225617\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                5.543008276822562\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                8.543008276822562\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                11.543008276822562\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                14.543008276822562\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                17.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                20.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                23.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                26.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                29.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                32.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                35.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                38.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                41.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                44.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                47.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                50.54300827682256\n            ],\n            [\n                -925.2275468338613,\n                -189.45510507608685,\n                53.54300827682256\n            ],\n            [\n                -922.2275468338613,\n                -184.25895265338022,\n                56.54300827682256\n            ],\n            [\n                -917.7275468338613,\n                -176.46472401932027,\n                56.54300827682256\n            ],\n            [\n                -913.2275468338613,\n                -168.67049538526032,\n                56.54300827682256\n            ],\n            [\n                -908.7275468338613,\n                -160.87626675120038,\n                56.54300827682256\n            ],\n            [\n                -904.2275468338613,\n                -153.08203811714043,\n                56.54300827682256\n            ],\n            [\n                -899.7275468338613,\n                -145.28780948308048,\n                56.54300827682256\n            ],\n            [\n                -895.2275468338613,\n                -137.49358084902053,\n                56.54300827682256\n            ],\n            [\n                -890.7275468338613,\n                -129.69935221496058,\n                56.54300827682256\n            ],\n            [\n                -889.2275468338613,\n                -127.10127600360727,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -124.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -118.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -109.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -100.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -91.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -82.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -73.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -64.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -58.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                56.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                53.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                50.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                47.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                44.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                41.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                38.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                35.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                32.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                29.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                26.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                23.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                20.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                17.54300827682256\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                14.543008276822562\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                11.543008276822562\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                8.543008276822562\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                5.543008276822562\n            ],\n            [\n                -887.7275468338613,\n                -55.50319979225395,\n                2.5430082768225617\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/low_short_updown/2025-01-19_06-17-38_215421\",\n        \"gpt_instruction\": \"Begin by ascending towards a large gray building characterized by a row of arched windows and rooftop structures . Continue straight ahead to encounter a similarly large gray radio or telecommunication antenna , notable for its tall metallic structure with a cylindrical base and overhead framework . Again , head upwards to another large gray building featuring antenna structures on its rooftop . Slightly turn left and go directly towards it . Slightly turn right and proceed forward to reach a large building with a gray and brown brick exterior and large windows . Slightly descend , then slightly turn right , continuing straight before stopping and descending further towards a medium - sized light gray commercial building , easily identified by its arched windows with black frames .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            4,\n            4,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            5,\n            3,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250119_061740_0\",\n            \"20250119_061741_1\",\n            \"20250119_061741_2\",\n            \"20250119_061742_3\",\n            \"20250119_061743_4\",\n            \"20250119_061744_5\",\n            \"20250119_061745_6\",\n            \"20250119_061746_7\",\n            \"20250119_061746_8\",\n            \"20250119_061747_9\",\n            \"20250119_061748_10\",\n            \"20250119_061750_13\",\n            \"20250119_061753_16\",\n            \"20250119_061756_19\",\n            \"20250119_061758_22\",\n            \"20250119_061759_23\",\n            \"20250119_061800_24\",\n            \"20250119_061801_25\",\n            \"20250119_061803_28\",\n            \"20250119_061806_31\",\n            \"20250119_061808_34\",\n            \"20250119_061811_37\",\n            \"20250119_061814_40\",\n            \"20250119_061814_41\",\n            \"20250119_061817_44\",\n            \"20250119_061818_45\",\n            \"20250119_061819_46\",\n            \"20250119_061820_47\",\n            \"20250119_061820_48\",\n            \"20250119_061821_49\",\n            \"20250119_061822_50\",\n            \"20250119_061823_51\",\n            \"20250119_061824_52\",\n            \"20250119_061825_53\",\n            \"20250119_061825_54\",\n            \"20250119_061826_55\",\n            \"20250119_061827_56\",\n            \"20250119_061828_57\",\n            \"20250119_061829_58\",\n            \"20250119_061830_59\",\n            \"20250119_061831_60\"\n        ],\n        \"pos\": [\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                0.11613363823794032,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                3.1161336382379403,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                6.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                9.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                12.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                15.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                18.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                21.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                24.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                27.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1047.4879714261815,\n                11.32306708096145,\n                30.11613363823794,\n                -2.0943951023931953\n            ],\n            [\n                -1050.4879714261815,\n                6.126914658254819,\n                33.11613363823794\n            ],\n            [\n                -1054.9879714261815,\n                -1.6673139758051292,\n                33.11613363823794\n            ],\n            [\n                -1059.4879714261815,\n                -9.461542609865077,\n                33.11613363823794\n            ],\n            [\n                -1063.9879714261815,\n                -17.255771243925025,\n                33.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -19.85384745527834,\n                33.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -19.85384745527834,\n                36.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -19.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -25.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -34.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -43.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -52.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -61.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1065.4879714261815,\n                -64.85384745527834,\n                39.11613363823794\n            ],\n            [\n                -1068.4879714261815,\n                -70.04999987798497,\n                39.11613363823794\n            ],\n            [\n                -1069.9879714261815,\n                -72.64807608933829,\n                39.11613363823794\n            ],\n            [\n                -1069.9879714261815,\n                -72.64807608933829,\n                36.11613363823794\n            ],\n            [\n                -1069.9879714261815,\n                -72.64807608933829,\n                36.11613363823794\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                36.11613363823794\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                33.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                30.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                27.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                24.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                21.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                18.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                15.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                12.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                9.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                6.11613363823794,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                3.1161336382379403,\n                -2.617993877991494\n            ],\n            [\n                -1072.5860476375349,\n                -74.14807608933829,\n                0.11613363823794032,\n                -2.617993877991494\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494,\n            -2.617993877991494\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/high_short/2025-1-9_16-47-42_2142757034\",\n        \"gpt_instruction\": \"Proceed directly to the light beige , tall , and prominent rectangular structure with a consistent pattern of windows , which is larger than the surrounding buildings and is a high - rise building .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_164746_2\",\n            \"20250109_164747_5\",\n            \"20250109_164748_7\",\n            \"20250109_164749_8\"\n        ],\n        \"pos\": [\n            [\n                84.87855159243526,\n                -115.6761254306856,\n                87.81275808263148\n            ],\n            [\n                77.08432295837531,\n                -111.1761254306856,\n                87.81275808263148\n            ],\n            [\n                71.88817053566868,\n                -108.1761254306856,\n                87.81275808263148\n            ],\n            [\n                69.29009432431536,\n                -106.6761254306856,\n                87.81275808263148\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_short/2025-1-9_15-19-1_981914693\",\n        \"gpt_instruction\": \"Proceed ahead until you reach the tall , gray landmark building with historic architectural design and rooftop spires . Then , slightly veer right and keep moving straight towards the medium - height , dark brown structure characterized by a dark rooftop and window bars , which is comparable in height to the surrounding buildings and serves as either a commercial or residential block .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_151905_2\",\n            \"20250109_151906_5\",\n            \"20250109_151908_8\",\n            \"20250109_151909_11\",\n            \"20250109_151910_13\",\n            \"20250109_151910_14\",\n            \"20250109_151911_17\",\n            \"20250109_151912_19\",\n            \"20250109_151912_20\"\n        ],\n        \"pos\": [\n            [\n                -937.288229493148,\n                92.60993844456753,\n                45.08563280908995\n            ],\n            [\n                -945.0824581272082,\n                88.10993844456753,\n                45.08563280908995\n            ],\n            [\n                -952.8766867612683,\n                83.60993844456753,\n                45.08563280908995\n            ],\n            [\n                -960.6709153953284,\n                79.10993844456753,\n                45.08563280908995\n            ],\n            [\n                -965.8670678180351,\n                76.10993844456753,\n                45.08563280908995\n            ],\n            [\n                -968.4651440293885,\n                74.60993844456753,\n                45.08563280908995\n            ],\n            [\n                -974.4651440293885,\n                74.60993844456753,\n                45.08563280908995\n            ],\n            [\n                -980.4651440293885,\n                74.60993844456753,\n                45.08563280908995\n            ],\n            [\n                -983.4651440293885,\n                74.60993844456753,\n                45.08563280908995\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_16/astar_data/medium_average/2025-1-9_20-2-55_922587542\",\n        \"gpt_instruction\": \"Head straight to a grey skyscraper featuring rectangular windows and modern design with a tall stature . Then , make a right turn towards a medium - sized dark blue building with a dome - shaped top and antennas on its rooftop . Advance forward to it . Slightly turn left and proceed forward to a prominent grey commercial building with a rectangular grid of windows and a billboard on the roof , standing out compared to its surrounding buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            3,\n            9,\n            8,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_200258_2\",\n            \"20250109_200300_5\",\n            \"20250109_200301_8\",\n            \"20250109_200302_11\",\n            \"20250109_200304_14\",\n            \"20250109_200305_17\",\n            \"20250109_200306_20\",\n            \"20250109_200308_23\",\n            \"20250109_200309_25\",\n            \"20250109_200309_26\",\n            \"20250109_200309_27\",\n            \"20250109_200311_30\",\n            \"20250109_200312_32\",\n            \"20250109_200312_33\",\n            \"20250109_200313_36\",\n            \"20250109_200314_37\"\n        ],\n        \"pos\": [\n            [\n                -226.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -217.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -208.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -199.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -190.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -181.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -172.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -163.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -157.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -154.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -154.07833729038012,\n                -538.6434283824042,\n                30.45824585413169\n            ],\n            [\n                -151.07833729038012,\n                -543.8395808051109,\n                30.45824585413169\n            ],\n            [\n                -148.07833729038012,\n                -549.0357332278177,\n                30.45824585413169\n            ],\n            [\n                -146.57833729038012,\n                -551.633809439171,\n                30.45824585413169\n            ],\n            [\n                -141.3821848676735,\n                -554.633809439171,\n                30.45824585413169\n            ],\n            [\n                -138.78410865632017,\n                -556.133809439171,\n                30.45824585413169\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_long/2025-1-9_1-3-50_1021784812\",\n        \"gpt_instruction\": \"Move forward to a large , dark gray building with a grid - like window pattern , then slightly turn right and head straight toward a large , gray building with a flat rooftop , visible weathering , and peeling surfaces . Afterward , slightly turn left and proceed straight to a large , beige rectangular structure featuring distinct edges and texture . Then , slightly turn left again and continue straight to a large , gray skyscraper with a glass facade displaying grid patterns . Finally , slightly turn right and move ahead to a medium - sized gray commercial building with a flat rooftop and visible air conditioning units .\",\n        \"action\": [\n            9,\n            1,\n            3,\n            9,\n            2,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_010354_2\",\n            \"20250109_010354_3\",\n            \"20250109_010354_4\",\n            \"20250109_010355_7\",\n            \"20250109_010356_8\",\n            \"20250109_010357_11\",\n            \"20250109_010358_14\",\n            \"20250109_010359_15\",\n            \"20250109_010359_16\",\n            \"20250109_010400_19\",\n            \"20250109_010402_22\",\n            \"20250109_010403_25\",\n            \"20250109_010404_28\",\n            \"20250109_010405_31\",\n            \"20250109_010407_34\",\n            \"20250109_010407_36\",\n            \"20250109_010408_37\",\n            \"20250109_010409_40\",\n            \"20250109_010410_43\",\n            \"20250109_010411_46\",\n            \"20250109_010412_49\",\n            \"20250109_010413_51\",\n            \"20250109_010413_52\"\n        ],\n        \"pos\": [\n            [\n                914.6831169866346,\n                440.386121403439,\n                4.008504278406168\n            ],\n            [\n                914.6831169866346,\n                437.386121403439,\n                4.008504278406168\n            ],\n            [\n                914.6831169866346,\n                434.386121403439,\n                4.008504278406168\n            ],\n            [\n                911.6831169866346,\n                429.1899689807324,\n                4.008504278406168\n            ],\n            [\n                910.1831169866346,\n                426.59189276937906,\n                4.008504278406168\n            ],\n            [\n                910.1831169866346,\n                420.59189276937906,\n                4.008504278406168\n            ],\n            [\n                910.1831169866346,\n                411.59189276937906,\n                4.008504278406168\n            ],\n            [\n                910.1831169866346,\n                408.59189276937906,\n                4.008504278406168\n            ],\n            [\n                910.1831169866346,\n                405.59189276937906,\n                4.008504278406168\n            ],\n            [\n                913.1831169866346,\n                400.3957403466724,\n                4.008504278406168\n            ],\n            [\n                917.6831169866346,\n                392.6015117126125,\n                4.008504278406168\n            ],\n            [\n                922.1831169866346,\n                384.80728307855253,\n                4.008504278406168\n            ],\n            [\n                926.6831169866346,\n                377.0130544444926,\n                4.008504278406168\n            ],\n            [\n                931.1831169866346,\n                369.21882581043263,\n                4.008504278406168\n            ],\n            [\n                935.6831169866346,\n                361.4245971763727,\n                4.008504278406168\n            ],\n            [\n                938.6831169866346,\n                356.22844475366605,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                353.63036854231274,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                347.63036854231274,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                338.63036854231274,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                329.63036854231274,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                320.63036854231274,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                314.63036854231274,\n                4.008504278406168\n            ],\n            [\n                940.1831169866346,\n                311.63036854231274,\n                4.008504278406168\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_long/2025-1-9_7-12-23_1042517543\",\n        \"gpt_instruction\": \"Proceed forward towards the large grey building with rectangular windows , then shift left to another similar structure . Continue directly to a large beige building , notable for its height and rectangular windows across multiple floors . Turn right to a third similar building , then move forward to a tall , dark structure distinguished by its vertically stacked sections . Slightly turn left and walk straight to a tall and narrow dark grey office building , featuring a grid pattern of rectangular windows .\",\n        \"action\": [\n            9,\n            8,\n            2,\n            2,\n            9,\n            3,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_071227_2\",\n            \"20250109_071228_4\",\n            \"20250109_071228_5\",\n            \"20250109_071228_6\",\n            \"20250109_071230_9\",\n            \"20250109_071230_10\",\n            \"20250109_071230_11\",\n            \"20250109_071232_14\",\n            \"20250109_071233_17\",\n            \"20250109_071234_20\",\n            \"20250109_071235_23\",\n            \"20250109_071236_26\",\n            \"20250109_071238_29\",\n            \"20250109_071239_32\",\n            \"20250109_071240_35\",\n            \"20250109_071241_38\",\n            \"20250109_071242_41\",\n            \"20250109_071244_44\",\n            \"20250109_071245_47\",\n            \"20250109_071245_48\",\n            \"20250109_071247_51\",\n            \"20250109_071248_54\",\n            \"20250109_071248_55\",\n            \"20250109_071249_56\"\n        ],\n        \"pos\": [\n            [\n                533.7020794105731,\n                221.49280039658598,\n                4.008504278406168\n            ],\n            [\n                533.7020794105731,\n                215.49280039658598,\n                4.008504278406168\n            ],\n            [\n                533.7020794105731,\n                212.49280039658598,\n                4.008504278406168\n            ],\n            [\n                533.7020794105731,\n                212.49280039658598,\n                4.008504278406168\n            ],\n            [\n                538.8982318332799,\n                209.49280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                207.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                207.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                201.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                192.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                183.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                174.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                165.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                156.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                147.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                138.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                129.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                120.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                111.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                102.99280039658598,\n                4.008504278406168\n            ],\n            [\n                541.4963080446332,\n                99.99280039658598,\n                4.008504278406168\n            ],\n            [\n                544.4963080446332,\n                94.79664797387935,\n                4.008504278406168\n            ],\n            [\n                548.9963080446332,\n                87.0024193398194,\n                4.008504278406168\n            ],\n            [\n                550.4963080446332,\n                84.40434312846608,\n                4.008504278406168\n            ],\n            [\n                551.9963080446332,\n                81.80626691711277,\n                4.008504278406168\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -1.047197551196588,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_short/2025-1-8_21-13-29_1756915667\",\n        \"gpt_instruction\": \"Proceed straight to the modern office building , characterized by a dark color with cream borders , featuring medium - sized rectangular windows with reflective glass panels .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_211332_2\",\n            \"20250108_211333_5\",\n            \"20250108_211335_8\",\n            \"20250108_211335_9\",\n            \"20250108_211335_10\"\n        ],\n        \"pos\": [\n            [\n                1577.9082731331375,\n                505.14843576736314,\n                -14.030541685236908\n            ],\n            [\n                1570.1140444990774,\n                500.64843576736314,\n                -14.030541685236908\n            ],\n            [\n                1562.3198158650173,\n                496.14843576736314,\n                -14.030541685236908\n            ],\n            [\n                1559.721739653664,\n                494.64843576736314,\n                -14.030541685236908\n            ],\n            [\n                1557.1236634423105,\n                493.14843576736314,\n                -14.030541685236908\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_short/2025-1-8_23-6-46_1693485026\",\n        \"gpt_instruction\": \"Proceed to the gray , rectangular skyscraper characterized by large glass windows and numerous columns at the base . Afterward , go directly to it . Then , slightly turn left and move ahead towards the dark structure with reflective glass panels and large cylindrical pillars at the base , which is a tall and wide modern high - rise building .\",\n        \"action\": [\n            1,\n            2,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_230646_0\",\n            \"20250108_230649_1\",\n            \"20250108_230650_4\",\n            \"20250108_230650_5\",\n            \"20250108_230651_7\",\n            \"20250108_230651_8\"\n        ],\n        \"pos\": [\n            [\n                1377.8893810177724,\n                768.9186377338308,\n                -36.25378652273585\n            ],\n            [\n                1380.8893810177724,\n                768.9186377338308,\n                -36.25378652273585\n            ],\n            [\n                1386.0855334404791,\n                771.9186377338308,\n                -36.25378652273585\n            ],\n            [\n                1388.6836096518325,\n                773.4186377338308,\n                -36.25378652273585\n            ],\n            [\n                1390.1836096518325,\n                776.0167139451842,\n                -36.25378652273585\n            ],\n            [\n                1391.6836096518325,\n                778.6147901565375,\n                -36.25378652273585\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894,\n            1.0471975511966072,\n            1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_short/2025-1-9_1-4-54_1364180570\",\n        \"gpt_instruction\": \"Proceed straight ahead towards a large white building facade characterized by a grid of windows . Then , perform a slight left turn to advance towards a large modern office building distinguished by a beige and dark reflective glass appearance and a grid - like structure of reflective windows , occupying a significant portion of the image .\",\n        \"action\": [\n            9,\n            9,\n            2,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_010458_2\",\n            \"20250109_010459_5\",\n            \"20250109_010459_6\",\n            \"20250109_010500_9\",\n            \"20250109_010501_10\",\n            \"20250109_010501_11\"\n        ],\n        \"pos\": [\n            [\n                1509.2598368510228,\n                676.6374338531272,\n                -14.030541685236908\n            ],\n            [\n                1500.2598368510228,\n                676.6374338531272,\n                -14.030541685236908\n            ],\n            [\n                1497.2598368510228,\n                676.6374338531272,\n                -14.030541685236908\n            ],\n            [\n                1492.063684428316,\n                673.6374338531272,\n                -14.030541685236908\n            ],\n            [\n                1489.4656082169627,\n                672.1374338531272,\n                -14.030541685236908\n            ],\n            [\n                1486.8675320056093,\n                670.6374338531272,\n                -14.030541685236908\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_average/2025-1-8_20-4-26_1795519125\",\n        \"gpt_instruction\": \"Proceed straight to a medium - sized modern urban building in the foreground , characterized by a white color , rectangular windows arranged in a grid pattern , and then slightly turn left . Continue straight to another medium - sized modern commercial building with prominent features , including square - shaped dark windows with a glowing greenish tint .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_200430_2\",\n            \"20250108_200431_5\",\n            \"20250108_200432_7\",\n            \"20250108_200432_8\",\n            \"20250108_200433_11\",\n            \"20250108_200434_12\"\n        ],\n        \"pos\": [\n            [\n                1707.0617775600765,\n                475.5343813046879,\n                -8.37259322125965\n            ],\n            [\n                1716.0617775600765,\n                475.5343813046879,\n                -8.37259322125965\n            ],\n            [\n                1722.0617775600765,\n                475.5343813046879,\n                -8.37259322125965\n            ],\n            [\n                1725.0617775600765,\n                475.5343813046879,\n                -8.37259322125965\n            ],\n            [\n                1730.2579299827833,\n                478.5343813046879,\n                -8.37259322125965\n            ],\n            [\n                1732.8560061941366,\n                480.0343813046879,\n                -8.37259322125965\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_average/2025-1-9_16-41-28_1231192379\",\n        \"gpt_instruction\": \"Combine the scattered actions and landmarks into a smooth and fluent sentence as follows : \\\" Keep going straight toward a medium - sized green and gray square with an open grassy area , scattered trees , and paved walkways . Slightly turn left and walk straight toward a large multi - story office building , white with black window panels , a flat rooftop with vents , and a small rooftop structure . \\\"\",\n        \"action\": [\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_164131_2\",\n            \"20250109_164132_3\",\n            \"20250109_164133_6\",\n            \"20250109_164134_9\",\n            \"20250109_164136_12\",\n            \"20250109_164137_15\",\n            \"20250109_164138_18\",\n            \"20250109_164139_21\",\n            \"20250109_164141_24\",\n            \"20250109_164142_27\",\n            \"20250109_164143_30\",\n            \"20250109_164144_33\",\n            \"20250109_164145_36\",\n            \"20250109_164146_38\",\n            \"20250109_164147_39\"\n        ],\n        \"pos\": [\n            [\n                1436.913511801473,\n                -99.46566955266945,\n                17.46431138117525\n            ],\n            [\n                1438.413511801473,\n                -102.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1443.6096642241798,\n                -105.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1451.40389285824,\n                -109.56374576402277,\n                17.46431138117525\n            ],\n            [\n                1459.1981214923,\n                -114.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1466.9923501263602,\n                -118.56374576402277,\n                17.46431138117525\n            ],\n            [\n                1474.7865787604203,\n                -123.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1482.5808073944804,\n                -127.56374576402277,\n                17.46431138117525\n            ],\n            [\n                1490.3750360285405,\n                -132.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1498.1692646626007,\n                -136.56374576402277,\n                17.46431138117525\n            ],\n            [\n                1505.9634932966608,\n                -141.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1513.757721930721,\n                -145.56374576402277,\n                17.46431138117525\n            ],\n            [\n                1521.551950564781,\n                -150.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1526.7481029874878,\n                -153.06374576402277,\n                17.46431138117525\n            ],\n            [\n                1529.3461791988411,\n                -154.56374576402277,\n                17.46431138117525\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_long/2025-1-10_5-50-21_1565306616\",\n        \"gpt_instruction\": \"Proceed directly ahead to a medium - sized , dark brown rectangular building with a flat rooftop and minimal windows . Slightly turn left and move forward toward a tall , multi - story building characterized by a black - and - white glass - panel facade featuring a grid pattern . Subsequently , slightly turn right , then continue ahead toward it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_055024_2\",\n            \"20250110_055026_5\",\n            \"20250110_055027_8\",\n            \"20250110_055028_11\",\n            \"20250110_055029_14\",\n            \"20250110_055031_17\",\n            \"20250110_055032_20\",\n            \"20250110_055033_23\",\n            \"20250110_055034_26\",\n            \"20250110_055035_27\",\n            \"20250110_055036_30\",\n            \"20250110_055037_33\",\n            \"20250110_055038_36\",\n            \"20250110_055040_39\",\n            \"20250110_055040_40\",\n            \"20250110_055041_41\",\n            \"20250110_055041_43\",\n            \"20250110_055042_44\"\n        ],\n        \"pos\": [\n            [\n                1881.6205159693386,\n                773.4983538938495,\n                30.21310795576765\n            ],\n            [\n                1886.1205159693386,\n                765.7041252597894,\n                30.21310795576765\n            ],\n            [\n                1890.6205159693386,\n                757.9098966257293,\n                30.21310795576765\n            ],\n            [\n                1895.1205159693386,\n                750.1156679916692,\n                30.21310795576765\n            ],\n            [\n                1899.6205159693386,\n                742.321439357609,\n                30.21310795576765\n            ],\n            [\n                1904.1205159693386,\n                734.5272107235489,\n                30.21310795576765\n            ],\n            [\n                1908.6205159693386,\n                726.7329820894888,\n                30.21310795576765\n            ],\n            [\n                1913.1205159693386,\n                718.9387534554287,\n                30.21310795576765\n            ],\n            [\n                1917.6205159693386,\n                711.1445248213686,\n                30.21310795576765\n            ],\n            [\n                1919.1205159693386,\n                708.5464486100152,\n                30.21310795576765\n            ],\n            [\n                1924.3166683920454,\n                705.5464486100152,\n                30.21310795576765\n            ],\n            [\n                1932.1108970261055,\n                701.0464486100152,\n                30.21310795576765\n            ],\n            [\n                1939.9051256601656,\n                696.5464486100152,\n                30.21310795576765\n            ],\n            [\n                1947.6993542942257,\n                692.0464486100152,\n                30.21310795576765\n            ],\n            [\n                1950.297430505579,\n                690.5464486100152,\n                30.21310795576765\n            ],\n            [\n                1952.8955067169325,\n                689.0464486100152,\n                30.21310795576765\n            ],\n            [\n                1954.3955067169325,\n                686.4483723986618,\n                30.21310795576765\n            ],\n            [\n                1955.8955067169325,\n                683.8502961873085,\n                30.21310795576765\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -1.0471975511966072,\n            -1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_average/2025-1-9_1-29-2_1316953165\",\n        \"gpt_instruction\": \"Proceed directly to a large gray building characterized by a brick texture with large rectangular windows . Then , slightly turn right and move forward to another large gray building with rectangular windows and arched entryways . Finally , slightly turn left and go straight to the tallest building in the image , a modern office building with a glass fa\\u00e7ade on the upper section and a solid structure below , showcasing a gray and brown color scheme .\",\n        \"action\": [\n            9,\n            9,\n            1,\n            3,\n            9,\n            1,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_012905_2\",\n            \"20250109_012906_5\",\n            \"20250109_012907_6\",\n            \"20250109_012907_7\",\n            \"20250109_012909_10\",\n            \"20250109_012909_11\",\n            \"20250109_012909_12\",\n            \"20250109_012910_13\",\n            \"20250109_012910_14\"\n        ],\n        \"pos\": [\n            [\n                1332.361655633764,\n                922.5705773603819,\n                -8.37259322125965\n            ],\n            [\n                1324.567426999704,\n                918.0705773603819,\n                -8.37259322125965\n            ],\n            [\n                1321.9693507883505,\n                916.5705773603819,\n                -8.37259322125965\n            ],\n            [\n                1319.3712745769972,\n                915.0705773603819,\n                -8.37259322125965\n            ],\n            [\n                1313.3712745769972,\n                915.0705773603819,\n                -8.37259322125965\n            ],\n            [\n                1310.3712745769972,\n                915.0705773603819,\n                -8.37259322125965\n            ],\n            [\n                1307.3712745769972,\n                915.0705773603819,\n                -8.37259322125965\n            ],\n            [\n                1307.3712745769972,\n                915.0705773603819,\n                -8.37259322125965\n            ],\n            [\n                1304.7731983656438,\n                913.5705773603819,\n                -8.37259322125965\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            3.665191429188092\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_long/2025-1-8_23-12-4_1908518808\",\n        \"gpt_instruction\": \"Head straight toward a large , dark - gray urban landmark characterized by a brick building with large arched windows . Then , slightly turn right and move ahead to a large , gray building featuring a flat roof with windows . Finally , slightly turn left and walk straight to a medium - sized gray office building distinguished by a tall structure with large windows spanning the facade , comparatively smaller than other buildings in the scene .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_231208_2\",\n            \"20250108_231209_5\",\n            \"20250108_231210_8\",\n            \"20250108_231211_9\",\n            \"20250108_231212_12\",\n            \"20250108_231213_15\",\n            \"20250108_231214_18\",\n            \"20250108_231216_21\",\n            \"20250108_231217_24\",\n            \"20250108_231218_27\",\n            \"20250108_231219_30\",\n            \"20250108_231221_33\",\n            \"20250108_231222_36\",\n            \"20250108_231223_39\",\n            \"20250108_231224_42\",\n            \"20250108_231225_43\",\n            \"20250108_231226_46\",\n            \"20250108_231227_49\",\n            \"20250108_231228_52\",\n            \"20250108_231229_53\"\n        ],\n        \"pos\": [\n            [\n                765.4542136539551,\n                -9.241324403218329,\n                4.008504278406168\n            ],\n            [\n                769.9542136539551,\n                -17.035553037278277,\n                4.008504278406168\n            ],\n            [\n                774.4542136539551,\n                -24.829781671338225,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -27.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -33.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -42.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -51.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -60.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -69.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -78.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -87.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -96.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -105.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -114.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -123.42785788269154,\n                4.008504278406168\n            ],\n            [\n                775.9542136539551,\n                -126.42785788269154,\n                4.008504278406168\n            ],\n            [\n                778.9542136539551,\n                -131.62401030539817,\n                4.008504278406168\n            ],\n            [\n                783.4542136539551,\n                -139.41823893945812,\n                4.008504278406168\n            ],\n            [\n                787.9542136539551,\n                -147.21246757351807,\n                4.008504278406168\n            ],\n            [\n                789.4542136539551,\n                -149.81054378487138,\n                4.008504278406168\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_average/2025-1-8_19-29-32_1057467587\",\n        \"gpt_instruction\": \"Proceed straight towards a white building characterized by a modern cubic design with large windows of medium size . Next , veer right to a large gray building featuring a brick facade with arched windows , and continue moving forward to a similar structure which is a historic high - rise . Then , slightly turn left and head straight towards another large gray building with arched windows , identified as a modern office building .\",\n        \"action\": [\n            9,\n            9,\n            1,\n            3,\n            3,\n            9,\n            2,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_192936_2\",\n            \"20250108_192937_5\",\n            \"20250108_192937_6\",\n            \"20250108_192938_7\",\n            \"20250108_192938_8\",\n            \"20250108_192940_11\",\n            \"20250108_192940_12\",\n            \"20250108_192941_15\",\n            \"20250108_192943_18\",\n            \"20250108_192944_21\",\n            \"20250108_192944_22\"\n        ],\n        \"pos\": [\n            [\n                730.8535607079041,\n                -5.004791772810357,\n                -8.37259322125965\n            ],\n            [\n                739.8535607079041,\n                -5.004791772810357,\n                -8.37259322125965\n            ],\n            [\n                742.8535607079041,\n                -5.004791772810357,\n                -8.37259322125965\n            ],\n            [\n                745.8535607079041,\n                -5.004791772810357,\n                -8.37259322125965\n            ],\n            [\n                745.8535607079041,\n                -5.004791772810357,\n                -8.37259322125965\n            ],\n            [\n                748.8535607079041,\n                -10.200944195516989,\n                -8.37259322125965\n            ],\n            [\n                750.3535607079041,\n                -12.799020406870305,\n                -8.37259322125965\n            ],\n            [\n                755.5497131306108,\n                -15.799020406870305,\n                -8.37259322125965\n            ],\n            [\n                763.343941764671,\n                -20.299020406870305,\n                -8.37259322125965\n            ],\n            [\n                771.1381703987311,\n                -24.799020406870305,\n                -8.37259322125965\n            ],\n            [\n                773.7362466100844,\n                -26.299020406870305,\n                -8.37259322125965\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_long/2025-1-8_20-32-23_1111783898\",\n        \"gpt_instruction\": \"\\\" Proceed straight toward a gray skyscraper with a glass facade and a reflective surface , then slightly turn left and advance toward another large gray building featuring rectangular windows and a blocky structure . Continue left , heading directly to a gray cylindrical skyscraper characterized by grid - like reflective windows . Finally , slightly turn right and proceed straight to a medium - sized gray modern architectural building with a triangular apex , contrasting in size with surrounding skyscrapers . \\\"\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_203227_2\",\n            \"20250108_203228_5\",\n            \"20250108_203229_8\",\n            \"20250108_203229_9\",\n            \"20250108_203231_12\",\n            \"20250108_203232_15\",\n            \"20250108_203233_18\",\n            \"20250108_203235_21\",\n            \"20250108_203236_24\",\n            \"20250108_203237_27\",\n            \"20250108_203238_30\",\n            \"20250108_203240_33\",\n            \"20250108_203241_36\",\n            \"20250108_203242_38\",\n            \"20250108_203242_39\",\n            \"20250108_203244_42\",\n            \"20250108_203245_45\",\n            \"20250108_203246_48\",\n            \"20250108_203248_51\",\n            \"20250108_203248_52\",\n            \"20250108_203248_53\",\n            \"20250108_203249_54\",\n            \"20250108_203249_55\"\n        ],\n        \"pos\": [\n            [\n                1414.3874807237676,\n                228.31427287281895,\n                3.54759650518017\n            ],\n            [\n                1418.8874807237676,\n                236.1085015068789,\n                3.54759650518017\n            ],\n            [\n                1423.3874807237676,\n                243.90273014093884,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                246.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                252.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                261.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                270.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                279.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                288.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                297.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                306.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                315.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                324.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                330.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1424.8874807237676,\n                333.50080635229216,\n                3.54759650518017\n            ],\n            [\n                1421.8874807237676,\n                338.6969587749988,\n                3.54759650518017\n            ],\n            [\n                1417.3874807237676,\n                346.49118740905874,\n                3.54759650518017\n            ],\n            [\n                1412.8874807237676,\n                354.2854160431187,\n                3.54759650518017\n            ],\n            [\n                1408.3874807237676,\n                362.07964467717863,\n                3.54759650518017\n            ],\n            [\n                1406.8874807237676,\n                364.67772088853195,\n                3.54759650518017\n            ],\n            [\n                1405.3874807237676,\n                367.27579709988527,\n                3.54759650518017\n            ],\n            [\n                1405.3874807237676,\n                367.27579709988527,\n                3.54759650518017\n            ],\n            [\n                1405.3874807237676,\n                370.27579709988527,\n                3.54759650518017\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_average/2025-1-8_17-58-13_2001100545\",\n        \"gpt_instruction\": \"Proceed towards the tall , modern skyscraper with a reflective glass facade , which is gray in color , and features a large size . It has a grid - like pattern of windows that reflect the surrounding buildings and sky . Then , slightly turn left and continue forward to another modern skyscraper . This high - rise building appears glass - like with a reflective surface , blending black and beige hues due to reflections .\",\n        \"action\": [\n            9,\n            1,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_175816_2\",\n            \"20250108_175817_3\",\n            \"20250108_175817_4\",\n            \"20250108_175818_6\",\n            \"20250108_175818_7\"\n        ],\n        \"pos\": [\n            [\n                1686.949916158099,\n                607.9223114937292,\n                -26.888958510984654\n            ],\n            [\n                1685.449916158099,\n                610.5203877050826,\n                -26.888958510984654\n            ],\n            [\n                1683.949916158099,\n                613.118463916436,\n                -26.888958510984654\n            ],\n            [\n                1681.3518399467457,\n                614.618463916436,\n                -26.888958510984654\n            ],\n            [\n                1678.7537637353923,\n                616.118463916436,\n                -26.888958510984654\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779915037,\n            2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_average/2025-1-9_21-26-41_562395735\",\n        \"gpt_instruction\": \"Proceed directly ahead to the gray building characterized by large grid - patterned windows , slightly turn left and advance forward to the skyscraper , which is dark with white grid patterns and vertical white stripes , featuring tall window designs .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_212644_2\",\n            \"20250109_212645_5\",\n            \"20250109_212647_8\",\n            \"20250109_212648_11\",\n            \"20250109_212649_14\",\n            \"20250109_212650_17\",\n            \"20250109_212652_20\",\n            \"20250109_212653_23\",\n            \"20250109_212654_26\",\n            \"20250109_212655_27\",\n            \"20250109_212656_30\",\n            \"20250109_212657_33\",\n            \"20250109_212658_36\",\n            \"20250109_212659_39\",\n            \"20250109_212659_40\"\n        ],\n        \"pos\": [\n            [\n                1115.8633435672327,\n                763.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                772.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                781.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                790.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                799.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                808.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                817.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                826.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                835.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1115.8633435672327,\n                838.0718613215162,\n                17.46431138117525\n            ],\n            [\n                1112.8633435672327,\n                843.2680137442228,\n                17.46431138117525\n            ],\n            [\n                1108.3633435672327,\n                851.0622423782829,\n                17.46431138117525\n            ],\n            [\n                1103.8633435672327,\n                858.856471012343,\n                17.46431138117525\n            ],\n            [\n                1099.3633435672327,\n                866.6506996464032,\n                17.46431138117525\n            ],\n            [\n                1097.8633435672327,\n                869.2487758577565,\n                17.46431138117525\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_average/2025-1-9_2-31-44_1767110751\",\n        \"gpt_instruction\": \"Begin by walking straight towards a light beige building characterized by a pattern of rectangular windows , which is large in size . Then , slightly turn left and head straight to a modern office building that is white , featuring multiple square windows with dark panes , and is medium - to - large high - rise in size .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_023148_2\",\n            \"20250109_023149_5\",\n            \"20250109_023150_8\",\n            \"20250109_023151_9\",\n            \"20250109_023151_10\",\n            \"20250109_023152_13\",\n            \"20250109_023153_15\",\n            \"20250109_023154_16\"\n        ],\n        \"pos\": [\n            [\n                1877.5180198736027,\n                511.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1868.5180198736027,\n                511.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1859.5180198736027,\n                511.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1856.5180198736027,\n                511.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1853.5180198736027,\n                511.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1848.321867450896,\n                508.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1843.1257150281892,\n                505.574933690888,\n                -8.37259322125965\n            ],\n            [\n                1840.5276388168359,\n                504.074933690888,\n                -8.37259322125965\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_short/2025-1-8_17-53-12_1600028624\",\n        \"gpt_instruction\": \"Proceed straight to a beige modern low - rise building with medium size relative to taller surrounding structures , featuring rectangular windows with dark glass . Then , make a left turn to a dark gray modern building characterized by a large size and grid - like window patterns . Head straight to a medium - sized brown building with rectangular windows and brick textures . Afterward , go left slightly to it . Continue by walking straight to it . Slightly turn right and keep going straight toward it .\",\n        \"action\": [\n            8,\n            2,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            2,\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_175315_1\",\n            \"20250108_175316_2\",\n            \"20250108_175316_3\",\n            \"20250108_175317_6\",\n            \"20250108_175319_9\",\n            \"20250108_175320_12\",\n            \"20250108_175321_15\",\n            \"20250108_175322_16\",\n            \"20250108_175322_17\",\n            \"20250108_175322_18\",\n            \"20250108_175324_21\",\n            \"20250108_175324_22\",\n            \"20250108_175324_23\",\n            \"20250108_175325_25\",\n            \"20250108_175326_26\"\n        ],\n        \"pos\": [\n            [\n                79.41375285474622,\n                -168.01660307646486,\n                -36.25378652273585\n            ],\n            [\n                77.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                77.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                71.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                62.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                53.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                44.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                41.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                38.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                38.91375285474622,\n                -165.41852686511155,\n                -36.25378652273585\n            ],\n            [\n                35.91375285474622,\n                -170.61467928781818,\n                -36.25378652273585\n            ],\n            [\n                34.41375285474622,\n                -173.2127554991715,\n                -36.25378652273585\n            ],\n            [\n                32.91375285474622,\n                -175.8108317105248,\n                -36.25378652273585\n            ],\n            [\n                30.315676643392905,\n                -177.3108317105248,\n                -36.25378652273585\n            ],\n            [\n                27.71760043203959,\n                -178.8108317105248,\n                -36.25378652273585\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.617993877991494,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_long_updown/2025-1-14_15-10-7_1856669179\",\n        \"gpt_instruction\": \"Begin by moving upward toward a medium - sized , dark gray building characterized by gothic architecture , featuring pointed roofs and numerous windows , standing in contrast to surrounding skyscrapers . Then , proceed straight to a large , dark gray structure with a rectangular shape , flat roof , and brick texture . Slightly turn left and continue straight to a large gray building with a grid - like reflective glass facade . Subsequently , slightly turn right and walk straight toward it . Finally , slightly pause and drop off at a large , dark structure with reflections on a glass facade that mirrors light on a water surface \\u2014 a modern building with striking features .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_151007_0\",\n            \"20250114_151010_1\",\n            \"20250114_151010_2\",\n            \"20250114_151011_3\",\n            \"20250114_151011_4\",\n            \"20250114_151011_5\",\n            \"20250114_151012_6\",\n            \"20250114_151012_7\",\n            \"20250114_151013_8\",\n            \"20250114_151013_9\",\n            \"20250114_151013_10\",\n            \"20250114_151014_11\",\n            \"20250114_151014_12\",\n            \"20250114_151015_13\",\n            \"20250114_151015_14\",\n            \"20250114_151016_15\",\n            \"20250114_151017_18\",\n            \"20250114_151018_21\",\n            \"20250114_151019_24\",\n            \"20250114_151021_27\",\n            \"20250114_151022_30\",\n            \"20250114_151023_33\",\n            \"20250114_151024_36\",\n            \"20250114_151025_37\",\n            \"20250114_151026_40\",\n            \"20250114_151027_43\",\n            \"20250114_151027_44\",\n            \"20250114_151028_45\",\n            \"20250114_151029_48\",\n            \"20250114_151030_51\",\n            \"20250114_151032_54\",\n            \"20250114_151033_57\",\n            \"20250114_151034_60\",\n            \"20250114_151035_63\",\n            \"20250114_151036_64\",\n            \"20250114_151036_65\",\n            \"20250114_151037_66\",\n            \"20250114_151037_67\",\n            \"20250114_151037_68\",\n            \"20250114_151038_69\",\n            \"20250114_151038_70\",\n            \"20250114_151038_71\",\n            \"20250114_151039_72\",\n            \"20250114_151039_73\",\n            \"20250114_151039_74\",\n            \"20250114_151040_75\",\n            \"20250114_151040_76\",\n            \"20250114_151040_77\",\n            \"20250114_151041_78\",\n            \"20250114_151041_79\",\n            \"20250114_151041_80\"\n        ],\n        \"pos\": [\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -47.68081891736867\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -44.68081891736867\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -41.68081891736867\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -38.68081891736867\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -35.68081891736867\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -32.68081891736867\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -29.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -26.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -23.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -20.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -17.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -14.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -11.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -8.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -5.680818917368672\n            ],\n            [\n                645.4983286415123,\n                339.4444943751703,\n                -2.680818917368672\n            ],\n            [\n                648.4983286415123,\n                344.6406467978769,\n                0.31918108263132794\n            ],\n            [\n                652.9983286415123,\n                352.43487543193686,\n                0.31918108263132794\n            ],\n            [\n                657.4983286415123,\n                360.2291040659968,\n                0.31918108263132794\n            ],\n            [\n                661.9983286415123,\n                368.02333270005676,\n                0.31918108263132794\n            ],\n            [\n                666.4983286415123,\n                375.8175613341167,\n                0.31918108263132794\n            ],\n            [\n                670.9983286415123,\n                383.61178996817665,\n                0.31918108263132794\n            ],\n            [\n                675.4983286415123,\n                391.4060186022366,\n                0.31918108263132794\n            ],\n            [\n                676.9983286415123,\n                394.0040948135899,\n                0.31918108263132794\n            ],\n            [\n                676.9983286415123,\n                400.0040948135899,\n                0.31918108263132794\n            ],\n            [\n                676.9983286415123,\n                409.0040948135899,\n                0.31918108263132794\n            ],\n            [\n                676.9983286415123,\n                412.0040948135899,\n                0.31918108263132794\n            ],\n            [\n                676.9983286415123,\n                415.0040948135899,\n                0.31918108263132794\n            ],\n            [\n                679.9983286415123,\n                420.20024723629655,\n                0.31918108263132794\n            ],\n            [\n                684.4983286415123,\n                427.9944758703565,\n                0.31918108263132794\n            ],\n            [\n                688.9983286415123,\n                435.78870450441644,\n                0.31918108263132794\n            ],\n            [\n                693.4983286415123,\n                443.5829331384764,\n                0.31918108263132794\n            ],\n            [\n                697.9983286415123,\n                451.37716177253634,\n                0.31918108263132794\n            ],\n            [\n                702.4983286415123,\n                459.1713904065963,\n                0.31918108263132794\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                0.31918108263132794\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -2.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -5.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -8.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -11.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -14.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -17.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -20.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -23.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -26.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -29.680818917368672\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -32.68081891736867\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -35.68081891736867\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -38.68081891736867\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -41.68081891736867\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -44.68081891736867\n            ],\n            [\n                703.9983286415123,\n                461.7694666179496,\n                -47.68081891736867\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_short/2025-1-8_23-21-22_940472515\",\n        \"gpt_instruction\": \"Advance forward to a white building with rectangular windows of medium size . Then slightly turn right and move forward to a gray building , which also features rectangular windows and is medium - sized , but is of a modern type .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_232125_2\",\n            \"20250108_232126_5\",\n            \"20250108_232128_8\",\n            \"20250108_232128_9\",\n            \"20250108_232128_10\",\n            \"20250108_232129_12\",\n            \"20250108_232130_13\"\n        ],\n        \"pos\": [\n            [\n                1697.6509100196668,\n                795.0910626630955,\n                -36.25378652273585\n            ],\n            [\n                1693.1509100196668,\n                787.2968340290354,\n                -36.25378652273585\n            ],\n            [\n                1688.6509100196668,\n                779.5026053949753,\n                -36.25378652273585\n            ],\n            [\n                1687.1509100196668,\n                776.9045291836219,\n                -36.25378652273585\n            ],\n            [\n                1685.6509100196668,\n                774.3064529722685,\n                -36.25378652273585\n            ],\n            [\n                1683.0528338083134,\n                772.8064529722685,\n                -36.25378652273585\n            ],\n            [\n                1680.45475759696,\n                771.3064529722685,\n                -36.25378652273585\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_average/2025-1-9_4-12-27_1602182790\",\n        \"gpt_instruction\": \"Proceed straight towards a medium - sized modern rooftop building with a white exterior . Then , slightly turn left and continue straight towards a medium - sized building that blends commercial and residential purposes , featuring light beige walls , grid - patterned windows , and a height similar to the surrounding structures .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_041230_2\",\n            \"20250109_041231_5\",\n            \"20250109_041232_8\",\n            \"20250109_041234_11\",\n            \"20250109_041235_14\",\n            \"20250109_041236_17\",\n            \"20250109_041237_20\",\n            \"20250109_041238_23\",\n            \"20250109_041240_26\",\n            \"20250109_041241_29\",\n            \"20250109_041242_30\",\n            \"20250109_041243_33\",\n            \"20250109_041243_34\"\n        ],\n        \"pos\": [\n            [\n                638.5300429522031,\n                586.6376766612564,\n                -26.888958510984654\n            ],\n            [\n                643.0300429522031,\n                594.4319052953165,\n                -26.888958510984654\n            ],\n            [\n                647.5300429522031,\n                602.2261339293766,\n                -26.888958510984654\n            ],\n            [\n                652.0300429522031,\n                610.0203625634367,\n                -26.888958510984654\n            ],\n            [\n                656.5300429522031,\n                617.8145911974968,\n                -26.888958510984654\n            ],\n            [\n                661.0300429522031,\n                625.608819831557,\n                -26.888958510984654\n            ],\n            [\n                665.5300429522031,\n                633.4030484656171,\n                -26.888958510984654\n            ],\n            [\n                670.0300429522031,\n                641.1972770996772,\n                -26.888958510984654\n            ],\n            [\n                674.5300429522031,\n                648.9915057337373,\n                -26.888958510984654\n            ],\n            [\n                679.0300429522031,\n                656.7857343677974,\n                -26.888958510984654\n            ],\n            [\n                680.5300429522031,\n                659.3838105791508,\n                -26.888958510984654\n            ],\n            [\n                680.5300429522031,\n                665.3838105791508,\n                -26.888958510984654\n            ],\n            [\n                680.5300429522031,\n                668.3838105791508,\n                -26.888958510984654\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_short/2025-1-8_21-15-59_711645630\",\n        \"gpt_instruction\": \"Proceed directly ahead to the brown fa\\u00e7ade with dark and light window areas , characterized by a large rectangular grid of windows with a uniform pattern and an illuminated bottom right section , which belongs to a building fa\\u00e7ade . Slightly turn left and proceed straight towards a dark grey fa\\u00e7ade , featuring a brick design with uniformly arranged square windows . This structure is large , extending across multiple floors , and serves as a residential or office building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_211602_2\",\n            \"20250108_211603_5\",\n            \"20250108_211605_8\",\n            \"20250108_211606_11\",\n            \"20250108_211607_14\",\n            \"20250108_211608_17\",\n            \"20250108_211610_20\",\n            \"20250108_211611_23\",\n            \"20250108_211611_24\",\n            \"20250108_211612_25\",\n            \"20250108_211612_26\",\n            \"20250108_211613_27\"\n        ],\n        \"pos\": [\n            [\n                470.61480830993213,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                479.61480830993213,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                488.61480830993213,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                497.61480830993213,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                506.61480830993213,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                515.6148083099322,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                524.6148083099322,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                533.6148083099322,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                536.6148083099322,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                539.6148083099322,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                539.6148083099322,\n                -222.65371070467236,\n                -14.030541685236908\n            ],\n            [\n                542.2128845212856,\n                -221.15371070467236,\n                -14.030541685236908\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_average/2025-1-9_1-46-37_1175989877\",\n        \"gpt_instruction\": \"Proceed straight towards a modern skyscraper characterized by a gray glass - paneled facade with reflective windows and horizontal dividers , then slightly turn left to advance straight to a dark building featuring large glass windows in a grid - like pattern . Continue directly ahead to it . Shift right towards a light beige modern office building with dark windows and a rectangular , mesh - like grid appearance , then advance forward to a grey building with grid - like windows and pillars . Finally , slightly turn right and head straight to a white commercial office building displaying a grid - like window pattern .\",\n        \"action\": [\n            9,\n            2,\n            9,\n            1,\n            2,\n            9,\n            9,\n            1,\n            3,\n            3,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_014640_2\",\n            \"20250109_014641_3\",\n            \"20250109_014642_6\",\n            \"20250109_014642_7\",\n            \"20250109_014643_8\",\n            \"20250109_014644_11\",\n            \"20250109_014645_14\",\n            \"20250109_014646_15\",\n            \"20250109_014646_16\",\n            \"20250109_014647_17\",\n            \"20250109_014648_20\",\n            \"20250109_014649_23\",\n            \"20250109_014650_25\",\n            \"20250109_014650_26\",\n            \"20250109_014652_29\",\n            \"20250109_014653_32\",\n            \"20250109_014654_35\",\n            \"20250109_014655_38\",\n            \"20250109_014657_41\",\n            \"20250109_014658_44\",\n            \"20250109_014659_47\",\n            \"20250109_014700_49\",\n            \"20250109_014700_50\"\n        ],\n        \"pos\": [\n            [\n                1831.384937561523,\n                -10.958531335074042,\n                -8.37259322125965\n            ],\n            [\n                1829.884937561523,\n                -8.360455123720726,\n                -8.37259322125965\n            ],\n            [\n                1824.6887851388162,\n                -5.360455123720726,\n                -8.37259322125965\n            ],\n            [\n                1822.0907089274629,\n                -3.860455123720726,\n                -8.37259322125965\n            ],\n            [\n                1819.4926327161095,\n                -2.360455123720726,\n                -8.37259322125965\n            ],\n            [\n                1813.4926327161095,\n                -2.360455123720725,\n                -8.37259322125965\n            ],\n            [\n                1804.4926327161095,\n                -2.360455123720724,\n                -8.37259322125965\n            ],\n            [\n                1801.4926327161095,\n                -2.3604551237207234,\n                -8.37259322125965\n            ],\n            [\n                1798.4926327161095,\n                -2.360455123720723,\n                -8.37259322125965\n            ],\n            [\n                1798.4926327161095,\n                -2.360455123720723,\n                -8.37259322125965\n            ],\n            [\n                1795.4926327161095,\n                2.835697298985909,\n                -8.37259322125965\n            ],\n            [\n                1790.9926327161095,\n                10.629925933045858,\n                -8.37259322125965\n            ],\n            [\n                1787.9926327161095,\n                15.82607835575249,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                18.424154567105806,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                24.424154567105806,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                33.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                42.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                51.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                60.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                69.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                78.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                84.4241545671058,\n                -8.37259322125965\n            ],\n            [\n                1786.4926327161095,\n                87.4241545671058,\n                -8.37259322125965\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_long/2025-1-8_18-46-34_2007905771\",\n        \"gpt_instruction\": \"Proceed straight to the large , modern skyscraper characterized by its gray color and multi -story structure with large rectangular windows . Then , slightly turn right , continue straight , and slightly turn left to head directly towards the medium - sized commercial building with an old and worn gray fa\\u00e7ade .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            1,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_184637_2\",\n            \"20250108_184638_5\",\n            \"20250108_184640_8\",\n            \"20250108_184641_11\",\n            \"20250108_184642_14\",\n            \"20250108_184643_17\",\n            \"20250108_184644_20\",\n            \"20250108_184646_23\",\n            \"20250108_184647_26\",\n            \"20250108_184648_29\",\n            \"20250108_184649_32\",\n            \"20250108_184650_35\",\n            \"20250108_184651_36\",\n            \"20250108_184651_37\",\n            \"20250108_184652_38\",\n            \"20250108_184653_40\",\n            \"20250108_184653_41\"\n        ],\n        \"pos\": [\n            [\n                1283.603048467556,\n                742.6684870837455,\n                3.54759650518017\n            ],\n            [\n                1279.103048467556,\n                750.4627157178056,\n                3.54759650518017\n            ],\n            [\n                1274.603048467556,\n                758.2569443518657,\n                3.54759650518017\n            ],\n            [\n                1270.103048467556,\n                766.0511729859259,\n                3.54759650518017\n            ],\n            [\n                1265.603048467556,\n                773.845401619986,\n                3.54759650518017\n            ],\n            [\n                1261.103048467556,\n                781.6396302540461,\n                3.54759650518017\n            ],\n            [\n                1256.603048467556,\n                789.4338588881062,\n                3.54759650518017\n            ],\n            [\n                1252.103048467556,\n                797.2280875221663,\n                3.54759650518017\n            ],\n            [\n                1247.603048467556,\n                805.0223161562265,\n                3.54759650518017\n            ],\n            [\n                1243.103048467556,\n                812.8165447902866,\n                3.54759650518017\n            ],\n            [\n                1238.603048467556,\n                820.6107734243467,\n                3.54759650518017\n            ],\n            [\n                1234.103048467556,\n                828.4050020584068,\n                3.54759650518017\n            ],\n            [\n                1232.603048467556,\n                831.0030782697602,\n                3.54759650518017\n            ],\n            [\n                1232.603048467556,\n                831.0030782697602,\n                3.54759650518017\n            ],\n            [\n                1232.603048467556,\n                834.0030782697602,\n                3.54759650518017\n            ],\n            [\n                1231.103048467556,\n                836.6011544811136,\n                3.54759650518017\n            ],\n            [\n                1229.603048467556,\n                839.1992306924669,\n                3.54759650518017\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948872,\n            1.5707963267948966,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_average/2025-1-8_21-21-24_1500474762\",\n        \"gpt_instruction\": \"Proceed directly ahead towards a large brown building with windows , then slightly turn left and walk straight towards a taller brown brick commercial structure featuring multiple stories and a grid of square windows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250108_212128_2\",\n            \"20250108_212129_5\",\n            \"20250108_212130_8\",\n            \"20250108_212131_11\",\n            \"20250108_212132_14\",\n            \"20250108_212133_17\",\n            \"20250108_212134_19\",\n            \"20250108_212135_20\",\n            \"20250108_212136_23\",\n            \"20250108_212137_26\",\n            \"20250108_212138_29\",\n            \"20250108_212139_30\",\n            \"20250108_212139_31\"\n        ],\n        \"pos\": [\n            [\n                1059.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1050.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1041.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1032.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1023.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1014.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1008.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1005.5822043606254,\n                817.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                1000.3860519379186,\n                814.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                992.5918233038585,\n                810.3614526952919,\n                -26.888958510984654\n            ],\n            [\n                984.7975946697984,\n                805.8614526952919,\n                -26.888958510984654\n            ],\n            [\n                982.199518458445,\n                804.3614526952919,\n                -26.888958510984654\n            ],\n            [\n                979.6014422470917,\n                802.8614526952919,\n                -26.888958510984654\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_long/2025-01-19_01-08-06_438584\",\n        \"gpt_instruction\": \"Proceed straight towards the gray building , featuring large arched windows with columns . Slightly ascend to encounter a carved stone sculpture resembling a crouching creature , likely a gargoyle . Then , slightly veer right to the large brown building adorned with arched windows . Next , slightly turn left heading straight towards another brown building , this time with large rectangular windows topped with a red section , identified as a brick building . Finally , descend to a large , multi - story commercial or office building characterized by numerous evenly spaced rectangular windows with arches at the top .\",\n        \"action\": [\n            9,\n            4,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_010809_2\",\n            \"20250119_010809_3\",\n            \"20250119_010812_6\",\n            \"20250119_010814_9\",\n            \"20250119_010817_12\",\n            \"20250119_010819_15\",\n            \"20250119_010821_18\",\n            \"20250119_010824_21\",\n            \"20250119_010826_24\",\n            \"20250119_010827_25\",\n            \"20250119_010828_26\",\n            \"20250119_010830_29\",\n            \"20250119_010831_30\",\n            \"20250119_010834_33\",\n            \"20250119_010836_36\",\n            \"20250119_010838_39\",\n            \"20250119_010841_42\",\n            \"20250119_010843_45\",\n            \"20250119_010845_48\",\n            \"20250119_010848_51\",\n            \"20250119_010849_52\",\n            \"20250119_010849_53\",\n            \"20250119_010850_54\"\n        ],\n        \"pos\": [\n            [\n                879.1182727226317,\n                -100.12155985486402,\n                3.54759650518017\n            ],\n            [\n                877.6182727226317,\n                -97.5234836435107,\n                3.54759650518017\n            ],\n            [\n                874.6182727226317,\n                -92.32733122080407,\n                6.54759650518017\n            ],\n            [\n                870.1182727226317,\n                -84.53310258674412,\n                6.54759650518017\n            ],\n            [\n                865.6182727226317,\n                -76.73887395268417,\n                6.54759650518017\n            ],\n            [\n                861.1182727226317,\n                -68.94464531862423,\n                6.54759650518017\n            ],\n            [\n                856.6182727226317,\n                -61.15041668456428,\n                6.54759650518017\n            ],\n            [\n                852.1182727226317,\n                -53.35618805050433,\n                6.54759650518017\n            ],\n            [\n                847.6182727226317,\n                -45.56195941644438,\n                6.54759650518017\n            ],\n            [\n                846.1182727226317,\n                -42.963883205091065,\n                6.54759650518017\n            ],\n            [\n                844.6182727226317,\n                -40.36580699373775,\n                6.54759650518017\n            ],\n            [\n                844.6182727226317,\n                -34.36580699373775,\n                6.54759650518017\n            ],\n            [\n                844.6182727226317,\n                -31.36580699373775,\n                6.54759650518017\n            ],\n            [\n                841.6182727226317,\n                -26.169654571031117,\n                6.54759650518017\n            ],\n            [\n                837.1182727226317,\n                -18.37542593697117,\n                6.54759650518017\n            ],\n            [\n                832.6182727226317,\n                -10.581197302911221,\n                6.54759650518017\n            ],\n            [\n                828.1182727226317,\n                -2.786968668851273,\n                6.54759650518017\n            ],\n            [\n                823.6182727226317,\n                5.007259965208675,\n                6.54759650518017\n            ],\n            [\n                819.1182727226317,\n                12.801488599268623,\n                6.54759650518017\n            ],\n            [\n                814.6182727226317,\n                20.59571723332857,\n                6.54759650518017\n            ],\n            [\n                813.1182727226317,\n                23.193793444681887,\n                6.54759650518017\n            ],\n            [\n                811.6182727226317,\n                25.791869656035203,\n                6.54759650518017\n            ],\n            [\n                811.6182727226317,\n                25.791869656035203,\n                3.54759650518017\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_average/2025-1-9_21-34-35_379461075\",\n        \"gpt_instruction\": \"Move ahead to a medium - sized gray building with rectangular windows and a flat roof , then slightly turn right and head straight to it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_213438_2\",\n            \"20250109_213439_5\",\n            \"20250109_213440_7\",\n            \"20250109_213441_8\",\n            \"20250109_213442_11\",\n            \"20250109_213443_14\",\n            \"20250109_213444_17\",\n            \"20250109_213445_20\",\n            \"20250109_213446_22\",\n            \"20250109_213447_23\"\n        ],\n        \"pos\": [\n            [\n                1407.324700740385,\n                497.79226019810903,\n                17.46431138117525\n            ],\n            [\n                1416.324700740385,\n                497.79226019810903,\n                17.46431138117525\n            ],\n            [\n                1422.324700740385,\n                497.79226019810903,\n                17.46431138117525\n            ],\n            [\n                1425.324700740385,\n                497.79226019810903,\n                17.46431138117525\n            ],\n            [\n                1430.5208531630917,\n                494.79226019810903,\n                17.46431138117525\n            ],\n            [\n                1438.3150817971518,\n                490.29226019810903,\n                17.46431138117525\n            ],\n            [\n                1446.109310431212,\n                485.79226019810903,\n                17.46431138117525\n            ],\n            [\n                1453.903539065272,\n                481.29226019810903,\n                17.46431138117525\n            ],\n            [\n                1459.0996914879788,\n                478.29226019810903,\n                17.46431138117525\n            ],\n            [\n                1461.6977676993322,\n                476.79226019810903,\n                17.46431138117525\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_short/2025-1-9_17-38-58_70788355\",\n        \"gpt_instruction\": \"Proceed straight towards the building , a large high - rise structure with a distinct facade . It features a dark color with illuminated sections and a grid - like pattern that includes some lit - up windows , occupying most of the frame .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_173902_2\",\n            \"20250109_173903_5\",\n            \"20250109_173904_7\",\n            \"20250109_173904_8\"\n        ],\n        \"pos\": [\n            [\n                999.7168406858884,\n                430.5087134355858,\n                36.154775318277345\n            ],\n            [\n                991.9226120518283,\n                435.0087134355858,\n                36.154775318277345\n            ],\n            [\n                986.7264596291216,\n                438.0087134355858,\n                36.154775318277345\n            ],\n            [\n                984.1283834177682,\n                439.5087134355858,\n                36.154775318277345\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_short/2025-1-9_17-6-53_1520223205\",\n        \"gpt_instruction\": \"Move forward towards a dark structure with white grid lines , characterized by a curved glass facade with a grid pattern . It is large , occupying most of the image center , and is a skyscraper or office building .\",\n        \"action\": [\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_170656_1\",\n            \"20250109_170656_2\"\n        ],\n        \"pos\": [\n            [\n                1199.9496091436129,\n                397.0675080247116,\n                36.154775318277345\n            ],\n            [\n                1201.4496091436129,\n                399.6655842360649,\n                36.154775318277345\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/low_average/2025-01-18_22-08-24_294515\",\n        \"gpt_instruction\": \"Start by ascending towards a white building featuring multiple black windows of medium size . Continue forward to reach a light beige building with large , grid - patterned rectangular windows . Slightly turn left and proceed to arrive at a light gray building characterized by a flat rooftop with peeling paint , which is also large in size . Then , slightly turn right and move ahead to a white , modern office building that has medium height , noted for its rectangular windows with a grid - like arrangement .\",\n        \"action\": [\n            1,\n            4,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250118_220825_0\",\n            \"20250118_220826_1\",\n            \"20250118_220828_4\",\n            \"20250118_220830_7\",\n            \"20250118_220833_10\",\n            \"20250118_220835_13\",\n            \"20250118_220836_14\",\n            \"20250118_220838_17\",\n            \"20250118_220841_20\",\n            \"20250118_220843_23\",\n            \"20250118_220844_24\",\n            \"20250118_220846_27\",\n            \"20250118_220848_30\",\n            \"20250118_220849_31\"\n        ],\n        \"pos\": [\n            [\n                1291.2593551336568,\n                -151.74239262578803,\n                -26.888958510984654\n            ],\n            [\n                1288.6612789223034,\n                -153.24239262578803,\n                -26.888958510984654\n            ],\n            [\n                1283.4651264995966,\n                -156.24239262578803,\n                -23.888958510984654\n            ],\n            [\n                1275.6708978655365,\n                -160.74239262578803,\n                -23.888958510984654\n            ],\n            [\n                1267.8766692314764,\n                -165.24239262578803,\n                -23.888958510984654\n            ],\n            [\n                1260.0824405974163,\n                -169.74239262578803,\n                -23.888958510984654\n            ],\n            [\n                1257.484364386063,\n                -171.24239262578803,\n                -23.888958510984654\n            ],\n            [\n                1254.484364386063,\n                -176.43854504849466,\n                -23.888958510984654\n            ],\n            [\n                1249.984364386063,\n                -184.2327736825546,\n                -23.888958510984654\n            ],\n            [\n                1245.484364386063,\n                -192.02700231661456,\n                -23.888958510984654\n            ],\n            [\n                1243.984364386063,\n                -194.62507852796787,\n                -23.888958510984654\n            ],\n            [\n                1238.7882119633562,\n                -197.62507852796787,\n                -23.888958510984654\n            ],\n            [\n                1230.993983329296,\n                -202.12507852796787,\n                -23.888958510984654\n            ],\n            [\n                1228.3959071179427,\n                -203.62507852796787,\n                -23.888958510984654\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/high_average/2025-1-9_16-51-22_1884661237\",\n        \"gpt_instruction\": \"Continue proceeding straight until you reach a large , green , square area characterized by grass , then slightly turn left and move ahead towards a tall skyscraper , which is a large and tall structure featuring a black and white color scheme , with a grid pattern of windows and some illuminated sections .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_165125_2\",\n            \"20250109_165126_5\",\n            \"20250109_165128_8\",\n            \"20250109_165129_11\",\n            \"20250109_165130_14\",\n            \"20250109_165131_17\",\n            \"20250109_165132_20\",\n            \"20250109_165134_23\",\n            \"20250109_165135_26\",\n            \"20250109_165135_27\",\n            \"20250109_165136_30\",\n            \"20250109_165138_33\",\n            \"20250109_165139_36\",\n            \"20250109_165139_37\"\n        ],\n        \"pos\": [\n            [\n                1219.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1210.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1201.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1192.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1183.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1174.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1165.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1156.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1147.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1144.6082345870059,\n                913.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1139.4120821642991,\n                910.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1131.617853530239,\n                906.1622814652469,\n                17.46431138117525\n            ],\n            [\n                1123.823624896179,\n                901.6622814652469,\n                17.46431138117525\n            ],\n            [\n                1121.2255486848255,\n                900.1622814652469,\n                17.46431138117525\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_18/astar_data/medium_average_updown/2025-1-14_11-46-56_1189641421\",\n        \"gpt_instruction\": \"First , climb to the off - white , medium - sized building featuring a rectangular structure with large windows . Then , move ahead to the brown , large building that showcases rectangular windows with arches on the upper floors . Slightly turn left and head straight to it . Finally , slightly stop and descend to the modern office building , brown with black glass , featuring large glass windows in a grid pattern and elevated on columns , medium to large in size .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_114656_0\",\n            \"20250114_114659_1\",\n            \"20250114_114700_2\",\n            \"20250114_114700_3\",\n            \"20250114_114701_4\",\n            \"20250114_114701_5\",\n            \"20250114_114701_6\",\n            \"20250114_114702_7\",\n            \"20250114_114702_8\",\n            \"20250114_114703_9\",\n            \"20250114_114703_10\",\n            \"20250114_114704_11\",\n            \"20250114_114704_12\",\n            \"20250114_114705_13\",\n            \"20250114_114705_14\",\n            \"20250114_114706_17\",\n            \"20250114_114708_20\",\n            \"20250114_114709_23\",\n            \"20250114_114710_26\",\n            \"20250114_114712_29\",\n            \"20250114_114713_32\",\n            \"20250114_114714_35\",\n            \"20250114_114716_38\",\n            \"20250114_114716_40\",\n            \"20250114_114717_41\",\n            \"20250114_114718_44\",\n            \"20250114_114719_47\",\n            \"20250114_114721_50\",\n            \"20250114_114722_53\",\n            \"20250114_114723_55\",\n            \"20250114_114723_56\",\n            \"20250114_114724_57\",\n            \"20250114_114724_58\",\n            \"20250114_114725_59\",\n            \"20250114_114725_60\",\n            \"20250114_114726_61\",\n            \"20250114_114726_62\",\n            \"20250114_114726_63\",\n            \"20250114_114727_64\",\n            \"20250114_114727_65\",\n            \"20250114_114728_66\",\n            \"20250114_114728_67\",\n            \"20250114_114729_68\",\n            \"20250114_114729_69\",\n            \"20250114_114729_70\",\n            \"20250114_114730_71\"\n        ],\n        \"pos\": [\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -46.77311792602363\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -43.77311792602363\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -40.77311792602363\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -37.77311792602363\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -34.77311792602363\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -31.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -28.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -25.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -22.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -19.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -16.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -13.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -10.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -7.773117926023627\n            ],\n            [\n                935.5569030002694,\n                75.00912725847023,\n                -4.773117926023627\n            ],\n            [\n                929.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                920.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                911.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                902.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                893.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                884.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                875.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                866.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                860.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                857.5569030002694,\n                75.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                852.3607505775626,\n                72.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                844.5665219435025,\n                67.50912725847023,\n                -1.7731179260236267\n            ],\n            [\n                836.7722933094424,\n                63.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                828.9780646753823,\n                58.50912725847023,\n                -1.7731179260236267\n            ],\n            [\n                823.7819122526755,\n                55.50912725847023,\n                -1.7731179260236267\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -1.7731179260236267\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -4.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -7.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -10.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -13.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -16.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -19.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -22.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -25.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -28.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -31.773117926023627\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -34.77311792602363\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -37.77311792602363\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -40.77311792602363\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -43.77311792602363\n            ],\n            [\n                821.1838360413221,\n                54.00912725847023,\n                -46.77311792602363\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_average/2025-1-9_12-28-13_2103318776\",\n        \"gpt_instruction\": \"Move ahead to a light gray circular layered structure , which could be a water tank or a monument , then make a slight right turn and proceed directly to a grey modern building featuring medium rectangular windows . Veer left slightly to it . Slightly turn right again to continue straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            8,\n            2,\n            2,\n            9,\n            9,\n            3,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_122817_2\",\n            \"20250109_122818_5\",\n            \"20250109_122819_8\",\n            \"20250109_122820_11\",\n            \"20250109_122821_13\",\n            \"20250109_122821_14\",\n            \"20250109_122823_17\",\n            \"20250109_122824_20\",\n            \"20250109_122825_23\",\n            \"20250109_122826_25\",\n            \"20250109_122826_26\",\n            \"20250109_122827_27\",\n            \"20250109_122828_30\",\n            \"20250109_122829_33\",\n            \"20250109_122829_34\",\n            \"20250109_122831_37\",\n            \"20250109_122832_40\",\n            \"20250109_122832_41\"\n        ],\n        \"pos\": [\n            [\n                -127.1057155276578,\n                -72.63247464659972,\n                57.77929372836308\n            ],\n            [\n                -122.6057155276578,\n                -80.42670328065967,\n                57.77929372836308\n            ],\n            [\n                -118.1057155276578,\n                -88.22093191471961,\n                57.77929372836308\n            ],\n            [\n                -113.6057155276578,\n                -96.01516054877956,\n                57.77929372836308\n            ],\n            [\n                -110.6057155276578,\n                -101.2113129714862,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -103.80938918283951,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -109.80938918283951,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -118.80938918283951,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -127.80938918283951,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -133.8093891828395,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -136.8093891828395,\n                57.77929372836308\n            ],\n            [\n                -109.1057155276578,\n                -136.8093891828395,\n                57.77929372836308\n            ],\n            [\n                -103.90956310495116,\n                -139.8093891828395,\n                57.77929372836308\n            ],\n            [\n                -96.11533447089121,\n                -144.3093891828395,\n                57.77929372836308\n            ],\n            [\n                -93.5172582595379,\n                -145.8093891828395,\n                57.77929372836308\n            ],\n            [\n                -90.5172582595379,\n                -151.00554160554614,\n                57.77929372836308\n            ],\n            [\n                -86.0172582595379,\n                -158.7997702396061,\n                57.77929372836308\n            ],\n            [\n                -84.5172582595379,\n                -161.3978464509594,\n                57.77929372836308\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_short/2025-1-9_12-33-47_780821396\",\n        \"gpt_instruction\": \"Move straight towards a large brown building characterized by flat structures with small protruding components , then slightly turn left and proceed straight to a large brown building exterior featuring vent - like structures with horizontal slats , occupying the majority of the frontal view , and equipped with a large louvered vent or light fixture .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_123350_2\",\n            \"20250109_123351_5\",\n            \"20250109_123353_8\",\n            \"20250109_123353_9\",\n            \"20250109_123353_10\",\n            \"20250109_123355_13\",\n            \"20250109_123355_14\",\n            \"20250109_123356_15\"\n        ],\n        \"pos\": [\n            [\n                -262.7599865513647,\n                -155.0728361316883,\n                72.24787185243571\n            ],\n            [\n                -258.2599865513647,\n                -147.27860749762834,\n                72.24787185243571\n            ],\n            [\n                -253.7599865513647,\n                -139.4843788635684,\n                72.24787185243571\n            ],\n            [\n                -252.2599865513647,\n                -136.88630265221508,\n                72.24787185243571\n            ],\n            [\n                -250.7599865513647,\n                -134.28822644086176,\n                72.24787185243571\n            ],\n            [\n                -250.7599865513647,\n                -128.28822644086176,\n                72.24787185243571\n            ],\n            [\n                -250.7599865513647,\n                -125.28822644086176,\n                72.24787185243571\n            ],\n            [\n                -250.7599865513647,\n                -122.28822644086176,\n                72.24787185243571\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/low_short/2025-1-2_16-58-6_1760243555\",\n        \"gpt_instruction\": \"Move forward to the medium - sized gray apartment building characterized by horizontal slats and protruding balconies , then veer right toward the large gray skyscraper featuring rectangular windows . Next , go directly ahead to the tall light beige modern building with similar window designs . Slightly turn left , moving ahead to the large gray modern building with rectangular windows and a concrete texture . Finally , slightly turn right to go directly ahead to the large light beige modern high - rise building featuring recessed windows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            3,\n            3,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            8,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_165809_2\",\n            \"20250102_165810_5\",\n            \"20250102_165812_8\",\n            \"20250102_165812_9\",\n            \"20250102_165812_10\",\n            \"20250102_165814_13\",\n            \"20250102_165815_16\",\n            \"20250102_165815_17\",\n            \"20250102_165816_18\",\n            \"20250102_165817_21\",\n            \"20250102_165818_24\",\n            \"20250102_165819_26\",\n            \"20250102_165819_27\",\n            \"20250102_165820_29\",\n            \"20250102_165821_30\"\n        ],\n        \"pos\": [\n            [\n                -104.01281563157974,\n                -238.71295749113162,\n                30.35382592660265\n            ],\n            [\n                -95.01281563157974,\n                -238.71295749113162,\n                30.35382592660265\n            ],\n            [\n                -86.01281563157974,\n                -238.71295749113162,\n                30.35382592660265\n            ],\n            [\n                -83.01281563157974,\n                -238.71295749113162,\n                30.35382592660265\n            ],\n            [\n                -83.01281563157974,\n                -238.71295749113162,\n                30.35382592660265\n            ],\n            [\n                -80.01281563157974,\n                -243.90910991383825,\n                30.35382592660265\n            ],\n            [\n                -75.51281563157974,\n                -251.7033385478982,\n                30.35382592660265\n            ],\n            [\n                -74.01281563157974,\n                -254.30141475925151,\n                30.35382592660265\n            ],\n            [\n                -72.51281563157974,\n                -256.8994909706048,\n                30.35382592660265\n            ],\n            [\n                -67.31666320887311,\n                -259.8994909706048,\n                30.35382592660265\n            ],\n            [\n                -59.52243457481316,\n                -264.3994909706048,\n                30.35382592660265\n            ],\n            [\n                -54.32628215210653,\n                -267.3994909706048,\n                30.35382592660265\n            ],\n            [\n                -51.72820594075321,\n                -268.8994909706048,\n                30.35382592660265\n            ],\n            [\n                -50.22820594075321,\n                -271.4975671819581,\n                30.35382592660265\n            ],\n            [\n                -48.72820594075321,\n                -274.09564339331143,\n                30.35382592660265\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.047197551196593,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_short/2025-1-9_20-48-29_1376710097\",\n        \"gpt_instruction\": \"Continue moving forward until you reach the building characterized by a grey flat roof with ventilation openings of large size and designated as a building roof . Then , make a slight right turn and proceed straight towards the commercial building , which features a light gray flat roof with multiple ventilation units and distinct geometric indentations near the edge , and is medium - sized in a dense urban area .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_204832_2\",\n            \"20250109_204834_5\",\n            \"20250109_204835_8\",\n            \"20250109_204836_11\",\n            \"20250109_204838_14\",\n            \"20250109_204839_17\",\n            \"20250109_204840_20\",\n            \"20250109_204841_21\",\n            \"20250109_204842_23\",\n            \"20250109_204842_24\"\n        ],\n        \"pos\": [\n            [\n                149.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                140.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                131.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                122.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                113.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                104.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                95.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                92.92645169574357,\n                -109.42481849120705,\n                103.16609352836659\n            ],\n            [\n                90.32837548439025,\n                -107.92481849120705,\n                103.16609352836659\n            ],\n            [\n                87.73029927303693,\n                -106.42481849120705,\n                103.16609352836659\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_average/2025-1-9_11-49-34_1780695788\",\n        \"gpt_instruction\": \"Advance forward toward a large yellow metallic structure with a horizontal beam ( a construction crane ) , slightly turn right and move ahead to reach a dark rectangular multi - story building with glass windows ( a skyscraper ) , then slightly turn left and walk straight to approach a dark gray cylindrical structure with rounded edges and visible ventilation pipes , mid-sized compared to surrounding buildings ( a commercial / utility building ) .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            1,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_114938_2\",\n            \"20250109_114939_5\",\n            \"20250109_114940_8\",\n            \"20250109_114942_11\",\n            \"20250109_114943_14\",\n            \"20250109_114943_15\",\n            \"20250109_114944_16\",\n            \"20250109_114945_19\",\n            \"20250109_114945_20\",\n            \"20250109_114946_21\",\n            \"20250109_114946_22\",\n            \"20250109_114946_23\"\n        ],\n        \"pos\": [\n            [\n                -47.56557370746761,\n                130.99120274334882,\n                57.77929372836308\n            ],\n            [\n                -39.77134507340766,\n                126.49120274334882,\n                57.77929372836308\n            ],\n            [\n                -31.97711643934771,\n                121.99120274334882,\n                57.77929372836308\n            ],\n            [\n                -24.182887805287763,\n                117.49120274334882,\n                57.77929372836308\n            ],\n            [\n                -16.388659171227815,\n                112.99120274334882,\n                57.77929372836308\n            ],\n            [\n                -13.790582959874499,\n                111.49120274334882,\n                57.77929372836308\n            ],\n            [\n                -11.192506748521183,\n                109.99120274334882,\n                57.77929372836308\n            ],\n            [\n                -8.192506748521183,\n                104.79505032064219,\n                57.77929372836308\n            ],\n            [\n                -6.692506748521183,\n                102.19697410928887,\n                57.77929372836308\n            ],\n            [\n                -5.192506748521183,\n                99.59889789793556,\n                57.77929372836308\n            ],\n            [\n                -5.192506748521183,\n                99.59889789793556,\n                57.77929372836308\n            ],\n            [\n                -2.5944305371678666,\n                98.09889789793556,\n                57.77929372836308\n            ]\n        ],\n        \"yaw\": [\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.523598775598299,\n            -0.523598775598299\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_short/2025-1-9_21-0-7_438792350\",\n        \"gpt_instruction\": \"Proceed directly forward to the gray high - rise building characterized by modern architecture , rooftop structures , and ducts , then slightly turn right and walk straight to the gray building with rounded edges , also featuring rooftop structures and ductwork ; it is large and has multiple stories .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_210011_2\",\n            \"20250109_210012_5\",\n            \"20250109_210014_8\",\n            \"20250109_210015_11\",\n            \"20250109_210015_12\",\n            \"20250109_210016_13\",\n            \"20250109_210017_16\",\n            \"20250109_210018_18\",\n            \"20250109_210018_19\"\n        ],\n        \"pos\": [\n            [\n                249.07239744579363,\n                -190.43632138445173,\n                103.16609352836659\n            ],\n            [\n                241.27816881173368,\n                -194.93632138445173,\n                103.16609352836659\n            ],\n            [\n                233.48394017767373,\n                -199.43632138445173,\n                103.16609352836659\n            ],\n            [\n                225.68971154361378,\n                -203.93632138445173,\n                103.16609352836659\n            ],\n            [\n                223.09163533226047,\n                -205.43632138445173,\n                103.16609352836659\n            ],\n            [\n                220.49355912090715,\n                -206.93632138445173,\n                103.16609352836659\n            ],\n            [\n                214.49355912090715,\n                -206.93632138445173,\n                103.16609352836659\n            ],\n            [\n                208.49355912090715,\n                -206.93632138445173,\n                103.16609352836659\n            ],\n            [\n                205.49355912090715,\n                -206.93632138445173,\n                103.16609352836659\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_long/2025-1-9_14-16-7_964445884\",\n        \"gpt_instruction\": \"Advance forward to a gray high - rise building characterized by a large rectangular structure with a repeated window pattern . Then , slightly turn left and proceed straight to it . Next , slightly turn right and keep going straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_141611_2\",\n            \"20250109_141612_5\",\n            \"20250109_141613_8\",\n            \"20250109_141614_9\",\n            \"20250109_141614_10\",\n            \"20250109_141615_13\",\n            \"20250109_141617_16\",\n            \"20250109_141618_19\",\n            \"20250109_141619_22\",\n            \"20250109_141620_25\",\n            \"20250109_141622_28\",\n            \"20250109_141623_31\",\n            \"20250109_141624_34\",\n            \"20250109_141625_37\",\n            \"20250109_141626_38\",\n            \"20250109_141626_39\",\n            \"20250109_141627_42\",\n            \"20250109_141628_45\",\n            \"20250109_141630_48\",\n            \"20250109_141630_49\"\n        ],\n        \"pos\": [\n            [\n                -197.82073422066455,\n                -74.12127069226673,\n                53.770209583431736\n            ],\n            [\n                -193.32073422066455,\n                -81.91549932632668,\n                53.770209583431736\n            ],\n            [\n                -188.82073422066455,\n                -89.70972796038663,\n                53.770209583431736\n            ],\n            [\n                -187.32073422066455,\n                -92.30780417173995,\n                53.770209583431736\n            ],\n            [\n                -185.82073422066455,\n                -94.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -180.62458179795792,\n                -97.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -172.83035316389797,\n                -102.40588038309326,\n                53.770209583431736\n            ],\n            [\n                -165.03612452983802,\n                -106.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -157.24189589577807,\n                -111.40588038309326,\n                53.770209583431736\n            ],\n            [\n                -149.44766726171812,\n                -115.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -141.65343862765818,\n                -120.40588038309326,\n                53.770209583431736\n            ],\n            [\n                -133.85920999359823,\n                -124.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -126.06498135953828,\n                -129.40588038309326,\n                53.770209583431736\n            ],\n            [\n                -118.27075272547833,\n                -133.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -115.67267651412502,\n                -135.40588038309326,\n                53.770209583431736\n            ],\n            [\n                -113.0746003027717,\n                -136.90588038309326,\n                53.770209583431736\n            ],\n            [\n                -110.0746003027717,\n                -142.1020328057999,\n                53.770209583431736\n            ],\n            [\n                -105.5746003027717,\n                -149.89626143985984,\n                53.770209583431736\n            ],\n            [\n                -101.0746003027717,\n                -157.6904900739198,\n                53.770209583431736\n            ],\n            [\n                -99.5746003027717,\n                -160.2885662852731,\n                53.770209583431736\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_short/2025-1-9_12-35-14_570073850\",\n        \"gpt_instruction\": \"Proceed straight to the large gray residential apartment building with curved balconies and corners , then slightly turn left and continue straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_123518_2\",\n            \"20250109_123519_5\",\n            \"20250109_123520_8\",\n            \"20250109_123521_9\",\n            \"20250109_123521_10\",\n            \"20250109_123522_13\",\n            \"20250109_123524_16\",\n            \"20250109_123524_17\"\n        ],\n        \"pos\": [\n            [\n                -104.97224670493236,\n                -269.21401298943226,\n                72.24787185243571\n            ],\n            [\n                -109.47224670493236,\n                -261.4197843553723,\n                72.24787185243571\n            ],\n            [\n                -113.97224670493236,\n                -253.62555572131237,\n                72.24787185243571\n            ],\n            [\n                -115.47224670493236,\n                -251.02747950995905,\n                72.24787185243571\n            ],\n            [\n                -116.97224670493236,\n                -248.42940329860573,\n                72.24787185243571\n            ],\n            [\n                -122.16839912763899,\n                -245.42940329860573,\n                72.24787185243571\n            ],\n            [\n                -129.96262776169894,\n                -240.92940329860573,\n                72.24787185243571\n            ],\n            [\n                -132.56070397305226,\n                -239.42940329860573,\n                72.24787185243571\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_average/2025-1-9_20-28-1_596516649\",\n        \"gpt_instruction\": \"Move forward to a gray building characterized by multiple rectangular windows arranged in a grid pattern ; it is a tall , high - rise structure . Slightly turn right and walk straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_202805_2\",\n            \"20250109_202806_5\",\n            \"20250109_202807_8\",\n            \"20250109_202809_11\",\n            \"20250109_202810_14\",\n            \"20250109_202811_16\",\n            \"20250109_202811_17\",\n            \"20250109_202812_19\",\n            \"20250109_202812_20\"\n        ],\n        \"pos\": [\n            [\n                -135.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -144.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -153.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -162.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -171.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -177.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -180.28435101037988,\n                -104.79580088425797,\n                100.009458039131\n            ],\n            [\n                -182.8824272217332,\n                -103.29580088425797,\n                100.009458039131\n            ],\n            [\n                -185.4805034330865,\n                -101.79580088425797,\n                100.009458039131\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_short/2025-1-9_21-30-38_349517445\",\n        \"gpt_instruction\": \"Proceed straight towards a light gray , medium - sized building characterized by modern architectural design with horizontal stripes and aligned rectangular windows . Then , slightly turn right and continue straight towards another light gray building , which is a tall high - rise featuring a grid - like arrangement of square windows and a flat - top design .\",\n        \"action\": [\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_213041_2\",\n            \"20250109_213042_3\",\n            \"20250109_213042_4\",\n            \"20250109_213044_7\",\n            \"20250109_213045_10\",\n            \"20250109_213046_13\",\n            \"20250109_213048_16\",\n            \"20250109_213049_19\",\n            \"20250109_213049_20\"\n        ],\n        \"pos\": [\n            [\n                -201.8419799804941,\n                135.13643992462698,\n                103.16609352836659\n            ],\n            [\n                -198.8419799804941,\n                135.13643992462698,\n                103.16609352836659\n            ],\n            [\n                -195.8419799804941,\n                135.13643992462698,\n                103.16609352836659\n            ],\n            [\n                -190.64582755778747,\n                132.13643992462698,\n                103.16609352836659\n            ],\n            [\n                -182.85159892372752,\n                127.63643992462698,\n                103.16609352836659\n            ],\n            [\n                -175.05737028966757,\n                123.13643992462698,\n                103.16609352836659\n            ],\n            [\n                -167.26314165560763,\n                118.63643992462698,\n                103.16609352836659\n            ],\n            [\n                -159.46891302154768,\n                114.13643992462698,\n                103.16609352836659\n            ],\n            [\n                -156.87083681019436,\n                112.63643992462698,\n                103.16609352836659\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_short/2025-1-9_12-0-20_1626276121\",\n        \"gpt_instruction\": \"Veer left toward a dark gray structure featuring horizontal slats with visible shadow detailing , characterized by its large size and classified as a building . Then , advance forward to it . Next , slightly turn right and move forward toward it .\",\n        \"action\": [\n            1,\n            2,\n            2,\n            9,\n            8,\n            3,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_120020_0\",\n            \"20250109_120023_1\",\n            \"20250109_120024_2\",\n            \"20250109_120025_5\",\n            \"20250109_120026_7\",\n            \"20250109_120026_8\",\n            \"20250109_120027_11\",\n            \"20250109_120028_12\",\n            \"20250109_120028_13\"\n        ],\n        \"pos\": [\n            [\n                -132.1278341134414,\n                -78.34970404155976,\n                72.24787185243571\n            ],\n            [\n                -129.1278341134414,\n                -78.34970404155976,\n                72.24787185243571\n            ],\n            [\n                -129.1278341134414,\n                -78.34970404155976,\n                72.24787185243571\n            ],\n            [\n                -126.1278341134414,\n                -73.15355161885313,\n                72.24787185243571\n            ],\n            [\n                -123.1278341134414,\n                -67.9573991961465,\n                72.24787185243571\n            ],\n            [\n                -121.6278341134414,\n                -65.35932298479318,\n                72.24787185243571\n            ],\n            [\n                -116.43168169073476,\n                -62.35932298479318,\n                72.24787185243571\n            ],\n            [\n                -113.83360547938145,\n                -60.85932298479318,\n                72.24787185243571\n            ],\n            [\n                -111.23552926802813,\n                -59.35932298479318,\n                72.24787185243571\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_average/2025-1-9_20-54-13_943947739\",\n        \"gpt_instruction\": \"Head straight towards the gray building with rectangular windows and modern design ; it 's tall . Then , slightly turn right and continue straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_205416_2\",\n            \"20250109_205417_5\",\n            \"20250109_205419_8\",\n            \"20250109_205420_11\",\n            \"20250109_205421_14\",\n            \"20250109_205422_17\",\n            \"20250109_205423_20\",\n            \"20250109_205425_23\",\n            \"20250109_205425_24\",\n            \"20250109_205426_25\",\n            \"20250109_205426_26\"\n        ],\n        \"pos\": [\n            [\n                239.7862680978692,\n                -67.0457698695788,\n                100.009458039131\n            ],\n            [\n                247.58049673192914,\n                -71.5457698695788,\n                100.009458039131\n            ],\n            [\n                255.3747253659891,\n                -76.0457698695788,\n                100.009458039131\n            ],\n            [\n                263.16895400004904,\n                -80.5457698695788,\n                100.009458039131\n            ],\n            [\n                270.963182634109,\n                -85.0457698695788,\n                100.009458039131\n            ],\n            [\n                278.75741126816894,\n                -89.5457698695788,\n                100.009458039131\n            ],\n            [\n                286.5516399022289,\n                -94.0457698695788,\n                100.009458039131\n            ],\n            [\n                294.34586853628883,\n                -98.5457698695788,\n                100.009458039131\n            ],\n            [\n                296.94394474764215,\n                -100.0457698695788,\n                100.009458039131\n            ],\n            [\n                296.94394474764215,\n                -100.0457698695788,\n                100.009458039131\n            ],\n            [\n                298.44394474764215,\n                -102.64384608093212,\n                100.009458039131\n            ]\n        ],\n        \"yaw\": [\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511965976,\n            -1.0471975511965976\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_average/2025-1-9_12-46-47_1977648522\",\n        \"gpt_instruction\": \"Proceed directly ahead to a large gray reinforced concrete structure characterized by rounded edges with grooves . Then , turn left towards another large gray concrete structure featuring a cylindrical shape . Next , move slightly forward to a large gray high - rise building with a rectangular structure and multiple windows . Slightly turn left again and move forward to a large light gray high - rise building adorned with vertical lines on the facade and rooftop antennas . Slightly turn left and go directly ahead to encounter it . Finally , slightly turn right and move forward to a large grey industrial or commercial building with a uniform concrete facade and horizontal window slits .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            2,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            8,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_124650_2\",\n            \"20250109_124651_5\",\n            \"20250109_124652_8\",\n            \"20250109_124653_9\",\n            \"20250109_124653_10\",\n            \"20250109_124654_11\",\n            \"20250109_124654_12\",\n            \"20250109_124654_13\",\n            \"20250109_124656_16\",\n            \"20250109_124657_19\",\n            \"20250109_124658_22\",\n            \"20250109_124659_25\",\n            \"20250109_124700_28\",\n            \"20250109_124700_29\",\n            \"20250109_124701_30\",\n            \"20250109_124702_33\",\n            \"20250109_124703_36\",\n            \"20250109_124705_39\",\n            \"20250109_124706_42\",\n            \"20250109_124706_43\",\n            \"20250109_124706_44\",\n            \"20250109_124708_47\",\n            \"20250109_124709_50\",\n            \"20250109_124710_53\",\n            \"20250109_124711_55\",\n            \"20250109_124711_56\",\n            \"20250109_124712_58\",\n            \"20250109_124712_59\"\n        ],\n        \"pos\": [\n            [\n                191.02300203115612,\n                87.8260315434583,\n                57.77929372836308\n            ],\n            [\n                191.02300203115612,\n                78.8260315434583,\n                57.77929372836308\n            ],\n            [\n                191.02300203115612,\n                69.8260315434583,\n                57.77929372836308\n            ],\n            [\n                191.02300203115612,\n                66.8260315434583,\n                57.77929372836308\n            ],\n            [\n                191.02300203115612,\n                63.8260315434583,\n                57.77929372836308\n            ],\n            [\n                191.02300203115612,\n                63.8260315434583,\n                57.77929372836308\n            ],\n            [\n                191.02300203115612,\n                63.8260315434583,\n                57.77929372836308\n            ],\n            [\n                193.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                199.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                208.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                217.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                226.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                235.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                238.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                241.62107824250944,\n                62.3260315434583,\n                57.77929372836308\n            ],\n            [\n                246.81723066521607,\n                65.3260315434583,\n                57.77929372836308\n            ],\n            [\n                254.61145929927602,\n                69.8260315434583,\n                57.77929372836308\n            ],\n            [\n                262.40568793333597,\n                74.3260315434583,\n                57.77929372836308\n            ],\n            [\n                270.1999165673959,\n                78.8260315434583,\n                57.77929372836308\n            ],\n            [\n                272.79799277874923,\n                80.3260315434583,\n                57.77929372836308\n            ],\n            [\n                275.39606899010255,\n                81.8260315434583,\n                57.77929372836308\n            ],\n            [\n                278.39606899010255,\n                87.02218396616493,\n                57.77929372836308\n            ],\n            [\n                282.89606899010255,\n                94.81641260022488,\n                57.77929372836308\n            ],\n            [\n                287.39606899010255,\n                102.61064123428483,\n                57.77929372836308\n            ],\n            [\n                290.39606899010255,\n                107.80679365699146,\n                57.77929372836308\n            ],\n            [\n                291.89606899010255,\n                110.40486986834478,\n                57.77929372836308\n            ],\n            [\n                294.49414520145586,\n                111.90486986834478,\n                57.77929372836308\n            ],\n            [\n                297.0922214128092,\n                113.40486986834478,\n                57.77929372836308\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -0.5235987755982989,\n            -0.5235987755982988,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_short/2025-1-9_12-47-8_653448036\",\n        \"gpt_instruction\": \"Proceed straight towards the tan wall with large rectangular tiles , then slightly turn right and go directly ahead to the brown wall with a large smooth surface .\",\n        \"action\": [\n            9,\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_124711_2\",\n            \"20250109_124713_5\",\n            \"20250109_124713_6\",\n            \"20250109_124713_7\",\n            \"20250109_124714_9\",\n            \"20250109_124715_10\"\n        ],\n        \"pos\": [\n            [\n                -52.596121207801986,\n                -243.3270355936679,\n                72.24787185243571\n            ],\n            [\n                -57.096121207801986,\n                -235.53280695960794,\n                72.24787185243571\n            ],\n            [\n                -58.596121207801986,\n                -232.93473074825462,\n                72.24787185243571\n            ],\n            [\n                -60.096121207801986,\n                -230.3366545369013,\n                72.24787185243571\n            ],\n            [\n                -60.096121207801986,\n                -227.3366545369013,\n                72.24787185243571\n            ],\n            [\n                -60.096121207801986,\n                -224.3366545369013,\n                72.24787185243571\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_long/2025-1-9_21-1-49_1477171087\",\n        \"gpt_instruction\": \"Proceed forward to a modern high - rise building featuring a beige color , with large balconies , and then slightly turn right to a similarly large building , characterized by a combination of gray and light beige hues , curved balconies , and distinct vertical structures on the facade .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_210153_2\",\n            \"20250109_210154_5\",\n            \"20250109_210155_8\",\n            \"20250109_210157_11\",\n            \"20250109_210158_14\",\n            \"20250109_210159_17\",\n            \"20250109_210201_20\",\n            \"20250109_210201_22\",\n            \"20250109_210202_23\",\n            \"20250109_210203_26\",\n            \"20250109_210204_29\",\n            \"20250109_210206_32\",\n            \"20250109_210207_35\",\n            \"20250109_210207_36\"\n        ],\n        \"pos\": [\n            [\n                205.20855642640902,\n                -273.2854712940326,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -264.2854712940326,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -255.28547129403262,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -246.28547129403262,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -237.28547129403262,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -228.28547129403262,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -219.28547129403262,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -213.28547129403262,\n                91.80529431779377\n            ],\n            [\n                205.20855642640902,\n                -210.28547129403262,\n                91.80529431779377\n            ],\n            [\n                208.20855642640902,\n                -205.089318871326,\n                91.80529431779377\n            ],\n            [\n                212.70855642640902,\n                -197.29509023726604,\n                91.80529431779377\n            ],\n            [\n                217.20855642640902,\n                -189.5008616032061,\n                91.80529431779377\n            ],\n            [\n                221.70855642640902,\n                -181.70663296914614,\n                91.80529431779377\n            ],\n            [\n                223.20855642640902,\n                -179.10855675779283,\n                91.80529431779377\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_average/2025-1-9_21-59-30_1069755936\",\n        \"gpt_instruction\": \"Proceed directly towards the large gray skyscraper , which features a rectangular design with protruding rooftop structures . Then slightly turn left and move forward to reach another large building . This one is tall , rectangular , and includes ventilation structures on the roof . After that , slightly turn right and go ahead to a tall , multi - story commercial building distinguished by its unique curved architectural element on the rooftop .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            1,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_215934_2\",\n            \"20250109_215935_5\",\n            \"20250109_215936_8\",\n            \"20250109_215938_11\",\n            \"20250109_215939_14\",\n            \"20250109_215940_17\",\n            \"20250109_215941_18\",\n            \"20250109_215941_19\",\n            \"20250109_215943_22\",\n            \"20250109_215944_25\",\n            \"20250109_215944_26\",\n            \"20250109_215945_27\",\n            \"20250109_215946_30\",\n            \"20250109_215947_31\"\n        ],\n        \"pos\": [\n            [\n                116.29459978851251,\n                -286.5126939626586,\n                100.009458039131\n            ],\n            [\n                111.79459978851251,\n                -278.71846532859865,\n                100.009458039131\n            ],\n            [\n                107.29459978851251,\n                -270.9242366945387,\n                100.009458039131\n            ],\n            [\n                102.79459978851251,\n                -263.13000806047876,\n                100.009458039131\n            ],\n            [\n                98.29459978851251,\n                -255.3357794264188,\n                100.009458039131\n            ],\n            [\n                93.79459978851251,\n                -247.54155079235886,\n                100.009458039131\n            ],\n            [\n                92.29459978851251,\n                -244.94347458100555,\n                100.009458039131\n            ],\n            [\n                90.79459978851251,\n                -242.34539836965223,\n                100.009458039131\n            ],\n            [\n                85.59844736580588,\n                -239.34539836965223,\n                100.009458039131\n            ],\n            [\n                77.80421873174593,\n                -234.84539836965223,\n                100.009458039131\n            ],\n            [\n                75.20614252039262,\n                -233.34539836965223,\n                100.009458039131\n            ],\n            [\n                72.6080663090393,\n                -231.84539836965223,\n                100.009458039131\n            ],\n            [\n                69.6080663090393,\n                -226.6492459469456,\n                100.009458039131\n            ],\n            [\n                68.1080663090393,\n                -224.05116973559228,\n                100.009458039131\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/low_long/2025-1-2_18-12-21_200747796\",\n        \"gpt_instruction\": \"Walk straight toward the gray building featuring large rectangular windows , then shift right to the similar gray building with a solid wall characterized by horizontal lines and small windows at the bottom . Slightly proceed straight and turn left , moving forward to it . Slightly turn right , head straight to it, then slightly turn right and walk straight toward it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            3,\n            1,\n            2,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_181224_2\",\n            \"20250102_181226_5\",\n            \"20250102_181227_7\",\n            \"20250102_181227_8\",\n            \"20250102_181228_9\",\n            \"20250102_181228_10\",\n            \"20250102_181229_11\",\n            \"20250102_181230_14\",\n            \"20250102_181231_16\",\n            \"20250102_181231_17\",\n            \"20250102_181232_20\",\n            \"20250102_181234_23\",\n            \"20250102_181235_26\",\n            \"20250102_181237_29\",\n            \"20250102_181238_32\",\n            \"20250102_181239_35\",\n            \"20250102_181240_38\",\n            \"20250102_181242_41\",\n            \"20250102_181243_44\",\n            \"20250102_181244_45\",\n            \"20250102_181245_48\",\n            \"20250102_181246_51\",\n            \"20250102_181248_54\",\n            \"20250102_181248_55\"\n        ],\n        \"pos\": [\n            [\n                240.03071786273568,\n                -28.89187652232613,\n                30.75977303304049\n            ],\n            [\n                235.53071786273568,\n                -21.09764788826618,\n                30.75977303304049\n            ],\n            [\n                232.53071786273568,\n                -15.901495465559549,\n                30.75977303304049\n            ],\n            [\n                231.03071786273568,\n                -13.303419254206233,\n                30.75977303304049\n            ],\n            [\n                231.03071786273568,\n                -13.303419254206233,\n                30.75977303304049\n            ],\n            [\n                231.03071786273568,\n                -13.303419254206233,\n                30.75977303304049\n            ],\n            [\n                232.53071786273568,\n                -10.705343042852917,\n                30.75977303304049\n            ],\n            [\n                232.53071786273568,\n                -4.705343042852917,\n                30.75977303304049\n            ],\n            [\n                232.53071786273568,\n                1.2946569571470832,\n                30.75977303304049\n            ],\n            [\n                232.53071786273568,\n                4.294656957147083,\n                30.75977303304049\n            ],\n            [\n                235.53071786273568,\n                9.490809379853715,\n                30.75977303304049\n            ],\n            [\n                240.03071786273568,\n                17.285038013913663,\n                30.75977303304049\n            ],\n            [\n                244.53071786273568,\n                25.07926664797361,\n                30.75977303304049\n            ],\n            [\n                249.03071786273568,\n                32.87349528203356,\n                30.75977303304049\n            ],\n            [\n                253.53071786273568,\n                40.66772391609351,\n                30.75977303304049\n            ],\n            [\n                258.0307178627357,\n                48.461952550153455,\n                30.75977303304049\n            ],\n            [\n                262.5307178627357,\n                56.2561811842134,\n                30.75977303304049\n            ],\n            [\n                267.0307178627357,\n                64.05040981827335,\n                30.75977303304049\n            ],\n            [\n                271.5307178627357,\n                71.8446384523333,\n                30.75977303304049\n            ],\n            [\n                273.0307178627357,\n                74.44271466368662,\n                30.75977303304049\n            ],\n            [\n                278.2268702854423,\n                77.44271466368662,\n                30.75977303304049\n            ],\n            [\n                286.02109891950226,\n                81.94271466368662,\n                30.75977303304049\n            ],\n            [\n                293.8153275535622,\n                86.44271466368662,\n                30.75977303304049\n            ],\n            [\n                296.41340376491553,\n                87.94271466368662,\n                30.75977303304049\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.0471975511965976,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_short/2025-1-9_11-31-13_1911759956\",\n        \"gpt_instruction\": \"Proceed straight towards a large grey building with multiple identical windows , then make a right turn at a large grey skyscraper distinguished by its tall structure and a blue construction crane . Continue straight until you reach another large skyscraper , characterized by its gray color and curved side with vertical window slits . Slightly turn left and move forward to a medium - sized light gray building with darker accents and a curved facade , featuring rounded corners and multiple small protrusions on each floor , which is shorter compared to the buildings directly surrounding it and serves as a residential or office space .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            3,\n            9,\n            9,\n            1,\n            2,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_113117_2\",\n            \"20250109_113118_5\",\n            \"20250109_113119_8\",\n            \"20250109_113120_11\",\n            \"20250109_113121_14\",\n            \"20250109_113122_15\",\n            \"20250109_113122_16\",\n            \"20250109_113124_19\",\n            \"20250109_113125_22\",\n            \"20250109_113125_23\",\n            \"20250109_113126_24\",\n            \"20250109_113127_27\",\n            \"20250109_113127_28\",\n            \"20250109_113128_29\"\n        ],\n        \"pos\": [\n            [\n                -57.35715607013964,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -48.35715607013964,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -39.35715607013964,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -30.357156070139638,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -21.357156070139638,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -18.357156070139638,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -18.357156070139638,\n                231.4725213862233,\n                72.24787185243571\n            ],\n            [\n                -15.357156070139638,\n                226.27636896351666,\n                72.24787185243571\n            ],\n            [\n                -10.857156070139638,\n                218.4821403294567,\n                72.24787185243571\n            ],\n            [\n                -9.357156070139638,\n                215.8840641181034,\n                72.24787185243571\n            ],\n            [\n                -7.857156070139638,\n                213.28598790675008,\n                72.24787185243571\n            ],\n            [\n                -2.661003647433006,\n                210.28598790675008,\n                72.24787185243571\n            ],\n            [\n                -0.06292743607968987,\n                208.78598790675008,\n                72.24787185243571\n            ],\n            [\n                2.535148775273626,\n                207.28598790675008,\n                72.24787185243571\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_long/2025-01-19_01-22-01_765789\",\n        \"gpt_instruction\": \"Proceed left towards a large gray modern high - rise building characterized by rounded corners with horizontal lines . Then continue straightforwardly and slightly turn right towards a large yellow construction crane structure . Next , slightly turn left and walk straight towards a large red advertisement with a billboard with text . Following that , turn slightly right and move forward to a large gray residential building with multiple floors and balconies . Afterward , turn left and proceed towards a medium - sized building sign that contains the word ' PORT ' in yellow on a red background . Walk straight towards a large dark gray building with rectangular shapes with horizontal lines , then turn slightly left and proceed to a large dark gray high - rise building featuring vent - like structures and antennas on the roof . Finally , move down to a small green commercial building with vertical lines along the front facade , similar in size to adjacent buildings .\",\n        \"action\": [\n            4,\n            2,\n            2,\n            1,\n            3,\n            8,\n            2,\n            9,\n            9,\n            9,\n            8,\n            3,\n            8,\n            2,\n            2,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_012202_0\",\n            \"20250119_012203_1\",\n            \"20250119_012204_2\",\n            \"20250119_012205_3\",\n            \"20250119_012205_4\",\n            \"20250119_012207_6\",\n            \"20250119_012208_7\",\n            \"20250119_012210_10\",\n            \"20250119_012213_13\",\n            \"20250119_012215_16\",\n            \"20250119_012217_18\",\n            \"20250119_012217_19\",\n            \"20250119_012219_21\",\n            \"20250119_012220_22\",\n            \"20250119_012221_23\",\n            \"20250119_012223_26\",\n            \"20250119_012225_29\",\n            \"20250119_012228_32\",\n            \"20250119_012230_35\",\n            \"20250119_012231_37\",\n            \"20250119_012232_38\",\n            \"20250119_012234_41\",\n            \"20250119_012237_44\",\n            \"20250119_012238_45\",\n            \"20250119_012238_46\"\n        ],\n        \"pos\": [\n            [\n                -75.40280205769733,\n                37.23512528443683,\n                53.770209583431736\n            ],\n            [\n                -75.40280205769733,\n                37.23512528443683,\n                56.770209583431736\n            ],\n            [\n                -75.40280205769733,\n                37.23512528443683,\n                56.770209583431736\n            ],\n            [\n                -75.40280205769733,\n                37.23512528443683,\n                56.770209583431736\n            ],\n            [\n                -73.90280205769733,\n                39.83320149579015,\n                56.770209583431736\n            ],\n            [\n                -71.30472584634401,\n                41.33320149579015,\n                56.770209583431736\n            ],\n            [\n                -68.7066496349907,\n                42.83320149579015,\n                56.770209583431736\n            ],\n            [\n                -65.7066496349907,\n                48.02935391849678,\n                56.770209583431736\n            ],\n            [\n                -61.2066496349907,\n                55.82358255255673,\n                56.770209583431736\n            ],\n            [\n                -56.7066496349907,\n                63.61781118661668,\n                56.770209583431736\n            ],\n            [\n                -53.7066496349907,\n                68.81396360932331,\n                56.770209583431736\n            ],\n            [\n                -52.2066496349907,\n                71.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -49.60857342363738,\n                72.91203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                74.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                74.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                80.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                89.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                98.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                107.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                113.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -47.010497212284065,\n                116.41203982067663,\n                56.770209583431736\n            ],\n            [\n                -50.010497212284065,\n                121.60819224338326,\n                56.770209583431736\n            ],\n            [\n                -54.510497212284065,\n                129.4024208774432,\n                56.770209583431736\n            ],\n            [\n                -56.010497212284065,\n                132.00049708879652,\n                56.770209583431736\n            ],\n            [\n                -56.010497212284065,\n                132.00049708879652,\n                53.770209583431736\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511965976,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            1.0471975511965976,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_average/2025-1-9_22-39-28_661761152\",\n        \"gpt_instruction\": \"Continue in a straight direction toward the dark blue , medium - sized rectangular structure with a flat roof and two open sections , relative to surrounding buildings , which is a rooftop building structure .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_223932_2\",\n            \"20250109_223933_5\",\n            \"20250109_223935_8\",\n            \"20250109_223936_11\",\n            \"20250109_223937_14\",\n            \"20250109_223939_17\",\n            \"20250109_223940_20\",\n            \"20250109_223941_23\",\n            \"20250109_223942_25\",\n            \"20250109_223942_26\"\n        ],\n        \"pos\": [\n            [\n                135.24067502303865,\n                -289.1627775747954,\n                100.009458039131\n            ],\n            [\n                130.74067502303865,\n                -281.3685489407355,\n                100.009458039131\n            ],\n            [\n                126.24067502303865,\n                -273.57432030667553,\n                100.009458039131\n            ],\n            [\n                121.74067502303865,\n                -265.7800916726156,\n                100.009458039131\n            ],\n            [\n                117.24067502303865,\n                -257.98586303855564,\n                100.009458039131\n            ],\n            [\n                112.74067502303865,\n                -250.1916344044957,\n                100.009458039131\n            ],\n            [\n                108.24067502303865,\n                -242.39740577043574,\n                100.009458039131\n            ],\n            [\n                103.74067502303865,\n                -234.6031771363758,\n                100.009458039131\n            ],\n            [\n                100.74067502303865,\n                -229.40702471366916,\n                100.009458039131\n            ],\n            [\n                99.24067502303865,\n                -226.80894850231584,\n                100.009458039131\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_long/2025-1-9_13-48-18_1555319301\",\n        \"gpt_instruction\": \"Starting at a large gray modern high - rise building with small ventilation - like windows and horizontal panel patterns , proceed slightly left to another large gray building characterized by a grid - like structure with protruding horizontal slabs . Then , take a right slightly toward it . Move forward to encounter another large light gray building , this one distinguished by rectangular windows and a panel - like fa\\u00e7ade . After a slight left turn , continue ahead to a large light blue - gray building with a smooth surface and panel - like divisions ; next , slightly turn left again and go directly to a medium - to - large gray structure that has rectangular windows aligned horizontally , representing a modern high - rise . Slightly turn right and head straight to a medium - to - large gray office building featuring vertical windows accompanied by air conditioning units . Finally , slightly turn right and go directly to a modern urban building with multiple rectangular windows , a flat exterior , and attached air conditioning units on its fa\\u00e7ade .\",\n        \"action\": [\n            9,\n            1,\n            2,\n            8,\n            3,\n            3,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            3,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_134822_2\",\n            \"20250109_134822_3\",\n            \"20250109_134822_4\",\n            \"20250109_134824_6\",\n            \"20250109_134824_7\",\n            \"20250109_134824_8\",\n            \"20250109_134826_11\",\n            \"20250109_134827_14\",\n            \"20250109_134828_17\",\n            \"20250109_134828_18\",\n            \"20250109_134829_19\",\n            \"20250109_134830_22\",\n            \"20250109_134831_24\",\n            \"20250109_134831_25\",\n            \"20250109_134832_28\",\n            \"20250109_134834_31\",\n            \"20250109_134835_34\",\n            \"20250109_134836_37\",\n            \"20250109_134837_40\",\n            \"20250109_134838_43\",\n            \"20250109_134840_46\",\n            \"20250109_134840_47\",\n            \"20250109_134840_48\",\n            \"20250109_134842_51\",\n            \"20250109_134842_52\",\n            \"20250109_134843_55\",\n            \"20250109_134844_58\",\n            \"20250109_134846_61\",\n            \"20250109_134846_62\",\n            \"20250109_134846_63\"\n        ],\n        \"pos\": [\n            [\n                124.10276545102958,\n                -59.63769971074649,\n                53.770209583431736\n            ],\n            [\n                122.60276545102958,\n                -62.235775922099805,\n                53.770209583431736\n            ],\n            [\n                121.10276545102958,\n                -64.83385213345312,\n                53.770209583431736\n            ],\n            [\n                121.10276545102958,\n                -67.83385213345312,\n                53.770209583431736\n            ],\n            [\n                121.10276545102958,\n                -70.83385213345312,\n                53.770209583431736\n            ],\n            [\n                121.10276545102958,\n                -70.83385213345312,\n                53.770209583431736\n            ],\n            [\n                115.90661302832295,\n                -73.83385213345312,\n                53.770209583431736\n            ],\n            [\n                108.112384394263,\n                -78.33385213345312,\n                53.770209583431736\n            ],\n            [\n                100.31815576020306,\n                -82.83385213345312,\n                53.770209583431736\n            ],\n            [\n                97.72007954884974,\n                -84.33385213345312,\n                53.770209583431736\n            ],\n            [\n                95.12200333749642,\n                -85.83385213345312,\n                53.770209583431736\n            ],\n            [\n                92.12200333749642,\n                -91.03000455615975,\n                53.770209583431736\n            ],\n            [\n                89.12200333749642,\n                -96.22615697886638,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -98.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -104.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -113.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -122.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -131.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -140.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -149.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -158.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -161.8242331902197,\n                53.770209583431736\n            ],\n            [\n                87.62200333749642,\n                -164.8242331902197,\n                53.770209583431736\n            ],\n            [\n                84.62200333749642,\n                -170.02038561292633,\n                53.770209583431736\n            ],\n            [\n                83.12200333749642,\n                -172.61846182427965,\n                53.770209583431736\n            ],\n            [\n                77.92585091478979,\n                -175.61846182427965,\n                53.770209583431736\n            ],\n            [\n                70.13162228072984,\n                -180.11846182427965,\n                53.770209583431736\n            ],\n            [\n                62.337393646669895,\n                -184.61846182427965,\n                53.770209583431736\n            ],\n            [\n                59.73931743531658,\n                -186.11846182427965,\n                53.770209583431736\n            ],\n            [\n                57.14124122396326,\n                -187.61846182427965,\n                53.770209583431736\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/low_long/2025-1-2_19-28-59_1065103348\",\n        \"gpt_instruction\": \"Walk straight toward a large gray building characterized by curved and rectangular structures with inset windows , then slightly turn left and continue walking straight to approach a tall gray modern high - rise building featuring vertical divisions with glass panes .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_192903_2\",\n            \"20250102_192904_5\",\n            \"20250102_192905_8\",\n            \"20250102_192906_10\",\n            \"20250102_192906_11\",\n            \"20250102_192908_14\",\n            \"20250102_192909_17\",\n            \"20250102_192910_20\",\n            \"20250102_192911_21\",\n            \"20250102_192911_22\"\n        ],\n        \"pos\": [\n            [\n                -111.85693364451913,\n                -245.0246443929526,\n                22.67604042075991\n            ],\n            [\n                -116.35693364451913,\n                -252.81887302701256,\n                22.67604042075991\n            ],\n            [\n                -120.85693364451913,\n                -260.6131016610725,\n                22.67604042075991\n            ],\n            [\n                -123.85693364451913,\n                -265.80925408377914,\n                22.67604042075991\n            ],\n            [\n                -125.35693364451913,\n                -268.40733029513245,\n                22.67604042075991\n            ],\n            [\n                -125.35693364451913,\n                -274.40733029513245,\n                22.67604042075991\n            ],\n            [\n                -125.35693364451913,\n                -283.40733029513245,\n                22.67604042075991\n            ],\n            [\n                -125.35693364451913,\n                -292.40733029513245,\n                22.67604042075991\n            ],\n            [\n                -125.35693364451913,\n                -295.40733029513245,\n                22.67604042075991\n            ],\n            [\n                -125.35693364451913,\n                -298.40733029513245,\n                22.67604042075991\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_short/2025-1-9_20-48-54_1330573317\",\n        \"gpt_instruction\": \"Proceed straight to a gray building featuring symmetrical roof structures with a stepped design at the center of the building , medium - sized compared to adjacent buildings .\",\n        \"action\": [\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_204858_2\",\n            \"20250109_204859_5\",\n            \"20250109_204900_6\"\n        ],\n        \"pos\": [\n            [\n                -14.698570619944604,\n                60.61890457619965,\n                103.16609352836659\n            ],\n            [\n                -5.698570619944604,\n                60.61890457619965,\n                103.16609352836659\n            ],\n            [\n                -2.6985706199446042,\n                60.61890457619965,\n                103.16609352836659\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/low_average/2025-1-2_17-38-15_1975960378\",\n        \"gpt_instruction\": \"Proceed forward to the grey , medium - sized part of a multi - story building characterized by horizontal slit - like windows aligned in a vertical stack , resembling a modern office or commercial building .\",\n        \"action\": [\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_173818_1\",\n            \"20250102_173819_2\"\n        ],\n        \"pos\": [\n            [\n                75.89069173599415,\n                157.14642059236525,\n                46.35596437085176\n            ],\n            [\n                74.39069173599415,\n                154.54834438101193,\n                46.35596437085176\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_short/2025-1-9_21-46-41_964445884\",\n        \"gpt_instruction\": \"Proceed directly to the large grey building characterized by a rooftop with antenna and ventilation structures . Then , slightly turn left and continue straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_214644_2\",\n            \"20250109_214646_5\",\n            \"20250109_214647_8\",\n            \"20250109_214647_9\",\n            \"20250109_214648_10\",\n            \"20250109_214648_12\",\n            \"20250109_214649_13\"\n        ],\n        \"pos\": [\n            [\n                -199.83989260080196,\n                -249.32581587234912,\n                103.16609352836659\n            ],\n            [\n                -195.33989260080196,\n                -257.12004450640904,\n                103.16609352836659\n            ],\n            [\n                -190.83989260080196,\n                -264.914273140469,\n                103.16609352836659\n            ],\n            [\n                -189.33989260080196,\n                -267.5123493518223,\n                103.16609352836659\n            ],\n            [\n                -187.83989260080196,\n                -270.1104255631756,\n                103.16609352836659\n            ],\n            [\n                -185.24181638944864,\n                -271.6104255631756,\n                103.16609352836659\n            ],\n            [\n                -182.64374017809533,\n                -273.1104255631756,\n                103.16609352836659\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.047197551196593,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_average/2025-1-9_21-33-58_480298490\",\n        \"gpt_instruction\": \"Proceed straight until you reach a tall , grey building with rectangular windows and a facade typical of high - rise buildings , then slightly turn left and continue straight to a medium - sized grey building with a crane beside it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_213402_2\",\n            \"20250109_213403_5\",\n            \"20250109_213404_8\",\n            \"20250109_213406_11\",\n            \"20250109_213406_12\",\n            \"20250109_213407_15\",\n            \"20250109_213409_18\",\n            \"20250109_213410_21\",\n            \"20250109_213411_24\",\n            \"20250109_213412_25\",\n            \"20250109_213412_26\"\n        ],\n        \"pos\": [\n            [\n                131.2754696713995,\n                147.62882548156853,\n                100.009458039131\n            ],\n            [\n                123.48124103733954,\n                143.12882548156853,\n                100.009458039131\n            ],\n            [\n                115.6870124032796,\n                138.62882548156853,\n                100.009458039131\n            ],\n            [\n                107.89278376921965,\n                134.12882548156853,\n                100.009458039131\n            ],\n            [\n                105.29470755786633,\n                132.62882548156853,\n                100.009458039131\n            ],\n            [\n                102.29470755786633,\n                127.4326730588619,\n                100.009458039131\n            ],\n            [\n                97.79470755786633,\n                119.63844442480195,\n                100.009458039131\n            ],\n            [\n                93.29470755786633,\n                111.844215790742,\n                100.009458039131\n            ],\n            [\n                88.79470755786633,\n                104.04998715668205,\n                100.009458039131\n            ],\n            [\n                87.29470755786633,\n                101.45191094532873,\n                100.009458039131\n            ],\n            [\n                85.79470755786633,\n                98.85383473397542,\n                100.009458039131\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/low_short/2025-1-2_17-24-34_1238433452\",\n        \"gpt_instruction\": \"Head straight towards a large , modern building characterized by a gray color , rounded corners with horizontal grooves , and then slightly turn left to advance forward towards a large gray structure with a modern concrete design , featuring rectangle - shaped indents with a small vent unit on the upper - left side and repeated patterns .\",\n        \"action\": [\n            9,\n            9,\n            2,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_172438_2\",\n            \"20250102_172439_5\",\n            \"20250102_172440_6\",\n            \"20250102_172441_9\",\n            \"20250102_172442_12\",\n            \"20250102_172442_13\",\n            \"20250102_172443_14\"\n        ],\n        \"pos\": [\n            [\n                131.15912301243722,\n                160.97280650924233,\n                20.124135639917895\n            ],\n            [\n                131.15912301243722,\n                151.97280650924233,\n                20.124135639917895\n            ],\n            [\n                131.15912301243722,\n                148.97280650924233,\n                20.124135639917895\n            ],\n            [\n                134.15912301243722,\n                143.7766540865357,\n                20.124135639917895\n            ],\n            [\n                138.65912301243722,\n                135.98242545247575,\n                20.124135639917895\n            ],\n            [\n                140.15912301243722,\n                133.38434924112244,\n                20.124135639917895\n            ],\n            [\n                141.65912301243722,\n                130.78627302976912,\n                20.124135639917895\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/low_short/2025-1-2_16-25-9_1365180540\",\n        \"gpt_instruction\": \"Proceed directly forward to the gray building characterized by rectangular windows and multiple floors , which is large in size . Then , slightly turn left and keep going straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_162513_2\",\n            \"20250102_162514_5\",\n            \"20250102_162515_8\",\n            \"20250102_162516_11\",\n            \"20250102_162517_14\",\n            \"20250102_162519_17\",\n            \"20250102_162519_18\",\n            \"20250102_162519_19\",\n            \"20250102_162521_22\",\n            \"20250102_162522_25\",\n            \"20250102_162523_27\",\n            \"20250102_162523_28\"\n        ],\n        \"pos\": [\n            [\n                134.55572631353482,\n                -236.52242225294862,\n                43.25295477209561\n            ],\n            [\n                139.05572631353482,\n                -244.31665088700856,\n                43.25295477209561\n            ],\n            [\n                143.55572631353482,\n                -252.1108795210685,\n                43.25295477209561\n            ],\n            [\n                148.05572631353482,\n                -259.90510815512846,\n                43.25295477209561\n            ],\n            [\n                152.55572631353482,\n                -267.6993367891884,\n                43.25295477209561\n            ],\n            [\n                157.05572631353482,\n                -275.49356542324836,\n                43.25295477209561\n            ],\n            [\n                158.55572631353482,\n                -278.0916416346017,\n                43.25295477209561\n            ],\n            [\n                160.05572631353482,\n                -280.689717845955,\n                43.25295477209561\n            ],\n            [\n                165.25187873624145,\n                -283.689717845955,\n                43.25295477209561\n            ],\n            [\n                173.0461073703014,\n                -288.189717845955,\n                43.25295477209561\n            ],\n            [\n                178.24225979300803,\n                -291.189717845955,\n                43.25295477209561\n            ],\n            [\n                180.84033600436135,\n                -292.689717845955,\n                43.25295477209561\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/high_short/2025-1-9_20-38-47_1937477084\",\n        \"gpt_instruction\": \"Proceed straight towards a gray rectangular structure with uniform windows , which is a medium - sized building . Then , take a left to encounter another gray building featuring curved balconies and being high - rise in size . Next , continue directly ahead to a gray building showcasing modern architectural elements and large in size . Slightly turn right and head straight again to arrive at a light gray residential building with rectangular rooftops , ventilation systems , and piping , identifiable as a medium - sized apartment block .\",\n        \"action\": [\n            9,\n            8,\n            2,\n            2,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_203850_2\",\n            \"20250109_203851_4\",\n            \"20250109_203852_5\",\n            \"20250109_203852_6\",\n            \"20250109_203853_9\",\n            \"20250109_203854_12\",\n            \"20250109_203856_15\",\n            \"20250109_203857_17\",\n            \"20250109_203857_18\",\n            \"20250109_203858_21\",\n            \"20250109_203859_24\",\n            \"20250109_203900_26\",\n            \"20250109_203901_27\"\n        ],\n        \"pos\": [\n            [\n                97.7679805821366,\n                -277.2009794726566,\n                103.16609352836659\n            ],\n            [\n                103.7679805821366,\n                -277.2009794726566,\n                103.16609352836659\n            ],\n            [\n                106.7679805821366,\n                -277.2009794726566,\n                103.16609352836659\n            ],\n            [\n                106.7679805821366,\n                -277.2009794726566,\n                103.16609352836659\n            ],\n            [\n                109.7679805821366,\n                -272.00482704995,\n                103.16609352836659\n            ],\n            [\n                114.2679805821366,\n                -264.21059841589005,\n                103.16609352836659\n            ],\n            [\n                118.7679805821366,\n                -256.4163697818301,\n                103.16609352836659\n            ],\n            [\n                121.7679805821366,\n                -251.22021735912347,\n                103.16609352836659\n            ],\n            [\n                123.2679805821366,\n                -248.62214114777015,\n                103.16609352836659\n            ],\n            [\n                128.46413300484323,\n                -245.62214114777015,\n                103.16609352836659\n            ],\n            [\n                136.25836163890318,\n                -241.12214114777015,\n                103.16609352836659\n            ],\n            [\n                141.4545140616098,\n                -238.12214114777015,\n                103.16609352836659\n            ],\n            [\n                144.05259027296313,\n                -236.62214114777015,\n                103.16609352836659\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_23/astar_data/medium_average/2025-1-9_13-5-3_387451659\",\n        \"gpt_instruction\": \"Advance forward toward a large , tall metal structure with a crane arm , characterized by its yellow color and categorized as a construction crane . Then , slightly turn left and move forward toward a medium - sized modern urban utility building with a gray color , featuring rectangular ventilation openings on its upper facade and a smooth concrete surface .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_130507_2\",\n            \"20250109_130508_5\",\n            \"20250109_130509_8\",\n            \"20250109_130511_11\",\n            \"20250109_130511_13\",\n            \"20250109_130512_14\",\n            \"20250109_130513_17\",\n            \"20250109_130514_20\",\n            \"20250109_130515_22\",\n            \"20250109_130515_23\"\n        ],\n        \"pos\": [\n            [\n                133.71731161920297,\n                136.83353075829464,\n                57.77929372836308\n            ],\n            [\n                125.92308298514303,\n                132.33353075829464,\n                57.77929372836308\n            ],\n            [\n                118.12885435108308,\n                127.83353075829464,\n                57.77929372836308\n            ],\n            [\n                110.33462571702313,\n                123.33353075829464,\n                57.77929372836308\n            ],\n            [\n                105.1384732943165,\n                120.33353075829464,\n                57.77929372836308\n            ],\n            [\n                102.54039708296318,\n                118.83353075829464,\n                57.77929372836308\n            ],\n            [\n                99.54039708296318,\n                113.63737833558801,\n                57.77929372836308\n            ],\n            [\n                95.04039708296318,\n                105.84314970152806,\n                57.77929372836308\n            ],\n            [\n                92.04039708296318,\n                100.64699727882143,\n                57.77929372836308\n            ],\n            [\n                90.54039708296318,\n                98.04892106746811,\n                57.77929372836308\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_short/2025-01-19_01-17-40_995685\",\n        \"gpt_instruction\": \"Continue moving straight until reaching a large , gray intersection with roads , then make a left turn onto a medium , grey urban street , and proceed until you encounter a medium - sized , grey paved road . Next , climb it . Turn right at a tall building with brown rectangular windows , and continue straight past a medium white / light grey building with a flat roof and modern design . Slightly descend and proceed straight past it . Finally , go downwards toward a large reddish - brown high - rise building with vertical white dividers in its rectangular windows .\",\n        \"action\": [\n            9,\n            1,\n            2,\n            2,\n            2,\n            9,\n            1,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            9,\n            9,\n            8,\n            3,\n            3,\n            9,\n            8,\n            5,\n            8,\n            2,\n            1,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_011743_2\",\n            \"20250119_011744_3\",\n            \"20250119_011745_4\",\n            \"20250119_011746_5\",\n            \"20250119_011746_6\",\n            \"20250119_011749_9\",\n            \"20250119_011750_10\",\n            \"20250119_011751_11\",\n            \"20250119_011752_12\",\n            \"20250119_011753_13\",\n            \"20250119_011754_14\",\n            \"20250119_011754_15\",\n            \"20250119_011755_16\",\n            \"20250119_011756_17\",\n            \"20250119_011757_18\",\n            \"20250119_011800_21\",\n            \"20250119_011803_24\",\n            \"20250119_011805_26\",\n            \"20250119_011805_27\",\n            \"20250119_011806_28\",\n            \"20250119_011809_31\",\n            \"20250119_011811_33\",\n            \"20250119_011812_34\",\n            \"20250119_011813_36\",\n            \"20250119_011814_37\",\n            \"20250119_011815_38\",\n            \"20250119_011816_39\",\n            \"20250119_011817_40\",\n            \"20250119_011818_41\",\n            \"20250119_011818_42\",\n            \"20250119_011819_43\",\n            \"20250119_011820_44\",\n            \"20250119_011821_45\",\n            \"20250119_011822_46\"\n        ],\n        \"pos\": [\n            [\n                -673.5704991901074,\n                -1872.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -676.5704991901074,\n                -1872.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1872.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1872.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1872.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1878.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1881.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -250.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -247.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -244.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -241.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -238.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -235.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -232.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1884.8016338782231,\n                -229.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1890.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1899.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1905.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1908.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -679.5704991901074,\n                -1908.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -684.766651612814,\n                -1911.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -689.9628040355208,\n                -1914.8016338782231,\n                -226.6683124086241\n            ],\n            [\n                -692.5608802468741,\n                -1916.3016338782231,\n                -226.6683124086241\n            ],\n            [\n                -695.1589564582275,\n                -1917.8016338782231,\n                -229.6683124086241\n            ],\n            [\n                -697.7570326695809,\n                -1919.3016338782231,\n                -229.6683124086241\n            ],\n            [\n                -697.7570326695809,\n                -1919.3016338782231,\n                -229.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -229.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -232.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -235.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -238.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -241.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -244.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -247.6683124086241\n            ],\n            [\n                -699.2570326695809,\n                -1921.8997100895765,\n                -250.6683124086241\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            4.1887902047863905,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205,\n            -2.094395102393205\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_average/2025-1-9_15-49-48_51245830\",\n        \"gpt_instruction\": \"The sequence of actions and corresponding landmarks can be summarized as follows : Advance forward to a gray skyscraper with a rectangular structure and vertical windows , make a left slightly turn toward it, move ahead to another black skyscraper characterized as a very large building , and slightly turn right before moving forward to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            2,\n            9,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_154952_2\",\n            \"20250109_154953_5\",\n            \"20250109_154954_8\",\n            \"20250109_154955_11\",\n            \"20250109_154957_14\",\n            \"20250109_154958_17\",\n            \"20250109_154959_20\",\n            \"20250109_155001_23\",\n            \"20250109_155001_24\",\n            \"20250109_155002_25\",\n            \"20250109_155003_28\",\n            \"20250109_155003_29\",\n            \"20250109_155004_31\",\n            \"20250109_155005_32\"\n        ],\n        \"pos\": [\n            [\n                744.577942238528,\n                -1236.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1245.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1254.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1263.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1272.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1281.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1290.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1299.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1302.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                744.577942238528,\n                -1302.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                749.7740946612346,\n                -1305.6105655914048,\n                -285.03212597454063\n            ],\n            [\n                752.372170872588,\n                -1307.1105655914048,\n                -285.03212597454063\n            ],\n            [\n                753.872170872588,\n                -1309.7086418027582,\n                -285.03212597454063\n            ],\n            [\n                755.372170872588,\n                -1312.3067180141115,\n                -285.03212597454063\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -1.0471975511966072,\n            -1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_long/2025-1-1_3-15-54_2033505236\",\n        \"gpt_instruction\": \"Begin by proceeding straight towards a large gray building characterized by a tall structure with a grid of windows . Upon arrival , make a slight right turn and continue straight to a large dark building with a brick pattern facade . After another slight right turn , keep heading straight to a large beige building with rectangular windows . As you reach this location , slightly turn left , go straight , then again slightly turn left to a structure with tan and gray rectangular windows in a repeating pattern , described as a large office building . Finally , make a slight right turn , move forward to a tall commercial office building , featuring large grid windows with reflective glass and a dark reflective blue and purple gradient .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            2,\n            1,\n            2,\n            9,\n            9,\n            8,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_031557_2\",\n            \"20250101_031559_5\",\n            \"20250101_031601_8\",\n            \"20250101_031602_11\",\n            \"20250101_031604_14\",\n            \"20250101_031605_17\",\n            \"20250101_031607_20\",\n            \"20250101_031608_22\",\n            \"20250101_031608_23\",\n            \"20250101_031610_26\",\n            \"20250101_031612_29\",\n            \"20250101_031613_32\",\n            \"20250101_031614_35\",\n            \"20250101_031615_36\",\n            \"20250101_031616_39\",\n            \"20250101_031617_40\",\n            \"20250101_031617_41\",\n            \"20250101_031618_42\",\n            \"20250101_031619_45\",\n            \"20250101_031620_48\",\n            \"20250101_031621_50\",\n            \"20250101_031622_51\",\n            \"20250101_031623_53\",\n            \"20250101_031623_54\"\n        ],\n        \"pos\": [\n            [\n                -69.97608580136271,\n                -1440.5269448109639,\n                -302.41634213538435\n            ],\n            [\n                -74.47608580136271,\n                -1448.321173445024,\n                -302.41634213538435\n            ],\n            [\n                -78.97608580136271,\n                -1456.115402079084,\n                -302.41634213538435\n            ],\n            [\n                -83.47608580136271,\n                -1463.9096307131442,\n                -302.41634213538435\n            ],\n            [\n                -87.97608580136271,\n                -1471.7038593472043,\n                -302.41634213538435\n            ],\n            [\n                -92.47608580136271,\n                -1479.4980879812645,\n                -302.41634213538435\n            ],\n            [\n                -96.97608580136271,\n                -1487.2923166153246,\n                -302.41634213538435\n            ],\n            [\n                -99.97608580136271,\n                -1492.4884690380313,\n                -302.41634213538435\n            ],\n            [\n                -101.47608580136271,\n                -1495.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -106.67223822406935,\n                -1498.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -114.4664668581293,\n                -1502.5865452493847,\n                -302.41634213538435\n            ],\n            [\n                -122.26069549218924,\n                -1507.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -130.0549241262492,\n                -1511.5865452493847,\n                -302.41634213538435\n            ],\n            [\n                -132.65300033760252,\n                -1513.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -138.65300033760252,\n                -1513.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -141.65300033760252,\n                -1513.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -141.65300033760252,\n                -1513.0865452493847,\n                -302.41634213538435\n            ],\n            [\n                -144.25107654895584,\n                -1514.5865452493847,\n                -302.41634213538435\n            ],\n            [\n                -147.25107654895584,\n                -1519.7826976720914,\n                -302.41634213538435\n            ],\n            [\n                -151.75107654895584,\n                -1527.5769263061516,\n                -302.41634213538435\n            ],\n            [\n                -154.75107654895584,\n                -1532.7730787288583,\n                -302.41634213538435\n            ],\n            [\n                -156.25107654895584,\n                -1535.3711549402117,\n                -302.41634213538435\n            ],\n            [\n                -158.84915276030915,\n                -1536.8711549402117,\n                -302.41634213538435\n            ],\n            [\n                -161.44722897166247,\n                -1538.3711549402117,\n                -302.41634213538435\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914966,\n            -2.6179938779914944,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            -2.6179938779914944,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_average/2025-1-10_4-40-58_1209379174\",\n        \"gpt_instruction\": \"Continue your path towards the light beige skyscraper , which is very tall with numerous windows and is large in size . Once there , make a slight right turn and proceed straight towards the medium - sized dark brown building characterized by its rounded corners and horizontal lines .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_044102_2\",\n            \"20250110_044103_5\",\n            \"20250110_044105_8\",\n            \"20250110_044106_11\",\n            \"20250110_044107_14\",\n            \"20250110_044109_17\",\n            \"20250110_044110_20\",\n            \"20250110_044110_21\",\n            \"20250110_044111_23\",\n            \"20250110_044112_24\"\n        ],\n        \"pos\": [\n            [\n                722.4739020523768,\n                -1412.6042775260817,\n                -211.9525511843355\n            ],\n            [\n                714.6796734183167,\n                -1408.1042775260817,\n                -211.9525511843355\n            ],\n            [\n                706.8854447842566,\n                -1403.6042775260817,\n                -211.9525511843355\n            ],\n            [\n                699.0912161501965,\n                -1399.1042775260817,\n                -211.9525511843355\n            ],\n            [\n                691.2969875161364,\n                -1394.6042775260817,\n                -211.9525511843355\n            ],\n            [\n                683.5027588820763,\n                -1390.1042775260817,\n                -211.9525511843355\n            ],\n            [\n                675.7085302480161,\n                -1385.6042775260817,\n                -211.9525511843355\n            ],\n            [\n                673.1104540366628,\n                -1384.1042775260817,\n                -211.9525511843355\n            ],\n            [\n                671.6104540366628,\n                -1381.5062013147283,\n                -211.9525511843355\n            ],\n            [\n                670.1104540366628,\n                -1378.908125103375,\n                -211.9525511843355\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_long/2025-1-9_21-28-29_221558440\",\n        \"gpt_instruction\": \"Proceed straight towards the tall brown and beige skyscraper with a grid pattern of windows , then slightly turn left and move ahead to the tall building characterized by gray vertical rectangular paneling with dark window sections . Veer left towards the beige tall skyscraper featuring vertical lines , keep going straight to the large white building with many windows arranged in a grid pattern , and slightly turn right , advancing forward to it .\",\n        \"action\": [\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_212833_2\",\n            \"20250109_212834_5\",\n            \"20250109_212834_6\",\n            \"20250109_212836_9\",\n            \"20250109_212837_12\",\n            \"20250109_212839_15\",\n            \"20250109_212840_18\",\n            \"20250109_212841_21\",\n            \"20250109_212843_24\",\n            \"20250109_212844_26\",\n            \"20250109_212844_27\",\n            \"20250109_212845_28\",\n            \"20250109_212846_31\",\n            \"20250109_212847_34\",\n            \"20250109_212849_37\",\n            \"20250109_212850_40\",\n            \"20250109_212852_43\",\n            \"20250109_212853_44\",\n            \"20250109_212853_45\",\n            \"20250109_212854_48\",\n            \"20250109_212856_51\",\n            \"20250109_212858_54\",\n            \"20250109_212858_55\",\n            \"20250109_212858_56\"\n        ],\n        \"pos\": [\n            [\n                382.9406134023691,\n                -1311.046019159557,\n                -207.99076791761004\n            ],\n            [\n                375.14638476830913,\n                -1306.546019159557,\n                -207.99076791761004\n            ],\n            [\n                372.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                366.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                357.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                348.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                339.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                330.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                321.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                315.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                312.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                312.5483085569558,\n                -1305.046019159557,\n                -207.99076791761004\n            ],\n            [\n                309.5483085569558,\n                -1310.2421715822638,\n                -207.99076791761004\n            ],\n            [\n                305.0483085569558,\n                -1318.036400216324,\n                -207.99076791761004\n            ],\n            [\n                300.5483085569558,\n                -1325.830628850384,\n                -207.99076791761004\n            ],\n            [\n                296.0483085569558,\n                -1333.6248574844442,\n                -207.99076791761004\n            ],\n            [\n                291.5483085569558,\n                -1341.4190861185043,\n                -207.99076791761004\n            ],\n            [\n                290.0483085569558,\n                -1344.0171623298577,\n                -207.99076791761004\n            ],\n            [\n                288.5483085569558,\n                -1346.615238541211,\n                -207.99076791761004\n            ],\n            [\n                283.3521561342492,\n                -1349.615238541211,\n                -207.99076791761004\n            ],\n            [\n                275.55792750018924,\n                -1354.115238541211,\n                -207.99076791761004\n            ],\n            [\n                267.7636988661293,\n                -1358.615238541211,\n                -207.99076791761004\n            ],\n            [\n                265.165622654776,\n                -1360.115238541211,\n                -207.99076791761004\n            ],\n            [\n                262.56754644342266,\n                -1361.615238541211,\n                -207.99076791761004\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_long/2025-1-1_0-35-3_1253207672\",\n        \"gpt_instruction\": \"Walk straight toward a medium - sized modern building with a brown color , large glass windows , and contemporary features ; then slightly turn right and head toward large high - rise buildings characterized by a brown and glassy appearance , tall and rectangular forms , and reflective windows . Afterward , turn to the left toward a tall brown structure with a rectangular shape and windows . Finally , proceed straight toward a significantly tall skyscraper with a red - brown uniform fa\\u00e7ade , towering over nearby buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_003506_2\",\n            \"20250101_003508_5\",\n            \"20250101_003510_8\",\n            \"20250101_003511_11\",\n            \"20250101_003513_14\",\n            \"20250101_003514_17\",\n            \"20250101_003516_20\",\n            \"20250101_003517_23\",\n            \"20250101_003519_26\",\n            \"20250101_003519_27\",\n            \"20250101_003521_30\",\n            \"20250101_003522_33\",\n            \"20250101_003524_36\",\n            \"20250101_003525_39\",\n            \"20250101_003526_41\",\n            \"20250101_003527_42\",\n            \"20250101_003527_43\",\n            \"20250101_003529_46\",\n            \"20250101_003530_48\",\n            \"20250101_003530_49\"\n        ],\n        \"pos\": [\n            [\n                1309.311343061383,\n                -1986.220062720844,\n                -300.0244985461348\n            ],\n            [\n                1301.517114427323,\n                -1990.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1293.7228857932628,\n                -1995.220062720844,\n                -300.0244985461348\n            ],\n            [\n                1285.9286571592027,\n                -1999.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1278.1344285251425,\n                -2004.220062720844,\n                -300.0244985461348\n            ],\n            [\n                1270.3401998910824,\n                -2008.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1262.5459712570223,\n                -2013.220062720844,\n                -300.0244985461348\n            ],\n            [\n                1254.7517426229622,\n                -2017.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1246.957513988902,\n                -2022.220062720844,\n                -300.0244985461348\n            ],\n            [\n                1244.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1238.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1229.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1220.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1211.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1205.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1202.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1202.3594377775487,\n                -2023.720062720844,\n                -300.0244985461348\n            ],\n            [\n                1199.3594377775487,\n                -2028.9162151435507,\n                -300.0244985461348\n            ],\n            [\n                1196.3594377775487,\n                -2034.1123675662575,\n                -300.0244985461348\n            ],\n            [\n                1194.8594377775487,\n                -2036.7104437776109,\n                -300.0244985461348\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_short/2025-1-1_13-15-10_1703964683\",\n        \"gpt_instruction\": \"Proceed forward to a large brick building characterized by red - brown color and rectangular windows with black frames . Then , take a left towards a medium - sized commercial building , distinguished by its gray color , rectangular shape , multiple floors , and a flat roof . Continue straight to encounter it . Slightly turn right and advance forward to a large building , notable for its gray color and rectangular windows . Next , veer right towards a building featuring beige color and red vertical accents of moderate size . Move forward again to it . Finally , slightly turn left and go directly ahead to reach a medium - sized commercial building with light gray color and rectangular windows with black frames .\",\n        \"action\": [\n            8,\n            2,\n            2,\n            8,\n            3,\n            9,\n            9,\n            9,\n            3,\n            3,\n            9,\n            1,\n            2,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_131513_1\",\n            \"20250101_131513_2\",\n            \"20250101_131514_3\",\n            \"20250101_131515_5\",\n            \"20250101_131515_6\",\n            \"20250101_131516_9\",\n            \"20250101_131518_12\",\n            \"20250101_131519_15\",\n            \"20250101_131520_16\",\n            \"20250101_131520_17\",\n            \"20250101_131521_20\",\n            \"20250101_131522_21\",\n            \"20250101_131522_22\",\n            \"20250101_131524_25\",\n            \"20250101_131524_26\",\n            \"20250101_131525_27\"\n        ],\n        \"pos\": [\n            [\n                906.5179775305767,\n                -1271.298065933894,\n                -299.57256856819856\n            ],\n            [\n                903.9199013192233,\n                -1269.798065933894,\n                -299.57256856819856\n            ],\n            [\n                903.9199013192233,\n                -1269.798065933894,\n                -299.57256856819856\n            ],\n            [\n                901.3218251078699,\n                -1271.298065933894,\n                -299.57256856819856\n            ],\n            [\n                898.7237488965166,\n                -1272.798065933894,\n                -299.57256856819856\n            ],\n            [\n                892.7237488965166,\n                -1272.798065933894,\n                -299.57256856819856\n            ],\n            [\n                883.7237488965166,\n                -1272.798065933894,\n                -299.57256856819856\n            ],\n            [\n                874.7237488965166,\n                -1272.798065933894,\n                -299.57256856819856\n            ],\n            [\n                871.7237488965166,\n                -1272.798065933894,\n                -299.57256856819856\n            ],\n            [\n                871.7237488965166,\n                -1272.798065933894,\n                -299.57256856819856\n            ],\n            [\n                868.7237488965166,\n                -1267.6019135111871,\n                -299.57256856819856\n            ],\n            [\n                867.2237488965166,\n                -1265.0038372998338,\n                -299.57256856819856\n            ],\n            [\n                865.7237488965166,\n                -1262.4057610884804,\n                -299.57256856819856\n            ],\n            [\n                860.5275964738098,\n                -1259.4057610884804,\n                -299.57256856819856\n            ],\n            [\n                857.9295202624564,\n                -1257.9057610884804,\n                -299.57256856819856\n            ],\n            [\n                855.3314440511031,\n                -1256.4057610884804,\n                -299.57256856819856\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            3.1415926535898024,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_long_updown/2025-1-14_17-17-27_425245975\",\n        \"gpt_instruction\": \"Proceed upwards to the tall , gray skyscraper with rectangular windows . Then , head straight to it . Slightly turn right and proceed straight to it . Next , make a left turn towards a large skyscraper with dark blue and gray glass windows arranged in a grid pattern . Keep going straight towards it . Finally , slightly stop and drop by the light beige modern commercial or office building , recognized by its textured concrete panels with circular indents in a grid pattern , and large flat walls covering multiple stories .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            2,\n            9,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_171728_0\",\n            \"20250114_171731_1\",\n            \"20250114_171731_2\",\n            \"20250114_171731_3\",\n            \"20250114_171732_4\",\n            \"20250114_171732_5\",\n            \"20250114_171733_6\",\n            \"20250114_171733_7\",\n            \"20250114_171733_8\",\n            \"20250114_171734_9\",\n            \"20250114_171734_10\",\n            \"20250114_171735_11\",\n            \"20250114_171735_12\",\n            \"20250114_171736_13\",\n            \"20250114_171736_14\",\n            \"20250114_171736_15\",\n            \"20250114_171737_16\",\n            \"20250114_171738_19\",\n            \"20250114_171739_22\",\n            \"20250114_171740_25\",\n            \"20250114_171741_27\",\n            \"20250114_171742_28\",\n            \"20250114_171743_31\",\n            \"20250114_171745_34\",\n            \"20250114_171746_37\",\n            \"20250114_171747_40\",\n            \"20250114_171749_43\",\n            \"20250114_171750_46\",\n            \"20250114_171752_49\",\n            \"20250114_171753_52\",\n            \"20250114_171754_55\",\n            \"20250114_171756_58\",\n            \"20250114_171756_59\",\n            \"20250114_171757_60\",\n            \"20250114_171757_61\",\n            \"20250114_171758_64\",\n            \"20250114_171800_67\",\n            \"20250114_171800_68\",\n            \"20250114_171801_69\",\n            \"20250114_171801_70\",\n            \"20250114_171801_71\",\n            \"20250114_171802_72\",\n            \"20250114_171802_73\",\n            \"20250114_171803_74\",\n            \"20250114_171803_75\",\n            \"20250114_171804_76\",\n            \"20250114_171804_77\",\n            \"20250114_171804_78\",\n            \"20250114_171805_79\",\n            \"20250114_171805_80\",\n            \"20250114_171806_81\",\n            \"20250114_171806_82\",\n            \"20250114_171806_83\",\n            \"20250114_171807_84\",\n            \"20250114_171807_85\"\n        ],\n        \"pos\": [\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -319.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -316.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -313.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -310.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -307.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -304.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -301.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -298.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -295.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -292.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -289.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -286.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -283.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -280.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -277.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -274.74235308666925\n            ],\n            [\n                279.7121966823633,\n                -1471.0263753713662,\n                -271.74235308666925\n            ],\n            [\n                276.7121966823633,\n                -1465.8302229486594,\n                -268.74235308666925\n            ],\n            [\n                272.2121966823633,\n                -1458.0359943145993,\n                -268.74235308666925\n            ],\n            [\n                267.7121966823633,\n                -1450.2417656805392,\n                -268.74235308666925\n            ],\n            [\n                264.7121966823633,\n                -1445.0456132578324,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1442.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1436.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1427.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1418.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1409.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1400.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1391.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1382.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1373.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1364.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1355.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1352.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1349.447537046479,\n                -268.74235308666925\n            ],\n            [\n                263.2121966823633,\n                -1349.447537046479,\n                -268.74235308666925\n            ],\n            [\n                258.01604425965667,\n                -1346.447537046479,\n                -268.74235308666925\n            ],\n            [\n                250.22181562559672,\n                -1341.947537046479,\n                -268.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -268.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -271.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -274.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -277.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -280.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -283.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -286.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -289.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -292.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -295.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -298.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -301.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -304.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -307.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -310.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -313.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -316.74235308666925\n            ],\n            [\n                247.6237394142434,\n                -1340.447537046479,\n                -319.74235308666925\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_long/2025-1-10_6-13-35_28264029\",\n        \"gpt_instruction\": \"Move forward to a light grey rectangular building with columns of medium size , slightly turn right and advance towards a blue and beige skyscraper characterized by large rectangular windows , then proceed directly ahead to a light beige building with a grid - like rooftop pattern .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_061339_2\",\n            \"20250110_061340_5\",\n            \"20250110_061342_8\",\n            \"20250110_061342_9\",\n            \"20250110_061343_12\",\n            \"20250110_061345_15\",\n            \"20250110_061346_18\",\n            \"20250110_061347_21\",\n            \"20250110_061349_24\",\n            \"20250110_061350_27\",\n            \"20250110_061351_30\",\n            \"20250110_061353_33\",\n            \"20250110_061354_36\",\n            \"20250110_061355_39\",\n            \"20250110_061357_42\",\n            \"20250110_061358_45\",\n            \"20250110_061358_46\",\n            \"20250110_061400_49\",\n            \"20250110_061401_52\",\n            \"20250110_061402_55\",\n            \"20250110_061403_56\",\n            \"20250110_061403_57\"\n        ],\n        \"pos\": [\n            [\n                232.06937441991988,\n                -1490.6996337797016,\n                -207.99076791761004\n            ],\n            [\n                227.56937441991988,\n                -1482.9054051456415,\n                -207.99076791761004\n            ],\n            [\n                223.06937441991988,\n                -1475.1111765115813,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1472.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1466.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1457.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1448.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1439.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1430.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1421.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1412.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1403.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1394.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1385.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1376.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1367.513100300228,\n                -207.99076791761004\n            ],\n            [\n                221.56937441991988,\n                -1364.513100300228,\n                -207.99076791761004\n            ],\n            [\n                224.56937441991988,\n                -1359.3169478775212,\n                -207.99076791761004\n            ],\n            [\n                229.06937441991988,\n                -1351.522719243461,\n                -207.99076791761004\n            ],\n            [\n                233.56937441991988,\n                -1343.728490609401,\n                -207.99076791761004\n            ],\n            [\n                235.06937441991988,\n                -1341.1304143980476,\n                -207.99076791761004\n            ],\n            [\n                236.56937441991988,\n                -1338.5323381866942,\n                -207.99076791761004\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_average/2025-1-9_13-4-41_1143408282\",\n        \"gpt_instruction\": \"Proceed straight towards the brown office building , characterized by its medium size and multiple stories . It has rectangular windows with a dark tint that are evenly spaced .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_130445_2\",\n            \"20250109_130446_5\",\n            \"20250109_130447_8\",\n            \"20250109_130449_11\",\n            \"20250109_130450_14\",\n            \"20250109_130452_17\",\n            \"20250109_130453_20\",\n            \"20250109_130455_23\",\n            \"20250109_130456_26\",\n            \"20250109_130457_29\",\n            \"20250109_130458_31\",\n            \"20250109_130459_32\"\n        ],\n        \"pos\": [\n            [\n                -1328.5008468181854,\n                -2029.1665023957105,\n                -285.03212597454063\n            ],\n            [\n                -1320.7066181841253,\n                -2033.6665023957105,\n                -285.03212597454063\n            ],\n            [\n                -1312.9123895500652,\n                -2038.1665023957105,\n                -285.03212597454063\n            ],\n            [\n                -1305.118160916005,\n                -2042.6665023957105,\n                -285.03212597454063\n            ],\n            [\n                -1297.323932281945,\n                -2047.1665023957105,\n                -285.03212597454063\n            ],\n            [\n                -1289.5297036478848,\n                -2051.6665023957103,\n                -285.03212597454063\n            ],\n            [\n                -1281.7354750138247,\n                -2056.1665023957103,\n                -285.03212597454063\n            ],\n            [\n                -1273.9412463797646,\n                -2060.6665023957103,\n                -285.03212597454063\n            ],\n            [\n                -1266.1470177457045,\n                -2065.1665023957103,\n                -285.03212597454063\n            ],\n            [\n                -1258.3527891116444,\n                -2069.6665023957103,\n                -285.03212597454063\n            ],\n            [\n                -1253.1566366889376,\n                -2072.6665023957103,\n                -285.03212597454063\n            ],\n            [\n                -1250.5585604775843,\n                -2074.1665023957103,\n                -285.03212597454063\n            ]\n        ],\n        \"yaw\": [\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_average/2025-1-10_3-47-10_1549495354\",\n        \"gpt_instruction\": \"Move towards the orange - beige building with rectangular windows and a tall structure , then slightly turn right to advance towards the brown and cream high - rise building , which also features rectangular windows and a rooftop with a marble - like texture .\",\n        \"action\": [\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_034714_2\",\n            \"20250110_034714_3\",\n            \"20250110_034716_6\",\n            \"20250110_034717_9\",\n            \"20250110_034719_12\",\n            \"20250110_034720_15\",\n            \"20250110_034722_18\",\n            \"20250110_034723_21\",\n            \"20250110_034725_24\",\n            \"20250110_034726_27\",\n            \"20250110_034728_30\",\n            \"20250110_034728_31\"\n        ],\n        \"pos\": [\n            [\n                -779.0809405088174,\n                -2268.4784629471183,\n                -211.9525511843355\n            ],\n            [\n                -777.5809405088174,\n                -2265.880386735765,\n                -211.9525511843355\n            ],\n            [\n                -772.3847880861108,\n                -2262.880386735765,\n                -211.9525511843355\n            ],\n            [\n                -764.5905594520507,\n                -2258.380386735765,\n                -211.9525511843355\n            ],\n            [\n                -756.7963308179906,\n                -2253.880386735765,\n                -211.9525511843355\n            ],\n            [\n                -749.0021021839304,\n                -2249.380386735765,\n                -211.9525511843355\n            ],\n            [\n                -741.2078735498703,\n                -2244.880386735765,\n                -211.9525511843355\n            ],\n            [\n                -733.4136449158102,\n                -2240.380386735765,\n                -211.9525511843355\n            ],\n            [\n                -725.6194162817501,\n                -2235.880386735765,\n                -211.9525511843355\n            ],\n            [\n                -717.82518764769,\n                -2231.380386735765,\n                -211.9525511843355\n            ],\n            [\n                -710.0309590136299,\n                -2226.880386735765,\n                -211.9525511843355\n            ],\n            [\n                -707.4328828022765,\n                -2225.380386735765,\n                -211.9525511843355\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965694,\n            1.0471975511965694,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_long_updown/2025-01-19_05-58-21_123593\",\n        \"gpt_instruction\": \"Begin by ascending towards a mid-sized , brown building featuring rectangular windows . Move forward to it . Slightly ascend and gently turn right , then proceed straight to a large building with a cream and black facade adorned with rectangular windows and decorative features . Gradually turn left as you advance towards a large , beige building , notable for its rectilinear stone facade with inset dark windows . Finally , slightly descend and come to a stop at another large , beige building distinguished by its stone arches and archways .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            4,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            5,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250119_055823_0\",\n            \"20250119_055823_1\",\n            \"20250119_055824_2\",\n            \"20250119_055825_3\",\n            \"20250119_055826_4\",\n            \"20250119_055827_5\",\n            \"20250119_055827_6\",\n            \"20250119_055828_7\",\n            \"20250119_055829_8\",\n            \"20250119_055830_9\",\n            \"20250119_055831_10\",\n            \"20250119_055833_13\",\n            \"20250119_055836_16\",\n            \"20250119_055839_19\",\n            \"20250119_055839_20\",\n            \"20250119_055840_21\",\n            \"20250119_055843_24\",\n            \"20250119_055845_27\",\n            \"20250119_055848_30\",\n            \"20250119_055850_33\",\n            \"20250119_055853_36\",\n            \"20250119_055856_39\",\n            \"20250119_055858_42\",\n            \"20250119_055901_45\",\n            \"20250119_055902_46\",\n            \"20250119_055903_47\",\n            \"20250119_055905_50\",\n            \"20250119_055908_53\",\n            \"20250119_055911_56\",\n            \"20250119_055913_59\",\n            \"20250119_055916_62\",\n            \"20250119_055918_65\",\n            \"20250119_055919_66\",\n            \"20250119_055920_67\",\n            \"20250119_055921_68\",\n            \"20250119_055922_69\",\n            \"20250119_055922_70\",\n            \"20250119_055923_71\",\n            \"20250119_055924_72\",\n            \"20250119_055925_73\",\n            \"20250119_055926_74\",\n            \"20250119_055926_75\",\n            \"20250119_055927_76\",\n            \"20250119_055928_77\",\n            \"20250119_055929_78\"\n        ],\n        \"pos\": [\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -320.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -317.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -314.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -311.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -308.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -305.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -302.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -299.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -296.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -293.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -67.70084179373796,\n                -1418.0178193259756,\n                -290.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                -62.50468937103133,\n                -1421.0178193259756,\n                -287.37275953148753\n            ],\n            [\n                -54.710460736971385,\n                -1425.5178193259756,\n                -287.37275953148753\n            ],\n            [\n                -46.91623210291144,\n                -1430.0178193259756,\n                -287.37275953148753\n            ],\n            [\n                -44.31815589155812,\n                -1431.5178193259756,\n                -287.37275953148753\n            ],\n            [\n                -44.31815589155812,\n                -1431.5178193259756,\n                -284.37275953148753\n            ],\n            [\n                -41.31815589155812,\n                -1436.7139717486823,\n                -284.37275953148753\n            ],\n            [\n                -36.81815589155812,\n                -1444.5082003827424,\n                -284.37275953148753\n            ],\n            [\n                -32.31815589155812,\n                -1452.3024290168025,\n                -284.37275953148753\n            ],\n            [\n                -27.81815589155812,\n                -1460.0966576508627,\n                -284.37275953148753\n            ],\n            [\n                -23.31815589155812,\n                -1467.8908862849228,\n                -284.37275953148753\n            ],\n            [\n                -18.81815589155812,\n                -1475.685114918983,\n                -284.37275953148753\n            ],\n            [\n                -14.31815589155812,\n                -1483.479343553043,\n                -284.37275953148753\n            ],\n            [\n                -9.81815589155812,\n                -1491.2735721871031,\n                -284.37275953148753\n            ],\n            [\n                -8.31815589155812,\n                -1493.8716483984565,\n                -284.37275953148753\n            ],\n            [\n                -6.818155891558121,\n                -1496.4697246098099,\n                -284.37275953148753\n            ],\n            [\n                -1.6220034688514886,\n                -1499.4697246098099,\n                -284.37275953148753\n            ],\n            [\n                6.172225165208459,\n                -1503.9697246098099,\n                -284.37275953148753\n            ],\n            [\n                13.966453799268407,\n                -1508.4697246098099,\n                -284.37275953148753\n            ],\n            [\n                21.760682433328356,\n                -1512.9697246098099,\n                -284.37275953148753\n            ],\n            [\n                29.554911067388304,\n                -1517.4697246098099,\n                -284.37275953148753\n            ],\n            [\n                37.34913970144825,\n                -1521.9697246098099,\n                -284.37275953148753\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -284.37275953148753\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -287.37275953148753\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -290.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -293.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -296.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -299.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -302.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -305.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -308.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -311.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -314.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -317.37275953148753,\n                -0.5235987755982988\n            ],\n            [\n                39.94721591280157,\n                -1523.4697246098099,\n                -320.37275953148753,\n                -0.5235987755982988\n            ]\n        ],\n        \"yaw\": [\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511965976,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755983084,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_average/2025-1-9_23-3-8_430253414\",\n        \"gpt_instruction\": \"Proceed directly to a tall , rectangular , multi - storied building with light beige color and dark accents , which is notable for being the tallest feature in the area and has windows . Then , slightly turn right and move ahead to a medium - sized building characterized by a distinct red rooftop .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_230312_2\",\n            \"20250109_230313_5\",\n            \"20250109_230314_8\",\n            \"20250109_230316_11\",\n            \"20250109_230316_12\",\n            \"20250109_230318_15\",\n            \"20250109_230319_18\",\n            \"20250109_230320_21\",\n            \"20250109_230322_24\",\n            \"20250109_230323_27\",\n            \"20250109_230325_30\",\n            \"20250109_230326_33\",\n            \"20250109_230328_36\",\n            \"20250109_230329_39\",\n            \"20250109_230330_42\",\n            \"20250109_230331_43\",\n            \"20250109_230331_44\"\n        ],\n        \"pos\": [\n            [\n                515.3891590842484,\n                -1309.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                524.3891590842484,\n                -1309.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                533.3891590842484,\n                -1309.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                542.3891590842484,\n                -1309.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                545.3891590842484,\n                -1309.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                550.5853115069551,\n                -1312.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                558.3795401410152,\n                -1317.0720108265027,\n                -211.9525511843355\n            ],\n            [\n                566.1737687750754,\n                -1321.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                573.9679974091355,\n                -1326.0720108265027,\n                -211.9525511843355\n            ],\n            [\n                581.7622260431956,\n                -1330.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                589.5564546772557,\n                -1335.0720108265027,\n                -211.9525511843355\n            ],\n            [\n                597.3506833113158,\n                -1339.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                605.144911945376,\n                -1344.0720108265027,\n                -211.9525511843355\n            ],\n            [\n                612.9391405794361,\n                -1348.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                620.7333692134962,\n                -1353.0720108265027,\n                -211.9525511843355\n            ],\n            [\n                623.3314454248496,\n                -1354.5720108265027,\n                -211.9525511843355\n            ],\n            [\n                625.9295216362029,\n                -1356.0720108265027,\n                -211.9525511843355\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_average/2025-1-1_1-20-24_1541027284\",\n        \"gpt_instruction\": \"Head straight to a large beige building characterized by its rectangular structure with vertical ridged designs and minimal windows . Then , slightly turn right and walk straight toward another large beige structure , a rectangular block with a rough , textured surface and faint horizontal lines . Finally , slightly turn left and proceed straight to a large beige structure featuring a concrete fa\\u00e7ade with visible horizontal lines and a noticeable dark mark , exemplifying a modern minimalist building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_012028_2\",\n            \"20250101_012029_5\",\n            \"20250101_012031_8\",\n            \"20250101_012032_11\",\n            \"20250101_012033_14\",\n            \"20250101_012035_17\",\n            \"20250101_012035_18\",\n            \"20250101_012035_19\",\n            \"20250101_012037_22\",\n            \"20250101_012038_25\",\n            \"20250101_012039_28\",\n            \"20250101_012040_29\",\n            \"20250101_012040_30\",\n            \"20250101_012041_31\"\n        ],\n        \"pos\": [\n            [\n                691.5126648262021,\n                -1425.5232604014784,\n                -300.95422927116965\n            ],\n            [\n                696.0126648262021,\n                -1417.7290317674183,\n                -300.95422927116965\n            ],\n            [\n                700.5126648262021,\n                -1409.9348031333582,\n                -300.95422927116965\n            ],\n            [\n                705.0126648262021,\n                -1402.140574499298,\n                -300.95422927116965\n            ],\n            [\n                709.5126648262021,\n                -1394.346345865238,\n                -300.95422927116965\n            ],\n            [\n                714.0126648262021,\n                -1386.5521172311778,\n                -300.95422927116965\n            ],\n            [\n                715.5126648262021,\n                -1383.9540410198244,\n                -300.95422927116965\n            ],\n            [\n                717.0126648262021,\n                -1381.355964808471,\n                -300.95422927116965\n            ],\n            [\n                722.2088172489089,\n                -1378.355964808471,\n                -300.95422927116965\n            ],\n            [\n                730.003045882969,\n                -1373.855964808471,\n                -300.95422927116965\n            ],\n            [\n                737.7972745170291,\n                -1369.355964808471,\n                -300.95422927116965\n            ],\n            [\n                740.3953507283825,\n                -1367.855964808471,\n                -300.95422927116965\n            ],\n            [\n                740.3953507283825,\n                -1367.855964808471,\n                -300.95422927116965\n            ],\n            [\n                741.8953507283825,\n                -1365.2578885971177,\n                -300.95422927116965\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            1.047197551196588,\n            1.047197551196588\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_long/2024-12-31_22-5-49_1424268980\",\n        \"gpt_instruction\": \"Advance forward to the large light gray building with multiple windows and Gothic architectural elements , then slightly turn right and move ahead to it .\",\n        \"action\": [\n            9,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20241231_220552_2\",\n            \"20241231_220553_3\",\n            \"20241231_220554_5\",\n            \"20241231_220554_6\"\n        ],\n        \"pos\": [\n            [\n                -492.18660826210737,\n                -2588.4753083284436,\n                -309.63117557220426\n            ],\n            [\n                -490.68660826210737,\n                -2591.0733845397967,\n                -309.63117557220426\n            ],\n            [\n                -490.68660826210737,\n                -2594.0733845397967,\n                -309.63117557220426\n            ],\n            [\n                -490.68660826210737,\n                -2597.0733845397967,\n                -309.63117557220426\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_short/2025-1-1_13-24-59_1911165193\",\n        \"gpt_instruction\": \"Walk straight to the gray structure with red brick accents and dark blue windows , which features a rectangular grid - like pattern with segmented columns and windows . It is a mid-sized , multi - story commercial or office building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_132503_2\",\n            \"20250101_132504_5\",\n            \"20250101_132505_8\",\n            \"20250101_132507_11\",\n            \"20250101_132508_14\",\n            \"20250101_132508_15\",\n            \"20250101_132509_16\"\n        ],\n        \"pos\": [\n            [\n                567.3510413114386,\n                -1998.5155827375293,\n                -310.84632368610323\n            ],\n            [\n                562.8510413114386,\n                -1990.7213541034691,\n                -310.84632368610323\n            ],\n            [\n                558.3510413114386,\n                -1982.927125469409,\n                -310.84632368610323\n            ],\n            [\n                553.8510413114386,\n                -1975.132896835349,\n                -310.84632368610323\n            ],\n            [\n                549.3510413114386,\n                -1967.3386682012888,\n                -310.84632368610323\n            ],\n            [\n                547.8510413114386,\n                -1964.7405919899354,\n                -310.84632368610323\n            ],\n            [\n                546.3510413114386,\n                -1962.142515778582,\n                -310.84632368610323\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_long/2025-1-9_11-43-55_783368690\",\n        \"gpt_instruction\": \"Proceed forward to the beige high - rise building , characterized by its textured concrete facade with vertical lines and rivets , which is large in size . Slightly turn left , then keep going straight to the large beige office building with vertical windows . Slightly turn right and walk straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_114358_2\",\n            \"20250109_114400_5\",\n            \"20250109_114401_8\",\n            \"20250109_114403_11\",\n            \"20250109_114404_14\",\n            \"20250109_114405_15\",\n            \"20250109_114405_16\",\n            \"20250109_114407_19\",\n            \"20250109_114408_22\",\n            \"20250109_114409_25\",\n            \"20250109_114411_28\",\n            \"20250109_114412_31\",\n            \"20250109_114414_34\",\n            \"20250109_114414_35\",\n            \"20250109_114415_36\",\n            \"20250109_114416_39\",\n            \"20250109_114417_42\",\n            \"20250109_114419_45\",\n            \"20250109_114420_48\",\n            \"20250109_114421_50\",\n            \"20250109_114422_51\"\n        ],\n        \"pos\": [\n            [\n                389.357250695516,\n                -1827.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                381.563022061456,\n                -1823.1757045183174,\n                -269.91094099928495\n            ],\n            [\n                373.7687934273961,\n                -1818.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                365.9745647933361,\n                -1814.1757045183174,\n                -269.91094099928495\n            ],\n            [\n                358.1803361592762,\n                -1809.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                355.58225994792286,\n                -1808.1757045183174,\n                -269.91094099928495\n            ],\n            [\n                352.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                346.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                337.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                328.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                319.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                310.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                301.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                298.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                295.98418373656955,\n                -1806.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                290.7880313138629,\n                -1803.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                282.99380267980297,\n                -1799.1757045183174,\n                -269.91094099928495\n            ],\n            [\n                275.199574045743,\n                -1794.6757045183174,\n                -269.91094099928495\n            ],\n            [\n                267.4053454116831,\n                -1790.1757045183174,\n                -269.91094099928495\n            ],\n            [\n                262.20919298897644,\n                -1787.1757045183174,\n                -269.91094099928495\n            ],\n            [\n                259.6111167776231,\n                -1785.6757045183174,\n                -269.91094099928495\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_average/2024-12-31_22-41-27_116087764\",\n        \"gpt_instruction\": \"Proceed forward toward the building with red and beige colors , featuring brick exteriors and rectangular windows , which is large in size . Slightly turn left slightly and head toward it . Then , slightly turn right and move straight toward it . Finally , slightly turn right and walk straight to the modern high - rise building with light beige coloration , showcasing a tall rectangular grid pattern of windows with alternating black and beige panels .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20241231_224131_2\",\n            \"20241231_224132_5\",\n            \"20241231_224134_8\",\n            \"20241231_224135_11\",\n            \"20241231_224136_14\",\n            \"20241231_224138_17\",\n            \"20241231_224139_20\",\n            \"20241231_224140_23\",\n            \"20241231_224141_24\",\n            \"20241231_224142_27\",\n            \"20241231_224144_30\",\n            \"20241231_224145_33\",\n            \"20241231_224146_34\",\n            \"20241231_224147_37\",\n            \"20241231_224148_40\",\n            \"20241231_224150_43\",\n            \"20241231_224151_46\",\n            \"20241231_224151_47\",\n            \"20241231_224152_48\",\n            \"20241231_224153_51\",\n            \"20241231_224154_53\",\n            \"20241231_224154_54\"\n        ],\n        \"pos\": [\n            [\n                -192.51748821600358,\n                -1668.785427557565,\n                -292.28478393775026\n            ],\n            [\n                -197.01748821600358,\n                -1660.991198923505,\n                -292.28478393775026\n            ],\n            [\n                -201.51748821600358,\n                -1653.1969702894448,\n                -292.28478393775026\n            ],\n            [\n                -206.01748821600358,\n                -1645.4027416553847,\n                -292.28478393775026\n            ],\n            [\n                -210.51748821600358,\n                -1637.6085130213246,\n                -292.28478393775026\n            ],\n            [\n                -215.01748821600358,\n                -1629.8142843872645,\n                -292.28478393775026\n            ],\n            [\n                -219.51748821600358,\n                -1622.0200557532044,\n                -292.28478393775026\n            ],\n            [\n                -224.01748821600358,\n                -1614.2258271191442,\n                -292.28478393775026\n            ],\n            [\n                -225.51748821600358,\n                -1611.6277509077909,\n                -292.28478393775026\n            ],\n            [\n                -230.71364063871022,\n                -1608.6277509077909,\n                -292.28478393775026\n            ],\n            [\n                -238.50786927277017,\n                -1604.1277509077909,\n                -292.28478393775026\n            ],\n            [\n                -246.3020979068301,\n                -1599.6277509077909,\n                -292.28478393775026\n            ],\n            [\n                -248.90017411818343,\n                -1598.1277509077909,\n                -292.28478393775026\n            ],\n            [\n                -251.90017411818343,\n                -1592.9315984850841,\n                -292.28478393775026\n            ],\n            [\n                -256.40017411818343,\n                -1585.137369851024,\n                -292.28478393775026\n            ],\n            [\n                -260.90017411818343,\n                -1577.3431412169639,\n                -292.28478393775026\n            ],\n            [\n                -265.40017411818343,\n                -1569.5489125829038,\n                -292.28478393775026\n            ],\n            [\n                -266.90017411818343,\n                -1566.9508363715504,\n                -292.28478393775026\n            ],\n            [\n                -268.40017411818343,\n                -1564.352760160197,\n                -292.28478393775026\n            ],\n            [\n                -268.40017411818343,\n                -1558.352760160197,\n                -292.28478393775026\n            ],\n            [\n                -268.40017411818343,\n                -1552.352760160197,\n                -292.28478393775026\n            ],\n            [\n                -268.40017411818343,\n                -1549.352760160197,\n                -292.28478393775026\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_short/2025-1-9_14-58-14_1663080928\",\n        \"gpt_instruction\": \"Proceed forward towards the gray apartment building , which features balconies with horizontal slat railings . It is a mid-sized structure with multiple floors .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_145818_2\",\n            \"20250109_145819_5\",\n            \"20250109_145820_8\",\n            \"20250109_145822_11\",\n            \"20250109_145823_14\",\n            \"20250109_145824_17\",\n            \"20250109_145826_20\",\n            \"20250109_145826_21\"\n        ],\n        \"pos\": [\n            [\n                1027.1755680985211,\n                -1934.6623114934855,\n                -270.6537207570657\n            ],\n            [\n                1031.6755680985211,\n                -1942.4565401275456,\n                -270.6537207570657\n            ],\n            [\n                1036.1755680985211,\n                -1950.2507687616057,\n                -270.6537207570657\n            ],\n            [\n                1040.6755680985211,\n                -1958.0449973956659,\n                -270.6537207570657\n            ],\n            [\n                1045.1755680985211,\n                -1965.839226029726,\n                -270.6537207570657\n            ],\n            [\n                1049.6755680985211,\n                -1973.633454663786,\n                -270.6537207570657\n            ],\n            [\n                1054.1755680985211,\n                -1981.4276832978462,\n                -270.6537207570657\n            ],\n            [\n                1055.6755680985211,\n                -1984.0257595091996,\n                -270.6537207570657\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_long/2025-1-10_8-19-16_1989200801\",\n        \"gpt_instruction\": \"Proceed forward to the white skyscraper , characterized by tall structure and large windows ; then slightly turn right and continue straight towards the gray , medium - sized flat - roofed building . Next , slightly turn left and advance to the light brown tall skyscraper with multiple windows before slightly turning left again to move directly ahead to the dark brown building , featuring modern glazing and reflective windows in a horizontal alignment . Finally , slightly turn right and walk straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_081919_2\",\n            \"20250110_081921_5\",\n            \"20250110_081922_8\",\n            \"20250110_081923_11\",\n            \"20250110_081925_14\",\n            \"20250110_081926_17\",\n            \"20250110_081927_20\",\n            \"20250110_081928_21\",\n            \"20250110_081929_24\",\n            \"20250110_081930_27\",\n            \"20250110_081932_30\",\n            \"20250110_081932_31\",\n            \"20250110_081933_34\",\n            \"20250110_081935_37\",\n            \"20250110_081936_40\",\n            \"20250110_081937_43\",\n            \"20250110_081938_45\",\n            \"20250110_081938_46\",\n            \"20250110_081940_49\",\n            \"20250110_081941_52\",\n            \"20250110_081942_55\",\n            \"20250110_081943_56\",\n            \"20250110_081944_59\",\n            \"20250110_081944_60\"\n        ],\n        \"pos\": [\n            [\n                -1025.9768499465188,\n                -2786.659699139735,\n                -207.99076791761004\n            ],\n            [\n                -1021.4768499465188,\n                -2794.4539277737945,\n                -207.99076791761004\n            ],\n            [\n                -1016.9768499465188,\n                -2802.248156407854,\n                -207.99076791761004\n            ],\n            [\n                -1012.4768499465188,\n                -2810.0423850419133,\n                -207.99076791761004\n            ],\n            [\n                -1007.9768499465188,\n                -2817.836613675973,\n                -207.99076791761004\n            ],\n            [\n                -1003.4768499465188,\n                -2825.630842310032,\n                -207.99076791761004\n            ],\n            [\n                -998.9768499465188,\n                -2833.4250709440917,\n                -207.99076791761004\n            ],\n            [\n                -997.4768499465188,\n                -2836.023147155445,\n                -207.99076791761004\n            ],\n            [\n                -997.4768499465188,\n                -2842.023147155445,\n                -207.99076791761004\n            ],\n            [\n                -997.4768499465188,\n                -2851.023147155445,\n                -207.99076791761004\n            ],\n            [\n                -997.4768499465188,\n                -2860.023147155445,\n                -207.99076791761004\n            ],\n            [\n                -997.4768499465188,\n                -2863.023147155445,\n                -207.99076791761004\n            ],\n            [\n                -994.4768499465188,\n                -2868.219299578151,\n                -207.99076791761004\n            ],\n            [\n                -989.9768499465188,\n                -2876.0135282122105,\n                -207.99076791761004\n            ],\n            [\n                -985.4768499465188,\n                -2883.80775684627,\n                -207.99076791761004\n            ],\n            [\n                -980.9768499465188,\n                -2891.6019854803294,\n                -207.99076791761004\n            ],\n            [\n                -977.9768499465188,\n                -2896.7981379030357,\n                -207.99076791761004\n            ],\n            [\n                -976.4768499465188,\n                -2899.396214114389,\n                -207.99076791761004\n            ],\n            [\n                -971.280697523812,\n                -2902.396214114389,\n                -207.99076791761004\n            ],\n            [\n                -963.4864688897519,\n                -2906.896214114389,\n                -207.99076791761004\n            ],\n            [\n                -955.6922402556918,\n                -2911.396214114389,\n                -207.99076791761004\n            ],\n            [\n                -953.0941640443384,\n                -2912.896214114389,\n                -207.99076791761004\n            ],\n            [\n                -950.0941640443384,\n                -2918.092366537095,\n                -207.99076791761004\n            ],\n            [\n                -948.5941640443384,\n                -2920.6904427484483,\n                -207.99076791761004\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -1.0471975511965694,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -1.0471975511965694,\n            -1.0471975511965694\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_short/2025-1-9_17-28-28_1456748696\",\n        \"gpt_instruction\": \"Move ahead to a beige building characterized by multiple uniform windows with a grid - like structure and large size . Then , slightly turn right slightly to proceed to it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_172831_2\",\n            \"20250109_172833_5\",\n            \"20250109_172834_7\",\n            \"20250109_172834_8\",\n            \"20250109_172835_11\",\n            \"20250109_172836_12\"\n        ],\n        \"pos\": [\n            [\n                233.12079741078927,\n                -1616.238339936314,\n                -270.6537207570657\n            ],\n            [\n                233.12079741078927,\n                -1607.238339936314,\n                -270.6537207570657\n            ],\n            [\n                233.12079741078927,\n                -1601.238339936314,\n                -270.6537207570657\n            ],\n            [\n                233.12079741078927,\n                -1598.238339936314,\n                -270.6537207570657\n            ],\n            [\n                236.12079741078927,\n                -1593.0421875136074,\n                -270.6537207570657\n            ],\n            [\n                237.62079741078927,\n                -1590.444111302254,\n                -270.6537207570657\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511966072,\n            1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_short/2025-1-9_23-4-2_991810563\",\n        \"gpt_instruction\": \"Proceed directly towards the large , cracked wall surface on the side of a building , which is light beige in color . Then , slightly turn left and continue going straight until you reach the very tall and narrow building with an off - white color and a smooth facade featuring vertical window rows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            8,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_230405_2\",\n            \"20250109_230407_5\",\n            \"20250109_230408_8\",\n            \"20250109_230409_10\",\n            \"20250109_230410_11\",\n            \"20250109_230411_13\",\n            \"20250109_230411_14\"\n        ],\n        \"pos\": [\n            [\n                -1208.3789180875617,\n                -2348.7646740581768,\n                -250.6683124086241\n            ],\n            [\n                -1200.5846894535016,\n                -2344.2646740581768,\n                -250.6683124086241\n            ],\n            [\n                -1192.7904608194415,\n                -2339.7646740581768,\n                -250.6683124086241\n            ],\n            [\n                -1187.5943083967347,\n                -2336.7646740581768,\n                -250.6683124086241\n            ],\n            [\n                -1184.9962321853814,\n                -2335.2646740581768,\n                -250.6683124086241\n            ],\n            [\n                -1183.4962321853814,\n                -2332.6665978468236,\n                -250.6683124086241\n            ],\n            [\n                -1181.9962321853814,\n                -2330.0685216354705,\n                -250.6683124086241\n            ]\n        ],\n        \"yaw\": [\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            1.0471975511965694,\n            1.0471975511965694\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_short/2025-1-10_3-22-48_1368062958\",\n        \"gpt_instruction\": \"Move directly towards the tall , rectangular beige building with repetitive windows , then slightly turn right and continue ahead to the light beige building featuring a long vertical alignment with black , window - striped across the facade , which is tall and narrow , extending beyond the top of the image .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_032251_2\",\n            \"20250110_032252_5\",\n            \"20250110_032254_8\",\n            \"20250110_032254_9\",\n            \"20250110_032255_10\",\n            \"20250110_032256_13\",\n            \"20250110_032257_16\",\n            \"20250110_032258_17\"\n        ],\n        \"pos\": [\n            [\n                -878.891963979552,\n                -2913.1366581900343,\n                -250.6683124086241\n            ],\n            [\n                -886.6861926136121,\n                -2917.6366581900343,\n                -250.6683124086241\n            ],\n            [\n                -894.4804212476722,\n                -2922.1366581900343,\n                -250.6683124086241\n            ],\n            [\n                -897.0784974590256,\n                -2923.6366581900343,\n                -250.6683124086241\n            ],\n            [\n                -899.676573670379,\n                -2925.1366581900343,\n                -250.6683124086241\n            ],\n            [\n                -905.676573670379,\n                -2925.1366581900343,\n                -250.6683124086241\n            ],\n            [\n                -914.676573670379,\n                -2925.1366581900343,\n                -250.6683124086241\n            ],\n            [\n                -917.676573670379,\n                -2925.1366581900343,\n                -250.6683124086241\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_long/2025-1-10_9-26-10_2026929416\",\n        \"gpt_instruction\": \"Begin by walking straight towards a large grey building characterized by its tall structure and vertical lines . Then , slightly turn right slightly and proceed to it . Next , turn left to a dark building that features tall , narrow windows and vertical lines . Finally , head straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_092614_2\",\n            \"20250110_092615_5\",\n            \"20250110_092617_8\",\n            \"20250110_092618_11\",\n            \"20250110_092619_14\",\n            \"20250110_092620_17\",\n            \"20250110_092622_20\",\n            \"20250110_092622_21\",\n            \"20250110_092624_24\",\n            \"20250110_092625_27\",\n            \"20250110_092626_30\",\n            \"20250110_092628_33\",\n            \"20250110_092629_36\",\n            \"20250110_092630_39\",\n            \"20250110_092632_42\",\n            \"20250110_092633_44\",\n            \"20250110_092633_45\",\n            \"20250110_092633_46\",\n            \"20250110_092635_49\",\n            \"20250110_092636_52\",\n            \"20250110_092636_53\"\n        ],\n        \"pos\": [\n            [\n                13.667005847743688,\n                -1542.0896587752557,\n                -207.99076791761004\n            ],\n            [\n                18.167005847743688,\n                -1549.8838874093158,\n                -207.99076791761004\n            ],\n            [\n                22.667005847743688,\n                -1557.678116043376,\n                -207.99076791761004\n            ],\n            [\n                27.167005847743688,\n                -1565.472344677436,\n                -207.99076791761004\n            ],\n            [\n                31.667005847743688,\n                -1573.2665733114961,\n                -207.99076791761004\n            ],\n            [\n                36.16700584774369,\n                -1581.0608019455562,\n                -207.99076791761004\n            ],\n            [\n                40.66700584774369,\n                -1588.8550305796164,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1591.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1597.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1606.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1615.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1624.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1633.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1642.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1651.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1657.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1660.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                42.16700584774369,\n                -1660.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                47.36315827045032,\n                -1663.4531067909697,\n                -207.99076791761004\n            ],\n            [\n                55.15738690451027,\n                -1667.9531067909697,\n                -207.99076791761004\n            ],\n            [\n                57.755463115863584,\n                -1669.4531067909697,\n                -207.99076791761004\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_long/2025-1-10_1-52-41_586235379\",\n        \"gpt_instruction\": \"Head straight to a very large gray skyscraper with a tall and smooth surface , then slightly turn left and keep going straight towards a large beige building characterized by its tall structure , vertical windows , and rectangular design . Finally , slightly turn right and move forward to reach a large light gray skyscraper , notable for its tall rectangular shape and multiple rows of evenly spaced black windows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_015245_2\",\n            \"20250110_015246_5\",\n            \"20250110_015248_8\",\n            \"20250110_015248_9\",\n            \"20250110_015250_12\",\n            \"20250110_015251_15\",\n            \"20250110_015253_18\",\n            \"20250110_015254_21\",\n            \"20250110_015256_24\",\n            \"20250110_015256_25\",\n            \"20250110_015258_28\",\n            \"20250110_015259_31\",\n            \"20250110_015301_34\",\n            \"20250110_015302_37\",\n            \"20250110_015304_40\",\n            \"20250110_015305_43\",\n            \"20250110_015307_46\",\n            \"20250110_015308_49\",\n            \"20250110_015309_51\",\n            \"20250110_015310_52\"\n        ],\n        \"pos\": [\n            [\n                -54.17952586840664,\n                -1454.0978572669144,\n                -207.99076791761004\n            ],\n            [\n                -61.97375450246659,\n                -1458.5978572669144,\n                -207.99076791761004\n            ],\n            [\n                -69.76798313652654,\n                -1463.0978572669144,\n                -207.99076791761004\n            ],\n            [\n                -72.36605934787985,\n                -1464.5978572669144,\n                -207.99076791761004\n            ],\n            [\n                -75.36605934787985,\n                -1469.7940096896211,\n                -207.99076791761004\n            ],\n            [\n                -79.86605934787985,\n                -1477.5882383236813,\n                -207.99076791761004\n            ],\n            [\n                -84.36605934787985,\n                -1485.3824669577414,\n                -207.99076791761004\n            ],\n            [\n                -88.86605934787985,\n                -1493.1766955918015,\n                -207.99076791761004\n            ],\n            [\n                -93.36605934787985,\n                -1500.9709242258616,\n                -207.99076791761004\n            ],\n            [\n                -94.86605934787985,\n                -1503.569000437215,\n                -207.99076791761004\n            ],\n            [\n                -100.06221177058649,\n                -1506.569000437215,\n                -207.99076791761004\n            ],\n            [\n                -107.85644040464643,\n                -1511.069000437215,\n                -207.99076791761004\n            ],\n            [\n                -115.65066903870638,\n                -1515.569000437215,\n                -207.99076791761004\n            ],\n            [\n                -123.44489767276633,\n                -1520.069000437215,\n                -207.99076791761004\n            ],\n            [\n                -131.23912630682628,\n                -1524.569000437215,\n                -207.99076791761004\n            ],\n            [\n                -139.03335494088623,\n                -1529.069000437215,\n                -207.99076791761004\n            ],\n            [\n                -146.82758357494617,\n                -1533.569000437215,\n                -207.99076791761004\n            ],\n            [\n                -154.62181220900612,\n                -1538.069000437215,\n                -207.99076791761004\n            ],\n            [\n                -159.81796463171275,\n                -1541.069000437215,\n                -207.99076791761004\n            ],\n            [\n                -162.41604084306607,\n                -1542.569000437215,\n                -207.99076791761004\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_short/2025-1-9_16-41-29_1220585472\",\n        \"gpt_instruction\": \"Move forward to a gray building characterized by large rectangular windows and a \\\" building \\\" type , then slightly turn right and proceed straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_164132_2\",\n            \"20250109_164134_5\",\n            \"20250109_164135_8\",\n            \"20250109_164136_11\",\n            \"20250109_164138_14\",\n            \"20250109_164139_17\",\n            \"20250109_164139_18\",\n            \"20250109_164141_21\",\n            \"20250109_164141_22\"\n        ],\n        \"pos\": [\n            [\n                -570.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -579.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -588.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -597.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -606.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -615.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -618.205937679655,\n                -1920.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -623.4020901023616,\n                -1917.9604620222499,\n                -270.6537207570657\n            ],\n            [\n                -626.000166313715,\n                -1916.4604620222499,\n                -270.6537207570657\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779915037,\n            2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_long/2025-1-10_7-2-30_75245562\",\n        \"gpt_instruction\": \"Advance towards the light brown building , which is tall , rectangular , with dark windows and is large in size . Slightly turn right and keep going straight towards it . Make a left turn to it . Move forward towards it . Slightly turn right and keep going straight towards the light brown building , which stands tall with distinctive corner structures and a significant number of windows , dominating the surrounding buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            1,\n            2,\n            2,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_070234_2\",\n            \"20250110_070235_5\",\n            \"20250110_070237_8\",\n            \"20250110_070238_11\",\n            \"20250110_070239_14\",\n            \"20250110_070241_17\",\n            \"20250110_070242_20\",\n            \"20250110_070242_21\",\n            \"20250110_070244_24\",\n            \"20250110_070244_25\",\n            \"20250110_070245_26\",\n            \"20250110_070245_27\",\n            \"20250110_070247_30\",\n            \"20250110_070248_33\",\n            \"20250110_070249_36\",\n            \"20250110_070250_38\",\n            \"20250110_070251_39\",\n            \"20250110_070252_42\",\n            \"20250110_070254_45\",\n            \"20250110_070255_48\",\n            \"20250110_070257_51\",\n            \"20250110_070257_52\"\n        ],\n        \"pos\": [\n            [\n                371.68866052637645,\n                -1279.3058992840279,\n                -207.99076791761004\n            ],\n            [\n                363.8944318923165,\n                -1283.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                356.10020325825656,\n                -1288.3058992840279,\n                -207.99076791761004\n            ],\n            [\n                348.3059746241966,\n                -1292.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                340.51174599013666,\n                -1297.3058992840279,\n                -207.99076791761004\n            ],\n            [\n                332.7175173560767,\n                -1301.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                324.92328872201676,\n                -1306.3058992840279,\n                -207.99076791761004\n            ],\n            [\n                322.32521251066345,\n                -1307.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                316.32521251066345,\n                -1307.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                313.32521251066345,\n                -1307.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                310.32521251066345,\n                -1307.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                310.32521251066345,\n                -1307.8058992840279,\n                -207.99076791761004\n            ],\n            [\n                307.32521251066345,\n                -1313.0020517067346,\n                -207.99076791761004\n            ],\n            [\n                302.82521251066345,\n                -1320.7962803407947,\n                -207.99076791761004\n            ],\n            [\n                298.32521251066345,\n                -1328.5905089748549,\n                -207.99076791761004\n            ],\n            [\n                295.32521251066345,\n                -1333.7866613975616,\n                -207.99076791761004\n            ],\n            [\n                293.82521251066345,\n                -1336.384737608915,\n                -207.99076791761004\n            ],\n            [\n                288.6290600879568,\n                -1339.384737608915,\n                -207.99076791761004\n            ],\n            [\n                280.83483145389687,\n                -1343.884737608915,\n                -207.99076791761004\n            ],\n            [\n                273.0406028198369,\n                -1348.384737608915,\n                -207.99076791761004\n            ],\n            [\n                265.246374185777,\n                -1352.884737608915,\n                -207.99076791761004\n            ],\n            [\n                262.64829797442366,\n                -1354.384737608915,\n                -207.99076791761004\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/high_average/2025-1-10_2-14-11_1151297278\",\n        \"gpt_instruction\": \"Proceed straight to the tall , brown cylindrical building with a grid - like window pattern , then slightly turn left and go directly ahead to the large , brick - red building featuring horizontal window strips .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_021415_2\",\n            \"20250110_021416_5\",\n            \"20250110_021418_8\",\n            \"20250110_021419_11\",\n            \"20250110_021420_14\",\n            \"20250110_021422_17\",\n            \"20250110_021423_20\",\n            \"20250110_021424_23\",\n            \"20250110_021426_26\",\n            \"20250110_021427_29\",\n            \"20250110_021428_32\",\n            \"20250110_021429_33\",\n            \"20250110_021430_36\",\n            \"20250110_021431_39\",\n            \"20250110_021432_41\",\n            \"20250110_021432_42\"\n        ],\n        \"pos\": [\n            [\n                -583.9918630785667,\n                -1996.9457432944535,\n                -211.9525511843355\n            ],\n            [\n                -591.7860917126268,\n                -1992.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -599.5803203466869,\n                -1987.9457432944535,\n                -211.9525511843355\n            ],\n            [\n                -607.374548980747,\n                -1983.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -615.1687776148071,\n                -1978.9457432944535,\n                -211.9525511843355\n            ],\n            [\n                -622.9630062488673,\n                -1974.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -630.7572348829274,\n                -1969.9457432944535,\n                -211.9525511843355\n            ],\n            [\n                -638.5514635169875,\n                -1965.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -646.3456921510476,\n                -1960.9457432944535,\n                -211.9525511843355\n            ],\n            [\n                -654.1399207851077,\n                -1956.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -661.9341494191679,\n                -1951.9457432944535,\n                -211.9525511843355\n            ],\n            [\n                -664.5322256305212,\n                -1950.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -670.5322256305212,\n                -1950.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -679.5322256305212,\n                -1950.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -685.5322256305212,\n                -1950.4457432944535,\n                -211.9525511843355\n            ],\n            [\n                -688.5322256305212,\n                -1950.4457432944535,\n                -211.9525511843355\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/low_long/2025-1-1_1-55-41_1586903190\",\n        \"gpt_instruction\": \"Move ahead to a gray building with rectangular windows that are medium - sized . Then , slightly turn right and proceed straight to a red and gray hotel featuring rounded corners with a decorative roof , which is medium - sized compared to surrounding buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_015545_2\",\n            \"20250101_015546_5\",\n            \"20250101_015547_8\",\n            \"20250101_015549_11\",\n            \"20250101_015550_14\",\n            \"20250101_015552_17\",\n            \"20250101_015553_20\",\n            \"20250101_015554_23\",\n            \"20250101_015555_24\",\n            \"20250101_015556_27\",\n            \"20250101_015557_28\",\n            \"20250101_015557_29\"\n        ],\n        \"pos\": [\n            [\n                -27.565926636239475,\n                -2394.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2403.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2412.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2421.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2430.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2439.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2448.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2457.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -27.565926636239475,\n                -2460.4846351407523,\n                -296.30638943601355\n            ],\n            [\n                -30.565926636239475,\n                -2465.6807875634586,\n                -296.30638943601355\n            ],\n            [\n                -32.065926636239475,\n                -2468.2788637748117,\n                -296.30638943601355\n            ],\n            [\n                -33.565926636239475,\n                -2470.876939986165,\n                -296.30638943601355\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023932237,\n            -2.0943951023932237,\n            -2.0943951023932237\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_26/astar_data/medium_long/2025-1-9_18-30-33_101323875\",\n        \"gpt_instruction\": \"Head straight towards a light beige skyscraper featuring a rectangular shape with a stepped top and vertical rows of windows . Then , go right to a gray building characterized by a tall rectangular structure with vertical lines and windows . Proceed straight towards a beige skyscraper that is tall and adorned with vertical stripes . Slightly turn right and continue straight to a beige concrete building marked by vertical window alignment and wall cracks , which is medium - sized .\",\n        \"action\": [\n            8,\n            3,\n            3,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250109_183036_1\",\n            \"20250109_183036_2\",\n            \"20250109_183037_3\",\n            \"20250109_183038_6\",\n            \"20250109_183039_9\",\n            \"20250109_183040_10\",\n            \"20250109_183041_13\",\n            \"20250109_183043_16\",\n            \"20250109_183044_19\",\n            \"20250109_183046_22\",\n            \"20250109_183047_25\",\n            \"20250109_183048_28\",\n            \"20250109_183050_31\",\n            \"20250109_183051_34\",\n            \"20250109_183053_37\",\n            \"20250109_183054_40\",\n            \"20250109_183055_43\",\n            \"20250109_183057_46\",\n            \"20250109_183058_49\",\n            \"20250109_183059_51\",\n            \"20250109_183100_52\"\n        ],\n        \"pos\": [\n            [\n                140.77302421195014,\n                -1612.81641189622,\n                -269.91094099928495\n            ],\n            [\n                143.77302421195014,\n                -1612.81641189622,\n                -269.91094099928495\n            ],\n            [\n                143.77302421195014,\n                -1612.81641189622,\n                -269.91094099928495\n            ],\n            [\n                146.77302421195014,\n                -1618.0125643189267,\n                -269.91094099928495\n            ],\n            [\n                151.27302421195014,\n                -1625.8067929529868,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1628.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1634.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1643.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1652.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1661.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1670.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1679.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1688.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1697.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1706.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1715.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1724.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1733.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1742.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1748.4048691643402,\n                -269.91094099928495\n            ],\n            [\n                152.77302421195014,\n                -1751.4048691643402,\n                -269.91094099928495\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-10_16-43-42_1113502215\",\n        \"gpt_instruction\": \"Walk directly towards the large white rectangular building with a flat roof , then slightly turn left and proceed directly towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_164345_2\",\n            \"20250110_164347_5\",\n            \"20250110_164349_8\",\n            \"20250110_164350_11\",\n            \"20250110_164352_14\",\n            \"20250110_164353_17\",\n            \"20250110_164354_18\",\n            \"20250110_164355_21\",\n            \"20250110_164357_24\",\n            \"20250110_164357_25\"\n        ],\n        \"pos\": [\n            [\n                58.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                67.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                76.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                85.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                94.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                103.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                106.48011789542207,\n                201.01798823536438,\n                156.84788500608087\n            ],\n            [\n                111.6762703181287,\n                204.01798823536438,\n                156.84788500608087\n            ],\n            [\n                119.47049895218865,\n                208.51798823536438,\n                156.84788500608087\n            ],\n            [\n                122.06857516354196,\n                210.01798823536438,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_short/2025-1-11_7-16-5_2055201132\",\n        \"gpt_instruction\": \"Proceed towards the medium - sized building characterized by its white color and rectangular shapes with linear indentations . Then , slightly turn left and move straight towards the large and prominent modern skyscraper , which features a white and silver color with a curved facade and reflective glass .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_071608_2\",\n            \"20250111_071610_5\",\n            \"20250111_071611_8\",\n            \"20250111_071612_9\",\n            \"20250111_071612_10\",\n            \"20250111_071614_13\",\n            \"20250111_071616_16\",\n            \"20250111_071616_17\"\n        ],\n        \"pos\": [\n            [\n                286.31470282962607,\n                230.15060839225063,\n                177.22623031643226\n            ],\n            [\n                278.5204741955661,\n                225.65060839225063,\n                177.22623031643226\n            ],\n            [\n                270.7262455615062,\n                221.15060839225063,\n                177.22623031643226\n            ],\n            [\n                268.12816935015286,\n                219.65060839225063,\n                177.22623031643226\n            ],\n            [\n                265.53009313879954,\n                218.15060839225063,\n                177.22623031643226\n            ],\n            [\n                262.53009313879954,\n                212.954455969544,\n                177.22623031643226\n            ],\n            [\n                258.03009313879954,\n                205.16022733548405,\n                177.22623031643226\n            ],\n            [\n                256.53009313879954,\n                202.56215112413074,\n                177.22623031643226\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/low_average/2025-1-1_22-37-59_1781999754\",\n        \"gpt_instruction\": \"\\\" Head straight toward a tall residential building with a brown exterior , minimal windows , and a large size , identified as an apartment tower . Then , slightly turn left and walk straight to reach a tall rectangular structure with a gray color , smooth glass - like fa\\u00e7ade , large size , and categorized as a modern high - rise building . \\\"\",\n        \"action\": [\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_223803_2\",\n            \"20250101_223805_5\",\n            \"20250101_223805_6\",\n            \"20250101_223807_9\",\n            \"20250101_223809_12\",\n            \"20250101_223811_15\",\n            \"20250101_223812_18\",\n            \"20250101_223814_21\",\n            \"20250101_223816_24\",\n            \"20250101_223818_27\",\n            \"20250101_223819_30\",\n            \"20250101_223821_33\",\n            \"20250101_223821_34\"\n        ],\n        \"pos\": [\n            [\n                1139.690706880984,\n                1008.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1148.690706880984,\n                1008.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1151.690706880984,\n                1008.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1156.8868593036907,\n                1011.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1164.6810879377508,\n                1015.7595515155147,\n                38.45987197213033\n            ],\n            [\n                1172.475316571811,\n                1020.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1180.269545205871,\n                1024.7595515155147,\n                38.45987197213033\n            ],\n            [\n                1188.0637738399312,\n                1029.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1195.8580024739913,\n                1033.7595515155147,\n                38.45987197213033\n            ],\n            [\n                1203.6522311080514,\n                1038.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1211.4464597421115,\n                1042.7595515155147,\n                38.45987197213033\n            ],\n            [\n                1219.2406883761716,\n                1047.2595515155147,\n                38.45987197213033\n            ],\n            [\n                1221.838764587525,\n                1048.7595515155147,\n                38.45987197213033\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_average_updown/2025-1-14_16-58-57_1845716951\",\n        \"gpt_instruction\": \"Move upwards to a large , black modern skyscraper that is tall and cylindrical with windows and balconies . Then , head straight towards it . Slightly turn left and proceed to it . Slightly stop and continue downwards to a tall , green modern skyscraper adorned with curved balconies and multiple stories .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_165857_0\",\n            \"20250114_165900_1\",\n            \"20250114_165900_2\",\n            \"20250114_165901_3\",\n            \"20250114_165901_4\",\n            \"20250114_165902_5\",\n            \"20250114_165902_6\",\n            \"20250114_165903_7\",\n            \"20250114_165903_8\",\n            \"20250114_165903_9\",\n            \"20250114_165904_10\",\n            \"20250114_165904_11\",\n            \"20250114_165905_12\",\n            \"20250114_165906_15\",\n            \"20250114_165908_18\",\n            \"20250114_165909_21\",\n            \"20250114_165911_24\",\n            \"20250114_165912_27\",\n            \"20250114_165914_30\",\n            \"20250114_165915_33\",\n            \"20250114_165917_36\",\n            \"20250114_165917_37\",\n            \"20250114_165917_38\",\n            \"20250114_165919_41\",\n            \"20250114_165921_44\",\n            \"20250114_165922_47\",\n            \"20250114_165923_48\",\n            \"20250114_165923_49\",\n            \"20250114_165924_50\",\n            \"20250114_165924_51\",\n            \"20250114_165924_52\",\n            \"20250114_165925_53\",\n            \"20250114_165925_54\",\n            \"20250114_165926_55\",\n            \"20250114_165926_56\",\n            \"20250114_165927_57\",\n            \"20250114_165927_58\",\n            \"20250114_165928_59\",\n            \"20250114_165928_60\",\n            \"20250114_165929_61\"\n        ],\n        \"pos\": [\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                23.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                26.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                29.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                32.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                35.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                38.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                41.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                44.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                47.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                50.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                53.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                56.215125399936845\n            ],\n            [\n                1256.5436268287963,\n                -619.3465600312236,\n                59.215125399936845\n            ],\n            [\n                1259.5436268287963,\n                -614.1504076085168,\n                62.215125399936845\n            ],\n            [\n                1264.0436268287963,\n                -606.3561789744567,\n                62.215125399936845\n            ],\n            [\n                1268.5436268287963,\n                -598.5619503403966,\n                62.215125399936845\n            ],\n            [\n                1273.0436268287963,\n                -590.7677217063365,\n                62.215125399936845\n            ],\n            [\n                1277.5436268287963,\n                -582.9734930722764,\n                62.215125399936845\n            ],\n            [\n                1282.0436268287963,\n                -575.1792644382163,\n                62.215125399936845\n            ],\n            [\n                1286.5436268287963,\n                -567.3850358041561,\n                62.215125399936845\n            ],\n            [\n                1291.0436268287963,\n                -559.590807170096,\n                62.215125399936845\n            ],\n            [\n                1292.5436268287963,\n                -556.9927309587426,\n                62.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -554.3946547473893,\n                62.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -548.3946547473893,\n                62.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -539.3946547473893,\n                62.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -530.3946547473893,\n                62.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                62.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                59.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                56.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                53.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                50.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                47.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                44.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                41.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                38.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                35.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                32.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                29.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                26.215125399936845\n            ],\n            [\n                1294.0436268287963,\n                -527.3946547473893,\n                23.215125399936845\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_long/2025-1-7_10-25-13_794123786\",\n        \"gpt_instruction\": \"First , walk straight toward the gray structure featuring large glass windows with metal frames , which is part of a modern building facade . Then , slightly turn left and head straight to the gray modern skyscraper characterized by tall windows and vegetation growing at the side .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_102517_2\",\n            \"20250107_102519_5\",\n            \"20250107_102520_8\",\n            \"20250107_102522_11\",\n            \"20250107_102523_14\",\n            \"20250107_102524_17\",\n            \"20250107_102526_20\",\n            \"20250107_102528_23\",\n            \"20250107_102529_26\",\n            \"20250107_102531_29\",\n            \"20250107_102532_32\",\n            \"20250107_102534_35\",\n            \"20250107_102534_36\",\n            \"20250107_102536_39\",\n            \"20250107_102537_41\",\n            \"20250107_102537_42\"\n        ],\n        \"pos\": [\n            [\n                438.3312210853634,\n                840.8349177576263,\n                56.222275915786355\n            ],\n            [\n                446.12544971942333,\n                845.3349177576263,\n                56.222275915786355\n            ],\n            [\n                453.9196783534833,\n                849.8349177576263,\n                56.222275915786355\n            ],\n            [\n                461.7139069875432,\n                854.3349177576263,\n                56.222275915786355\n            ],\n            [\n                469.5081356216032,\n                858.8349177576263,\n                56.222275915786355\n            ],\n            [\n                477.3023642556631,\n                863.3349177576263,\n                56.222275915786355\n            ],\n            [\n                485.09659288972307,\n                867.8349177576263,\n                56.222275915786355\n            ],\n            [\n                492.890821523783,\n                872.3349177576263,\n                56.222275915786355\n            ],\n            [\n                500.68505015784297,\n                876.8349177576263,\n                56.222275915786355\n            ],\n            [\n                508.4792787919029,\n                881.3349177576263,\n                56.222275915786355\n            ],\n            [\n                516.2735074259629,\n                885.8349177576263,\n                56.222275915786355\n            ],\n            [\n                524.067736060023,\n                890.3349177576263,\n                56.222275915786355\n            ],\n            [\n                526.6658122713764,\n                891.8349177576263,\n                56.222275915786355\n            ],\n            [\n                529.6658122713764,\n                897.031070180333,\n                56.222275915786355\n            ],\n            [\n                532.6658122713764,\n                902.2272226030398,\n                56.222275915786355\n            ],\n            [\n                534.1658122713764,\n                904.8252988143931,\n                56.222275915786355\n            ]\n        ],\n        \"yaw\": [\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_short_updown/2025-1-14_6-25-55_937558955\",\n        \"gpt_instruction\": \"Begin at the large modern building with a white color and characterized by horizontal lines , then proceed straight along another similar building that also features a white color and horizontal lines pattern . After slightly pausing , go downwards towards a structure distinguished by its light beige color , constructed with square tiles and a central vertical seam , which is medium to large in size and appears flat and wide . This last structure is identified as an exterior wall or building facade .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_062555_0\",\n            \"20250114_062558_1\",\n            \"20250114_062559_2\",\n            \"20250114_062559_3\",\n            \"20250114_062600_4\",\n            \"20250114_062600_5\",\n            \"20250114_062601_6\",\n            \"20250114_062601_7\",\n            \"20250114_062602_8\",\n            \"20250114_062602_9\",\n            \"20250114_062602_10\",\n            \"20250114_062603_11\",\n            \"20250114_062603_12\",\n            \"20250114_062604_13\",\n            \"20250114_062604_14\",\n            \"20250114_062605_15\",\n            \"20250114_062605_16\",\n            \"20250114_062606_17\",\n            \"20250114_062607_20\",\n            \"20250114_062609_23\",\n            \"20250114_062611_26\",\n            \"20250114_062612_29\",\n            \"20250114_062614_32\",\n            \"20250114_062616_35\",\n            \"20250114_062618_38\",\n            \"20250114_062618_39\",\n            \"20250114_062619_40\",\n            \"20250114_062619_41\",\n            \"20250114_062620_42\",\n            \"20250114_062620_43\",\n            \"20250114_062621_44\",\n            \"20250114_062621_45\",\n            \"20250114_062622_46\",\n            \"20250114_062622_47\",\n            \"20250114_062623_48\",\n            \"20250114_062623_49\",\n            \"20250114_062624_50\",\n            \"20250114_062624_51\",\n            \"20250114_062625_52\",\n            \"20250114_062625_53\",\n            \"20250114_062626_54\",\n            \"20250114_062626_55\",\n            \"20250114_062626_56\",\n            \"20250114_062627_57\"\n        ],\n        \"pos\": [\n            [\n                315.3795900295682,\n                198.2562680894906,\n                22.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                25.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                28.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                31.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                34.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                37.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                40.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                43.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                46.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                49.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                52.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                55.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                58.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                61.482939086907805\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                64.4829390869078\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                67.4829390869078\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                70.4829390869078\n            ],\n            [\n                315.3795900295682,\n                198.2562680894906,\n                73.4829390869078\n            ],\n            [\n                309.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                300.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                291.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                282.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                273.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                264.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                255.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                76.4829390869078\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                73.4829390869078\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                70.4829390869078\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                67.4829390869078\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                64.4829390869078\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                61.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                58.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                55.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                52.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                49.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                46.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                43.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                40.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                37.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                34.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                31.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                28.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                25.482939086907805\n            ],\n            [\n                252.3795900295682,\n                198.2562680894906,\n                22.482939086907805\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_long/2025-1-6_22-49-32_1777288820\",\n        \"gpt_instruction\": \"Proceed straight towards a medium - sized building featuring white and black colors with a rectangular structure and vertical black stripes . Then , slightly turn right and continue directly ahead to reach a modern office building that is tall , colored in gray and blue - green , and has a pyramid - shaped roof . Finally , slightly turn left and walk straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_224936_2\",\n            \"20250106_224938_5\",\n            \"20250106_224939_7\",\n            \"20250106_224939_8\",\n            \"20250106_224941_11\",\n            \"20250106_224942_14\",\n            \"20250106_224944_17\",\n            \"20250106_224945_20\",\n            \"20250106_224947_23\",\n            \"20250106_224948_26\",\n            \"20250106_224949_29\",\n            \"20250106_224951_32\",\n            \"20250106_224952_35\",\n            \"20250106_224954_38\",\n            \"20250106_224955_40\",\n            \"20250106_224956_41\",\n            \"20250106_224957_44\",\n            \"20250106_224958_46\",\n            \"20250106_224959_47\"\n        ],\n        \"pos\": [\n            [\n                -1032.7517257030336,\n                -947.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -1023.7517257030336,\n                -947.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -1017.7517257030336,\n                -947.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -1014.7517257030336,\n                -947.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -1009.5555732803268,\n                -950.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -1001.7613446462667,\n                -954.7278015352958,\n                64.91834897342241\n            ],\n            [\n                -993.9671160122066,\n                -959.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -986.1728873781465,\n                -963.7278015352958,\n                64.91834897342241\n            ],\n            [\n                -978.3786587440864,\n                -968.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -970.5844301100262,\n                -972.7278015352958,\n                64.91834897342241\n            ],\n            [\n                -962.7902014759661,\n                -977.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -954.995972841906,\n                -981.7278015352958,\n                64.91834897342241\n            ],\n            [\n                -947.2017442078459,\n                -986.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -939.4075155737858,\n                -990.7278015352958,\n                64.91834897342241\n            ],\n            [\n                -934.211363151079,\n                -993.7278015352958,\n                64.91834897342241\n            ],\n            [\n                -931.6132869397256,\n                -995.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -925.6132869397256,\n                -995.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -919.6132869397256,\n                -995.2278015352958,\n                64.91834897342241\n            ],\n            [\n                -916.6132869397256,\n                -995.2278015352958,\n                64.91834897342241\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            0.0,\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/low_average/2025-1-2_0-18-22_19485054\",\n        \"gpt_instruction\": \"Start by proceeding straight until you reach a large gray building characterized by multiple windows in vertical alignment . Then , slightly turn right and continue directly ahead to find a tall gray commercial high - rise building , distinguished by vertical columns and a large glass canopy on top .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_001826_2\",\n            \"20250102_001828_5\",\n            \"20250102_001830_8\",\n            \"20250102_001831_9\",\n            \"20250102_001832_11\",\n            \"20250102_001833_12\"\n        ],\n        \"pos\": [\n            [\n                -1521.4208454889456,\n                643.236521300699,\n                36.59604346909296\n            ],\n            [\n                -1516.9208454889456,\n                635.4422926666389,\n                36.59604346909296\n            ],\n            [\n                -1512.4208454889456,\n                627.6480640325788,\n                36.59604346909296\n            ],\n            [\n                -1510.9208454889456,\n                625.0499878212254,\n                36.59604346909296\n            ],\n            [\n                -1510.9208454889456,\n                622.0499878212254,\n                36.59604346909296\n            ],\n            [\n                -1510.9208454889456,\n                619.0499878212254,\n                36.59604346909296\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_short/2025-1-11_5-55-3_245382704\",\n        \"gpt_instruction\": \"Proceed directly ahead to the large skyscraper featuring gray and blue vertical stripes with rectangular window panels and a modern facade .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_055507_2\",\n            \"20250111_055509_5\",\n            \"20250111_055510_8\",\n            \"20250111_055511_9\"\n        ],\n        \"pos\": [\n            [\n                -194.29764579314704,\n                114.85102301608053,\n                177.22623031643226\n            ],\n            [\n                -185.29764579314704,\n                114.85102301608053,\n                177.22623031643226\n            ],\n            [\n                -176.29764579314704,\n                114.85102301608053,\n                177.22623031643226\n            ],\n            [\n                -173.29764579314704,\n                114.85102301608053,\n                177.22623031643226\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_short/2025-1-10_22-48-49_822514995\",\n        \"gpt_instruction\": \"Advance forward to a large white building characterized by a rectangular shape , horizontal lines , and small windows . Then , slightly turn right and head straight toward it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_224853_2\",\n            \"20250110_224854_5\",\n            \"20250110_224856_7\",\n            \"20250110_224856_8\",\n            \"20250110_224858_11\",\n            \"20250110_224900_14\",\n            \"20250110_224900_15\"\n        ],\n        \"pos\": [\n            [\n                283.74047649663703,\n                163.16154520979413,\n                177.22623031643226\n            ],\n            [\n                274.74047649663703,\n                163.16154520979413,\n                177.22623031643226\n            ],\n            [\n                268.74047649663703,\n                163.16154520979413,\n                177.22623031643226\n            ],\n            [\n                265.74047649663703,\n                163.16154520979413,\n                177.22623031643226\n            ],\n            [\n                260.5443240739304,\n                166.16154520979413,\n                177.22623031643226\n            ],\n            [\n                252.75009543987045,\n                170.66154520979413,\n                177.22623031643226\n            ],\n            [\n                250.15201922851713,\n                172.16154520979413,\n                177.22623031643226\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-11_0-45-31_2026478004\",\n        \"gpt_instruction\": \"Proceed directly towards the high - rise building showcasing a modern design with a distinctive step - like facade and aligned vertical lines in shades of white and dark gray , then slightly turn right and move ahead to the tall and prominent octagonal building with vertical sections , which is accented in white and black .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_004535_2\",\n            \"20250111_004536_5\",\n            \"20250111_004538_8\",\n            \"20250111_004540_11\",\n            \"20250111_004541_14\",\n            \"20250111_004543_17\",\n            \"20250111_004544_20\",\n            \"20250111_004546_23\",\n            \"20250111_004546_24\",\n            \"20250111_004548_27\",\n            \"20250111_004549_30\",\n            \"20250111_004550_31\"\n        ],\n        \"pos\": [\n            [\n                529.4848620632796,\n                292.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                301.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                310.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                319.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                328.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                337.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                346.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                355.4327679543983,\n                156.84788500608087\n            ],\n            [\n                529.4848620632796,\n                358.4327679543983,\n                156.84788500608087\n            ],\n            [\n                532.4848620632796,\n                363.6289203771049,\n                156.84788500608087\n            ],\n            [\n                536.9848620632796,\n                371.42314901116487,\n                156.84788500608087\n            ],\n            [\n                538.4848620632796,\n                374.0212252225182,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_long/2025-1-6_22-31-35_1390598089\",\n        \"gpt_instruction\": \"Advance forward to a black and reflective modern office building with a large glass fa\\u00e7ade featuring a grid - like pattern . Slightly turn left slightly to reach it . Continue slightly to the left and move straight to find it . Proceed straight to it . Slightly turn right , then keep going straight to arrive at a brown and white brick building , medium - sized relative to its surroundings , featuring rectangular windows arranged in a grid pattern .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            2,\n            1,\n            3,\n            3,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_223138_2\",\n            \"20250106_223140_5\",\n            \"20250106_223142_8\",\n            \"20250106_223144_11\",\n            \"20250106_223146_14\",\n            \"20250106_223148_17\",\n            \"20250106_223150_20\",\n            \"20250106_223151_21\",\n            \"20250106_223153_24\",\n            \"20250106_223154_25\",\n            \"20250106_223154_26\",\n            \"20250106_223155_27\",\n            \"20250106_223156_28\",\n            \"20250106_223158_31\",\n            \"20250106_223158_32\",\n            \"20250106_223159_33\",\n            \"20250106_223201_36\",\n            \"20250106_223203_39\",\n            \"20250106_223205_42\",\n            \"20250106_223207_45\",\n            \"20250106_223209_48\",\n            \"20250106_223211_51\",\n            \"20250106_223211_52\"\n        ],\n        \"pos\": [\n            [\n                1211.6919644808456,\n                -1584.8825695985008,\n                42.855175188356434\n            ],\n            [\n                1207.1919644808456,\n                -1577.0883409644407,\n                42.855175188356434\n            ],\n            [\n                1202.6919644808456,\n                -1569.2941123303806,\n                42.855175188356434\n            ],\n            [\n                1198.1919644808456,\n                -1561.4998836963205,\n                42.855175188356434\n            ],\n            [\n                1193.6919644808456,\n                -1553.7056550622603,\n                42.855175188356434\n            ],\n            [\n                1189.1919644808456,\n                -1545.9114264282002,\n                42.855175188356434\n            ],\n            [\n                1184.6919644808456,\n                -1538.11719779414,\n                42.855175188356434\n            ],\n            [\n                1183.1919644808456,\n                -1535.5191215827867,\n                42.855175188356434\n            ],\n            [\n                1177.9958120581389,\n                -1532.5191215827867,\n                42.855175188356434\n            ],\n            [\n                1175.3977358467855,\n                -1531.0191215827867,\n                42.855175188356434\n            ],\n            [\n                1175.3977358467855,\n                -1531.0191215827867,\n                42.855175188356434\n            ],\n            [\n                1172.3977358467855,\n                -1531.0191215827867,\n                42.855175188356434\n            ],\n            [\n                1172.3977358467855,\n                -1531.0191215827867,\n                42.855175188356434\n            ],\n            [\n                1169.3977358467855,\n                -1525.82296916008,\n                42.855175188356434\n            ],\n            [\n                1167.8977358467855,\n                -1523.2248929487266,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1520.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1514.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1505.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1496.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1487.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1478.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1469.6268167373732,\n                42.855175188356434\n            ],\n            [\n                1166.3977358467855,\n                -1466.6268167373732,\n                42.855175188356434\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.1415926535898024,\n            3.141592653589793,\n            2.6179938779914944,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_short/2025-1-6_14-19-8_1404196431\",\n        \"gpt_instruction\": \"Head straight toward a large white and brown residential apartment building characterized by multiple windows and balconies . Then , slightly turn left and proceed straight to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_141912_2\",\n            \"20250106_141913_5\",\n            \"20250106_141915_8\",\n            \"20250106_141916_9\",\n            \"20250106_141918_12\",\n            \"20250106_141919_14\",\n            \"20250106_141919_15\"\n        ],\n        \"pos\": [\n            [\n                1664.3909159906939,\n                884.0489549205003,\n                59.844217305829574\n            ],\n            [\n                1656.5966873566338,\n                879.5489549205003,\n                59.844217305829574\n            ],\n            [\n                1648.8024587225736,\n                875.0489549205003,\n                59.844217305829574\n            ],\n            [\n                1646.2043825112203,\n                873.5489549205003,\n                59.844217305829574\n            ],\n            [\n                1643.2043825112203,\n                868.3528024977936,\n                59.844217305829574\n            ],\n            [\n                1640.2043825112203,\n                863.1566500750869,\n                59.844217305829574\n            ],\n            [\n                1638.7043825112203,\n                860.5585738637335,\n                59.844217305829574\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_long/2025-1-12_5-22-30_160715005\",\n        \"gpt_instruction\": \"Proceed directly to the light brown tall building with multiple windows . Then , slightly turn right towards the large gray square with geometric patterns . Next , slightly turn left and head towards the white and black tall skyscraper with vertical stripes . Afterward , slightly turn left again , continue straight , and head towards the large white and blue office building or skyscraper with an angled glass facade featuring distinct linear patterns and vertical lines , occupying most of the right side of the image .\",\n        \"action\": [\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250112_052233_2\",\n            \"20250112_052234_3\",\n            \"20250112_052234_4\",\n            \"20250112_052236_7\",\n            \"20250112_052237_10\",\n            \"20250112_052239_13\",\n            \"20250112_052240_16\",\n            \"20250112_052240_17\",\n            \"20250112_052242_20\",\n            \"20250112_052243_23\",\n            \"20250112_052245_26\",\n            \"20250112_052246_29\",\n            \"20250112_052247_32\",\n            \"20250112_052248_33\",\n            \"20250112_052249_36\",\n            \"20250112_052251_39\",\n            \"20250112_052252_41\",\n            \"20250112_052252_42\"\n        ],\n        \"pos\": [\n            [\n                86.29751162567474,\n                691.7790080745449,\n                141.12572478310412\n            ],\n            [\n                84.79751162567474,\n                689.1809318631915,\n                141.12572478310412\n            ],\n            [\n                83.29751162567474,\n                686.5828556518381,\n                141.12572478310412\n            ],\n            [\n                78.10135920296811,\n                683.5828556518381,\n                141.12572478310412\n            ],\n            [\n                70.30713056890816,\n                679.0828556518381,\n                141.12572478310412\n            ],\n            [\n                62.512901934848216,\n                674.5828556518381,\n                141.12572478310412\n            ],\n            [\n                54.71867330078827,\n                670.0828556518381,\n                141.12572478310412\n            ],\n            [\n                52.12059708943495,\n                668.5828556518381,\n                141.12572478310412\n            ],\n            [\n                49.12059708943495,\n                663.3867032291314,\n                141.12572478310412\n            ],\n            [\n                44.62059708943495,\n                655.5924745950713,\n                141.12572478310412\n            ],\n            [\n                40.12059708943495,\n                647.7982459610112,\n                141.12572478310412\n            ],\n            [\n                35.62059708943495,\n                640.004017326951,\n                141.12572478310412\n            ],\n            [\n                31.120597089434952,\n                632.2097886928909,\n                141.12572478310412\n            ],\n            [\n                29.620597089434952,\n                629.6117124815376,\n                141.12572478310412\n            ],\n            [\n                29.620597089434952,\n                623.6117124815376,\n                141.12572478310412\n            ],\n            [\n                29.620597089434952,\n                614.6117124815376,\n                141.12572478310412\n            ],\n            [\n                29.620597089434952,\n                608.6117124815376,\n                141.12572478310412\n            ],\n            [\n                29.620597089434952,\n                605.6117124815376,\n                141.12572478310412\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-10_16-21-26_1309383303\",\n        \"gpt_instruction\": \"Head directly towards a modern tall skyscraper characterized by a vertical window design , which is white in color and large in size . Then , veer left to enter a medium - sized square with minimal vegetation , featuring a paved open space and a gray surface . Continue straight to approach it . Slightly turn right as you walk straight to a tall vertical structure with rounded ends and horizontal lines , which is the tallest building in the vicinity , distinguished by its white color with gray accents , and is a modern skyscraper .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            2,\n            8,\n            3,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_162130_2\",\n            \"20250110_162131_5\",\n            \"20250110_162133_8\",\n            \"20250110_162134_11\",\n            \"20250110_162136_14\",\n            \"20250110_162137_17\",\n            \"20250110_162139_20\",\n            \"20250110_162140_21\",\n            \"20250110_162140_22\",\n            \"20250110_162141_23\",\n            \"20250110_162142_25\",\n            \"20250110_162142_26\",\n            \"20250110_162144_29\",\n            \"20250110_162145_32\",\n            \"20250110_162147_34\",\n            \"20250110_162147_35\"\n        ],\n        \"pos\": [\n            [\n                334.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                343.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                352.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                361.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                370.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                379.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                388.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                391.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                394.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                394.58349929817723,\n                578.8381365196327,\n                156.84788500608087\n            ],\n            [\n                396.08349929817723,\n                581.436212730986,\n                156.84788500608087\n            ],\n            [\n                397.58349929817723,\n                584.0342889423393,\n                156.84788500608087\n            ],\n            [\n                402.77965172088386,\n                587.0342889423393,\n                156.84788500608087\n            ],\n            [\n                410.5738803549438,\n                591.5342889423393,\n                156.84788500608087\n            ],\n            [\n                415.77003277765044,\n                594.5342889423393,\n                156.84788500608087\n            ],\n            [\n                418.36810898900376,\n                596.0342889423393,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511965883,\n            1.0471975511966072,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/low_short_updown/2025-01-19_11-50-51_187857\",\n        \"gpt_instruction\": \"Move up to a large , multi - story residential building characterized by a modern design with large windows and rectangular patterns , in white and dark brown . Head straight to it . Slightly turn right and go directly ahead to a medium - sized concrete bench in light grey , noted for its rectangular shape . Slightly go up and proceed to a modern high - rise building , large in size , with tall , rectangular shapes and a vertical striped pattern in grey - white . Finally , slightly turn left , go straight , and slightly stop at a large cylindrical high - rise building with a reflective glass surface , in blue - grey .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            4,\n            9,\n            2,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250119_115052_0\",\n            \"20250119_115053_1\",\n            \"20250119_115054_2\",\n            \"20250119_115055_3\",\n            \"20250119_115058_6\",\n            \"20250119_115101_9\",\n            \"20250119_115104_12\",\n            \"20250119_115107_15\",\n            \"20250119_115108_16\",\n            \"20250119_115109_17\",\n            \"20250119_115112_20\",\n            \"20250119_115115_23\",\n            \"20250119_115117_26\",\n            \"20250119_115120_29\",\n            \"20250119_115123_32\",\n            \"20250119_115124_33\",\n            \"20250119_115127_36\",\n            \"20250119_115128_37\",\n            \"20250119_115129_38\",\n            \"20250119_115130_39\",\n            \"20250119_115131_40\",\n            \"20250119_115132_41\",\n            \"20250119_115132_42\",\n            \"20250119_115133_43\",\n            \"20250119_115134_44\"\n        ],\n        \"pos\": [\n            [\n                778.8089811712017,\n                699.1325713896781,\n                23.308114466109473,\n                -1.0471975511966072\n            ],\n            [\n                778.8089811712017,\n                699.1325713896781,\n                26.308114466109473,\n                -1.0471975511966072\n            ],\n            [\n                778.8089811712017,\n                699.1325713896781,\n                29.308114466109473,\n                -1.0471975511966072\n            ],\n            [\n                778.8089811712017,\n                699.1325713896781,\n                32.30811446610947,\n                -1.0471975511966072\n            ],\n            [\n                781.8089811712017,\n                693.9364189669714,\n                35.30811446610947\n            ],\n            [\n                786.3089811712017,\n                686.1421903329112,\n                35.30811446610947\n            ],\n            [\n                790.8089811712017,\n                678.3479616988511,\n                35.30811446610947\n            ],\n            [\n                795.3089811712017,\n                670.553733064791,\n                35.30811446610947\n            ],\n            [\n                796.8089811712017,\n                667.9556568534376,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                665.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                659.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                650.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                641.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                632.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                623.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                620.3575806420843,\n                35.30811446610947\n            ],\n            [\n                798.3089811712017,\n                614.3575806420843,\n                38.30811446610947\n            ],\n            [\n                798.3089811712017,\n                611.3575806420843,\n                38.30811446610947\n            ],\n            [\n                798.3089811712017,\n                611.3575806420843,\n                38.30811446610947\n            ],\n            [\n                799.8089811712017,\n                608.7595044307309,\n                38.30811446610947\n            ],\n            [\n                799.8089811712017,\n                608.7595044307309,\n                35.30811446610947,\n                -1.0471975511965979\n            ],\n            [\n                799.8089811712017,\n                608.7595044307309,\n                32.30811446610947,\n                -1.0471975511965979\n            ],\n            [\n                799.8089811712017,\n                608.7595044307309,\n                29.308114466109473,\n                -1.0471975511965979\n            ],\n            [\n                799.8089811712017,\n                608.7595044307309,\n                26.308114466109473,\n                -1.0471975511965979\n            ],\n            [\n                799.8089811712017,\n                608.7595044307309,\n                23.308114466109473,\n                -1.0471975511965979\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-10_21-38-23_1651574882\",\n        \"gpt_instruction\": \"Advance forward to a structure characterized by a silver , glass - like appearance , featuring a curved facade with reflective windows , notable height , and surrounded by other skyscrapers . Slightly turn right and proceed straight to a building with a white and glassy finish , showcasing an angular design that combines modern architecture , rooftop pools , and prominent glass panels , appearing as a large , multi - story skyscraper .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_213827_2\",\n            \"20250110_213829_5\",\n            \"20250110_213830_8\",\n            \"20250110_213832_11\",\n            \"20250110_213834_14\",\n            \"20250110_213835_17\",\n            \"20250110_213837_20\",\n            \"20250110_213839_23\",\n            \"20250110_213839_24\",\n            \"20250110_213840_25\",\n            \"20250110_213841_28\",\n            \"20250110_213842_30\",\n            \"20250110_213843_31\"\n        ],\n        \"pos\": [\n            [\n                137.60330044315026,\n                -248.35243505695843,\n                156.84788500608087\n            ],\n            [\n                133.10330044315026,\n                -240.55820642289848,\n                156.84788500608087\n            ],\n            [\n                128.60330044315026,\n                -232.76397778883853,\n                156.84788500608087\n            ],\n            [\n                124.10330044315026,\n                -224.96974915477858,\n                156.84788500608087\n            ],\n            [\n                119.60330044315026,\n                -217.17552052071863,\n                156.84788500608087\n            ],\n            [\n                115.10330044315026,\n                -209.3812918866587,\n                156.84788500608087\n            ],\n            [\n                110.60330044315026,\n                -201.58706325259874,\n                156.84788500608087\n            ],\n            [\n                106.10330044315026,\n                -193.7928346185388,\n                156.84788500608087\n            ],\n            [\n                104.60330044315026,\n                -191.19475840718547,\n                156.84788500608087\n            ],\n            [\n                103.10330044315026,\n                -188.59668219583216,\n                156.84788500608087\n            ],\n            [\n                103.10330044315026,\n                -182.59668219583216,\n                156.84788500608087\n            ],\n            [\n                103.10330044315026,\n                -176.59668219583216,\n                156.84788500608087\n            ],\n            [\n                103.10330044315026,\n                -173.59668219583216,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/low_short_updown/2025-1-14_5-2-10_1242561041\",\n        \"gpt_instruction\": \"First , head upwards towards the large modern building characterized by a mixed color scheme of brick red and blue glass , featuring large glass windows and a brick fa\\u00e7ade . Then , proceed forward to another large , modern building displaying a color palette of white and gray , distinguished by multiple stories with horizontal lines and rounded edges . Next , slightly turn left and continue straight towards the tall modern skyscraper with immense proportions , boasting grey and blue hues and reflective glass panels . Lastly , slightly turn right , continue straight a little further , and proceed downwards to a medium - height modern commercial building adorned with a blue and brown glass fa\\u00e7ade and horizontal paneling .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            8,\n            2,\n            9,\n            9,\n            3,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_050210_0\",\n            \"20250114_050213_1\",\n            \"20250114_050214_2\",\n            \"20250114_050214_3\",\n            \"20250114_050216_6\",\n            \"20250114_050217_8\",\n            \"20250114_050217_9\",\n            \"20250114_050219_12\",\n            \"20250114_050221_15\",\n            \"20250114_050221_16\",\n            \"20250114_050222_17\",\n            \"20250114_050223_18\",\n            \"20250114_050223_19\",\n            \"20250114_050224_20\",\n            \"20250114_050224_21\",\n            \"20250114_050225_22\"\n        ],\n        \"pos\": [\n            [\n                -1800.4807667752439,\n                610.7716851023839,\n                23.308114466109473\n            ],\n            [\n                -1800.4807667752439,\n                610.7716851023839,\n                26.308114466109473\n            ],\n            [\n                -1800.4807667752439,\n                610.7716851023839,\n                29.308114466109473\n            ],\n            [\n                -1800.4807667752439,\n                610.7716851023839,\n                32.30811446610947\n            ],\n            [\n                -1797.4807667752439,\n                615.9678375250905,\n                35.30811446610947\n            ],\n            [\n                -1794.4807667752439,\n                621.1639899477973,\n                35.30811446610947\n            ],\n            [\n                -1792.9807667752439,\n                623.7620661591507,\n                35.30811446610947\n            ],\n            [\n                -1792.9807667752439,\n                629.7620661591507,\n                35.30811446610947\n            ],\n            [\n                -1792.9807667752439,\n                638.7620661591507,\n                35.30811446610947\n            ],\n            [\n                -1792.9807667752439,\n                641.7620661591507,\n                35.30811446610947\n            ],\n            [\n                -1792.9807667752439,\n                641.7620661591507,\n                35.30811446610947\n            ],\n            [\n                -1791.4807667752439,\n                644.360142370504,\n                35.30811446610947\n            ],\n            [\n                -1791.4807667752439,\n                644.360142370504,\n                32.30811446610947\n            ],\n            [\n                -1791.4807667752439,\n                644.360142370504,\n                29.308114466109473\n            ],\n            [\n                -1791.4807667752439,\n                644.360142370504,\n                26.308114466109473\n            ],\n            [\n                -1791.4807667752439,\n                644.360142370504,\n                23.308114466109473\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965883,\n            1.0471975511965883,\n            1.0471975511965883,\n            1.0471975511965883,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-11_3-1-49_1836997946\",\n        \"gpt_instruction\": \"Head straight to a large , multi - storied skyscraper characterized by its white and glass color scheme , a modern architectural design with prominent reflective glass surfaces , and towering size .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_030152_2\",\n            \"20250111_030154_5\",\n            \"20250111_030156_8\",\n            \"20250111_030157_11\",\n            \"20250111_030159_14\",\n            \"20250111_030200_17\",\n            \"20250111_030202_20\",\n            \"20250111_030203_22\",\n            \"20250111_030203_23\"\n        ],\n        \"pos\": [\n            [\n                138.58038394003455,\n                -225.27061159917974,\n                156.84788500608087\n            ],\n            [\n                134.08038394003455,\n                -217.4763829651198,\n                156.84788500608087\n            ],\n            [\n                129.58038394003455,\n                -209.68215433105985,\n                156.84788500608087\n            ],\n            [\n                125.08038394003455,\n                -201.8879256969999,\n                156.84788500608087\n            ],\n            [\n                120.58038394003455,\n                -194.09369706293995,\n                156.84788500608087\n            ],\n            [\n                116.08038394003455,\n                -186.29946842888,\n                156.84788500608087\n            ],\n            [\n                111.58038394003455,\n                -178.50523979482006,\n                156.84788500608087\n            ],\n            [\n                108.58038394003455,\n                -173.30908737211342,\n                156.84788500608087\n            ],\n            [\n                107.08038394003455,\n                -170.7110111607601,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_long/2025-1-6_22-34-42_674402557\",\n        \"gpt_instruction\": \"First , proceed straight towards a light gray skyscraper featuring a tall , pyramid - shaped top and a large size . Then , slightly turn right and continue straight to reach a white building with orange accents , characterized by arched structures on the rooftop , symmetric window designs , a mid-sized stature compared to the surrounding skyscrapers , and its designation as a residential building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_223445_2\",\n            \"20250106_223447_5\",\n            \"20250106_223448_8\",\n            \"20250106_223449_9\",\n            \"20250106_223450_12\",\n            \"20250106_223452_15\",\n            \"20250106_223454_18\",\n            \"20250106_223455_21\",\n            \"20250106_223457_24\",\n            \"20250106_223458_27\",\n            \"20250106_223500_30\",\n            \"20250106_223501_33\",\n            \"20250106_223503_36\",\n            \"20250106_223504_39\",\n            \"20250106_223506_42\",\n            \"20250106_223507_45\",\n            \"20250106_223508_46\"\n        ],\n        \"pos\": [\n            [\n                504.66598313972906,\n                -223.2746074479178,\n                44.60117943971279\n            ],\n            [\n                513.6659831397291,\n                -223.2746074479178,\n                44.60117943971279\n            ],\n            [\n                522.6659831397291,\n                -223.2746074479178,\n                44.60117943971279\n            ],\n            [\n                525.6659831397291,\n                -223.2746074479178,\n                44.60117943971279\n            ],\n            [\n                530.8621355624357,\n                -226.2746074479178,\n                44.60117943971279\n            ],\n            [\n                538.6563641964958,\n                -230.7746074479178,\n                44.60117943971279\n            ],\n            [\n                546.4505928305559,\n                -235.2746074479178,\n                44.60117943971279\n            ],\n            [\n                554.244821464616,\n                -239.7746074479178,\n                44.60117943971279\n            ],\n            [\n                562.0390500986762,\n                -244.2746074479178,\n                44.60117943971279\n            ],\n            [\n                569.8332787327363,\n                -248.7746074479178,\n                44.60117943971279\n            ],\n            [\n                577.6275073667964,\n                -253.2746074479178,\n                44.60117943971279\n            ],\n            [\n                585.4217360008565,\n                -257.7746074479178,\n                44.60117943971279\n            ],\n            [\n                593.2159646349166,\n                -262.2746074479178,\n                44.60117943971279\n            ],\n            [\n                601.0101932689768,\n                -266.7746074479178,\n                44.60117943971279\n            ],\n            [\n                608.8044219030369,\n                -271.2746074479178,\n                44.60117943971279\n            ],\n            [\n                616.598650537097,\n                -275.7746074479178,\n                44.60117943971279\n            ],\n            [\n                619.1967267484504,\n                -277.2746074479178,\n                44.60117943971279\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_short_updown/2025-1-14_3-26-7_155789224\",\n        \"gpt_instruction\": \"Move upwards towards the light gray apartment building characterized by glass balconies with a blue tint , medium height , occupying the center foreground . Proceed to the large gray skyscraper with a multi - story structure . Slightly turn left and go directly ahead to the large building with a modern architectural design featuring unique geometric shapes in blue and beige . Stop briefly , then go downwards to it .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            8,\n            2,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_032608_0\",\n            \"20250114_032611_1\",\n            \"20250114_032611_2\",\n            \"20250114_032612_3\",\n            \"20250114_032612_4\",\n            \"20250114_032612_5\",\n            \"20250114_032613_6\",\n            \"20250114_032613_7\",\n            \"20250114_032614_8\",\n            \"20250114_032614_9\",\n            \"20250114_032615_10\",\n            \"20250114_032615_11\",\n            \"20250114_032616_12\",\n            \"20250114_032616_13\",\n            \"20250114_032617_14\",\n            \"20250114_032617_15\",\n            \"20250114_032618_16\",\n            \"20250114_032618_17\",\n            \"20250114_032620_20\",\n            \"20250114_032621_23\",\n            \"20250114_032622_25\",\n            \"20250114_032622_26\",\n            \"20250114_032623_28\",\n            \"20250114_032624_29\",\n            \"20250114_032624_30\",\n            \"20250114_032625_31\",\n            \"20250114_032625_32\",\n            \"20250114_032626_33\",\n            \"20250114_032626_34\",\n            \"20250114_032627_35\",\n            \"20250114_032627_36\",\n            \"20250114_032628_37\",\n            \"20250114_032628_38\",\n            \"20250114_032629_39\",\n            \"20250114_032629_40\",\n            \"20250114_032630_41\",\n            \"20250114_032630_42\",\n            \"20250114_032631_43\",\n            \"20250114_032631_44\",\n            \"20250114_032631_45\",\n            \"20250114_032632_46\",\n            \"20250114_032632_47\"\n        ],\n        \"pos\": [\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                22.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                25.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                28.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                31.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                34.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                37.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                40.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                43.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                46.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                49.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                52.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                55.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                58.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                61.482939086907805\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                64.4829390869078\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                67.4829390869078\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                70.4829390869078\n            ],\n            [\n                1360.9649814508334,\n                -741.9448444291498,\n                73.4829390869078\n            ],\n            [\n                1363.9649814508334,\n                -747.1409968518565,\n                76.4829390869078\n            ],\n            [\n                1368.4649814508334,\n                -754.9352254859166,\n                76.4829390869078\n            ],\n            [\n                1371.4649814508334,\n                -760.1313779086233,\n                76.4829390869078\n            ],\n            [\n                1372.9649814508334,\n                -762.7294541199767,\n                76.4829390869078\n            ],\n            [\n                1375.5630576621868,\n                -764.2294541199767,\n                76.4829390869078\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                76.4829390869078\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                73.4829390869078\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                70.4829390869078\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                67.4829390869078\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                64.4829390869078\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                61.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                58.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                55.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                52.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                49.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                46.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                43.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                40.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                37.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                34.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                31.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                28.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                25.482939086907805\n            ],\n            [\n                1378.1611338735402,\n                -765.7294541199767,\n                22.482939086907805\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-11_1-32-26_1460674641\",\n        \"gpt_instruction\": \"Head straight toward a large white modern - style building with rectangular windows and a minimalist design . Then , slightly turn left and keep going straight toward another large white building characterized by a rectangular structure featuring vertical and horizontal lines on the fa\\u00e7ade .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_013230_2\",\n            \"20250111_013231_5\",\n            \"20250111_013233_8\",\n            \"20250111_013235_11\",\n            \"20250111_013236_14\",\n            \"20250111_013238_17\",\n            \"20250111_013239_20\",\n            \"20250111_013241_23\",\n            \"20250111_013243_26\",\n            \"20250111_013244_28\",\n            \"20250111_013244_29\",\n            \"20250111_013245_31\",\n            \"20250111_013246_32\"\n        ],\n        \"pos\": [\n            [\n                201.98762277281077,\n                74.61750475891279,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                83.61750475891279,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                92.61750475891279,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                101.61750475891279,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                110.61750475891279,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                119.61750475891279,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                128.6175047589128,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                137.6175047589128,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                146.6175047589128,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                152.6175047589128,\n                156.84788500608087\n            ],\n            [\n                201.98762277281077,\n                155.6175047589128,\n                156.84788500608087\n            ],\n            [\n                200.48762277281077,\n                158.2155809702661,\n                156.84788500608087\n            ],\n            [\n                198.98762277281077,\n                160.81365718161942,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-10_18-57-52_11671338\",\n        \"gpt_instruction\": \"\\\" Advance forward to a tall and wide white building characterized by vertical linear patterns with curved edges at the corners . Slightly turn left and proceed straight to it . \\\"\",\n        \"action\": [\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_185756_2\",\n            \"20250110_185758_5\",\n            \"20250110_185759_8\",\n            \"20250110_185800_10\",\n            \"20250110_185801_11\",\n            \"20250110_185802_14\",\n            \"20250110_185804_17\",\n            \"20250110_185806_20\",\n            \"20250110_185807_23\",\n            \"20250110_185809_26\",\n            \"20250110_185809_27\"\n        ],\n        \"pos\": [\n            [\n                321.36635405020075,\n                745.053231579717,\n                156.84788500608087\n            ],\n            [\n                325.86635405020075,\n                752.8474602137771,\n                156.84788500608087\n            ],\n            [\n                330.36635405020075,\n                760.6416888478373,\n                156.84788500608087\n            ],\n            [\n                333.36635405020075,\n                765.837841270544,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                768.4359174818974,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                774.4359174818974,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                783.4359174818974,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                792.4359174818974,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                801.4359174818974,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                810.4359174818974,\n                156.84788500608087\n            ],\n            [\n                334.86635405020075,\n                813.4359174818974,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_short/2025-1-6_20-26-54_278608944\",\n        \"gpt_instruction\": \"Proceed directly ahead to a large , dark grey building featuring Art Deco style with geometric patterns . Then , slightly turn left and head straight to a medium - high , dark brown office building exhibiting Art Deco style with decorative elements near the roofline , compared to surrounding structures .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_202658_2\",\n            \"20250106_202700_5\",\n            \"20250106_202702_8\",\n            \"20250106_202703_11\",\n            \"20250106_202705_14\",\n            \"20250106_202706_15\",\n            \"20250106_202706_16\",\n            \"20250106_202708_19\",\n            \"20250106_202709_20\"\n        ],\n        \"pos\": [\n            [\n                -1543.4738879073684,\n                696.1696632711771,\n                51.931139632995546\n            ],\n            [\n                -1538.9738879073684,\n                688.375434637117,\n                51.931139632995546\n            ],\n            [\n                -1534.4738879073684,\n                680.5812060030569,\n                51.931139632995546\n            ],\n            [\n                -1529.9738879073684,\n                672.7869773689968,\n                51.931139632995546\n            ],\n            [\n                -1525.4738879073684,\n                664.9927487349366,\n                51.931139632995546\n            ],\n            [\n                -1523.9738879073684,\n                662.3946725235833,\n                51.931139632995546\n            ],\n            [\n                -1522.4738879073684,\n                659.7965963122299,\n                51.931139632995546\n            ],\n            [\n                -1517.2777354846617,\n                656.7965963122299,\n                51.931139632995546\n            ],\n            [\n                -1514.6796592733083,\n                655.2965963122299,\n                51.931139632995546\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_short/2025-1-6_21-46-12_639854683\",\n        \"gpt_instruction\": \"Continue straight towards the large white building featuring a clock , then slightly turn left and proceed straight towards the tall , multi - story residential high - rise building with light beige color , featuring balconies with glass railings and numerous windows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_214616_2\",\n            \"20250106_214617_5\",\n            \"20250106_214619_8\",\n            \"20250106_214621_11\",\n            \"20250106_214623_14\",\n            \"20250106_214625_17\",\n            \"20250106_214625_18\",\n            \"20250106_214627_21\",\n            \"20250106_214629_24\",\n            \"20250106_214629_25\"\n        ],\n        \"pos\": [\n            [\n                1257.3134328310723,\n                -199.4145206456609,\n                68.82373033723795\n            ],\n            [\n                1249.5192041970122,\n                -203.9145206456609,\n                68.82373033723795\n            ],\n            [\n                1241.724975562952,\n                -208.4145206456609,\n                68.82373033723795\n            ],\n            [\n                1233.930746928892,\n                -212.9145206456609,\n                68.82373033723795\n            ],\n            [\n                1226.1365182948318,\n                -217.4145206456609,\n                68.82373033723795\n            ],\n            [\n                1218.3422896607717,\n                -221.9145206456609,\n                68.82373033723795\n            ],\n            [\n                1215.7442134494183,\n                -223.4145206456609,\n                68.82373033723795\n            ],\n            [\n                1212.7442134494183,\n                -228.61067306836753,\n                68.82373033723795\n            ],\n            [\n                1208.2442134494183,\n                -236.40490170242748,\n                68.82373033723795\n            ],\n            [\n                1206.7442134494183,\n                -239.0029779137808,\n                68.82373033723795\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/low_short/2025-1-1_22-49-14_1738110294\",\n        \"gpt_instruction\": \"Proceed directly ahead toward a large residential building characterized by a red - and - white color scheme , a brick exterior complemented by large windows and white detailing . Then , slightly turn left and move forward toward it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_224917_2\",\n            \"20250101_224919_5\",\n            \"20250101_224921_8\",\n            \"20250101_224922_11\",\n            \"20250101_224924_14\",\n            \"20250101_224926_17\",\n            \"20250101_224927_19\",\n            \"20250101_224927_20\",\n            \"20250101_224929_22\",\n            \"20250101_224929_23\"\n        ],\n        \"pos\": [\n            [\n                -1167.7073784585853,\n                -182.78465374639512,\n                32.836393838559744\n            ],\n            [\n                -1172.2073784585853,\n                -190.57888238045507,\n                32.836393838559744\n            ],\n            [\n                -1176.7073784585853,\n                -198.37311101451502,\n                32.836393838559744\n            ],\n            [\n                -1181.2073784585853,\n                -206.16733964857497,\n                32.836393838559744\n            ],\n            [\n                -1185.7073784585853,\n                -213.96156828263491,\n                32.836393838559744\n            ],\n            [\n                -1190.2073784585853,\n                -221.75579691669486,\n                32.836393838559744\n            ],\n            [\n                -1193.2073784585853,\n                -226.9519493394015,\n                32.836393838559744\n            ],\n            [\n                -1194.7073784585853,\n                -229.5500255507548,\n                32.836393838559744\n            ],\n            [\n                -1194.7073784585853,\n                -232.5500255507548,\n                32.836393838559744\n            ],\n            [\n                -1194.7073784585853,\n                -235.5500255507548,\n                32.836393838559744\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/low_short_updown/2025-1-14_0-28-39_1653377373\",\n        \"gpt_instruction\": \"Start by ascending towards the tall , large , brown and cream colored apartment building featuring a rectangular shape with multiple windows . Then , proceed forward and subtly veer to the left , heading straight to it . Finally , gently come to a halt and move down to it .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            1,\n            2,\n            9,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_002840_0\",\n            \"20250114_002843_1\",\n            \"20250114_002843_2\",\n            \"20250114_002844_3\",\n            \"20250114_002845_6\",\n            \"20250114_002847_9\",\n            \"20250114_002847_10\",\n            \"20250114_002848_11\",\n            \"20250114_002849_14\",\n            \"20250114_002850_16\",\n            \"20250114_002851_17\",\n            \"20250114_002851_18\",\n            \"20250114_002852_19\",\n            \"20250114_002852_20\",\n            \"20250114_002853_21\"\n        ],\n        \"pos\": [\n            [\n                -1008.5466236904982,\n                -1063.151219130607,\n                23.308114466109473\n            ],\n            [\n                -1008.5466236904982,\n                -1063.151219130607,\n                26.308114466109473\n            ],\n            [\n                -1008.5466236904982,\n                -1063.151219130607,\n                29.308114466109473\n            ],\n            [\n                -1008.5466236904982,\n                -1063.151219130607,\n                32.30811446610947\n            ],\n            [\n                -1014.5466236904982,\n                -1063.151219130607,\n                35.30811446610947\n            ],\n            [\n                -1023.5466236904982,\n                -1063.151219130607,\n                35.30811446610947\n            ],\n            [\n                -1026.546623690498,\n                -1063.151219130607,\n                35.30811446610947\n            ],\n            [\n                -1029.546623690498,\n                -1063.151219130607,\n                35.30811446610947\n            ],\n            [\n                -1034.7427761132049,\n                -1066.151219130607,\n                35.30811446610947\n            ],\n            [\n                -1039.9389285359116,\n                -1069.151219130607,\n                35.30811446610947\n            ],\n            [\n                -1042.537004747265,\n                -1070.651219130607,\n                35.30811446610947\n            ],\n            [\n                -1042.537004747265,\n                -1070.651219130607,\n                32.30811446610947\n            ],\n            [\n                -1042.537004747265,\n                -1070.651219130607,\n                29.308114466109473\n            ],\n            [\n                -1042.537004747265,\n                -1070.651219130607,\n                26.308114466109473\n            ],\n            [\n                -1042.537004747265,\n                -1070.651219130607,\n                23.308114466109473\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/high_average/2025-1-11_2-29-24_150203107\",\n        \"gpt_instruction\": \"Proceed straight toward the grey skyscraper characterized by a modern design with a curved glass fa\\u00e7ade and towering high - rise profile . It slightly curves right as you continue forward to it . Turn slightly left to advance towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_022928_2\",\n            \"20250111_022930_5\",\n            \"20250111_022931_8\",\n            \"20250111_022933_11\",\n            \"20250111_022934_14\",\n            \"20250111_022936_17\",\n            \"20250111_022936_18\",\n            \"20250111_022937_19\",\n            \"20250111_022938_22\",\n            \"20250111_022940_25\",\n            \"20250111_022942_28\",\n            \"20250111_022943_31\",\n            \"20250111_022944_32\",\n            \"20250111_022944_33\",\n            \"20250111_022946_36\",\n            \"20250111_022946_37\"\n        ],\n        \"pos\": [\n            [\n                145.52600373878926,\n                -246.72493214916284,\n                156.84788500608087\n            ],\n            [\n                150.02600373878926,\n                -238.9307035151029,\n                156.84788500608087\n            ],\n            [\n                154.52600373878926,\n                -231.13647488104294,\n                156.84788500608087\n            ],\n            [\n                159.02600373878926,\n                -223.342246246983,\n                156.84788500608087\n            ],\n            [\n                163.52600373878926,\n                -215.54801761292305,\n                156.84788500608087\n            ],\n            [\n                168.02600373878926,\n                -207.7537889788631,\n                156.84788500608087\n            ],\n            [\n                169.52600373878926,\n                -205.15571276750978,\n                156.84788500608087\n            ],\n            [\n                171.02600373878926,\n                -202.55763655615647,\n                156.84788500608087\n            ],\n            [\n                176.2221561614959,\n                -199.55763655615647,\n                156.84788500608087\n            ],\n            [\n                184.01638479555584,\n                -195.05763655615647,\n                156.84788500608087\n            ],\n            [\n                191.81061342961578,\n                -190.55763655615647,\n                156.84788500608087\n            ],\n            [\n                199.60484206367573,\n                -186.05763655615647,\n                156.84788500608087\n            ],\n            [\n                202.20291827502905,\n                -184.55763655615647,\n                156.84788500608087\n            ],\n            [\n                204.80099448638236,\n                -183.05763655615647,\n                156.84788500608087\n            ],\n            [\n                207.80099448638236,\n                -177.86148413344984,\n                156.84788500608087\n            ],\n            [\n                209.30099448638236,\n                -175.26340792209652,\n                156.84788500608087\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_long/2025-1-7_19-4-58_1929465979\",\n        \"gpt_instruction\": \"Proceed straight toward the white skyscraper with very large vertical striped windows . Slightly turn right and move forward to the beige high - rise building , which features multiple balconies and numerous windows , identified as a large apartment building . Then , slightly turn left and continue straight to reach the white residential skyscraper , distinguished by its tall and narrow high - rise structure with vertical rows of recessed balconies featuring glass panel railings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_190502_2\",\n            \"20250107_190503_5\",\n            \"20250107_190505_8\",\n            \"20250107_190506_11\",\n            \"20250107_190508_14\",\n            \"20250107_190509_17\",\n            \"20250107_190511_20\",\n            \"20250107_190512_23\",\n            \"20250107_190513_26\",\n            \"20250107_190515_28\",\n            \"20250107_190515_29\",\n            \"20250107_190516_32\",\n            \"20250107_190518_35\",\n            \"20250107_190520_38\",\n            \"20250107_190521_41\",\n            \"20250107_190522_42\",\n            \"20250107_190523_44\",\n            \"20250107_190523_45\"\n        ],\n        \"pos\": [\n            [\n                989.5092995844109,\n                -1570.3606760566074,\n                55.99601567802139\n            ],\n            [\n                985.0092995844109,\n                -1578.1549046906675,\n                55.99601567802139\n            ],\n            [\n                980.5092995844109,\n                -1585.9491333247277,\n                55.99601567802139\n            ],\n            [\n                976.0092995844109,\n                -1593.7433619587878,\n                55.99601567802139\n            ],\n            [\n                971.5092995844109,\n                -1601.537590592848,\n                55.99601567802139\n            ],\n            [\n                967.0092995844109,\n                -1609.331819226908,\n                55.99601567802139\n            ],\n            [\n                962.5092995844109,\n                -1617.1260478609681,\n                55.99601567802139\n            ],\n            [\n                958.0092995844109,\n                -1624.9202764950282,\n                55.99601567802139\n            ],\n            [\n                953.5092995844109,\n                -1632.7145051290884,\n                55.99601567802139\n            ],\n            [\n                950.5092995844109,\n                -1637.910657551795,\n                55.99601567802139\n            ],\n            [\n                949.0092995844109,\n                -1640.5087337631485,\n                55.99601567802139\n            ],\n            [\n                943.8131471617041,\n                -1643.5087337631485,\n                55.99601567802139\n            ],\n            [\n                936.018918527644,\n                -1648.0087337631485,\n                55.99601567802139\n            ],\n            [\n                928.2246898935839,\n                -1652.5087337631485,\n                55.99601567802139\n            ],\n            [\n                920.4304612595238,\n                -1657.0087337631485,\n                55.99601567802139\n            ],\n            [\n                917.8323850481704,\n                -1658.5087337631485,\n                55.99601567802139\n            ],\n            [\n                916.3323850481704,\n                -1661.1068099745019,\n                55.99601567802139\n            ],\n            [\n                914.8323850481704,\n                -1663.7048861858552,\n                55.99601567802139\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_sh/astar_data/medium_average_updown/2025-1-14_3-48-13_155789224\",\n        \"gpt_instruction\": \"Begin by ascending to the white and brown high - rise building characterized by its vertical stripes and balconies , which is a large residential skyscraper . Then , proceed forward to it . Head straight towards the orange and white skyscraper featuring distinct horizontal lines and move straight to a tall office building of light gray adorned with vertical window lines . Next , turn left toward an orange and white residential high - rise with rooftop trees . Continue in a straight path to a brown residential building with multiple vertical stripes on its facade and go directly ahead to a brown building with a rectangular shape and blue windows . Finally , slightly halt and move downwards towards a medium - sized orange residential building distinguished by horizontal white lines and blue windows .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            8,\n            3,\n            8,\n            3,\n            8,\n            2,\n            2,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_034813_0\",\n            \"20250114_034816_1\",\n            \"20250114_034817_2\",\n            \"20250114_034818_3\",\n            \"20250114_034818_4\",\n            \"20250114_034819_5\",\n            \"20250114_034819_6\",\n            \"20250114_034820_7\",\n            \"20250114_034820_8\",\n            \"20250114_034821_9\",\n            \"20250114_034821_10\",\n            \"20250114_034822_11\",\n            \"20250114_034822_12\",\n            \"20250114_034823_14\",\n            \"20250114_034824_15\",\n            \"20250114_034825_17\",\n            \"20250114_034825_18\",\n            \"20250114_034826_20\",\n            \"20250114_034827_21\",\n            \"20250114_034828_22\",\n            \"20250114_034829_25\",\n            \"20250114_034832_28\",\n            \"20250114_034833_31\",\n            \"20250114_034835_34\",\n            \"20250114_034837_36\",\n            \"20250114_034837_37\",\n            \"20250114_034839_40\",\n            \"20250114_034841_43\",\n            \"20250114_034843_46\",\n            \"20250114_034845_49\",\n            \"20250114_034847_52\",\n            \"20250114_034848_53\",\n            \"20250114_034849_54\",\n            \"20250114_034849_55\",\n            \"20250114_034850_56\",\n            \"20250114_034850_57\",\n            \"20250114_034851_58\",\n            \"20250114_034851_59\",\n            \"20250114_034852_60\",\n            \"20250114_034852_61\",\n            \"20250114_034853_62\",\n            \"20250114_034853_63\",\n            \"20250114_034854_64\",\n            \"20250114_034854_65\",\n            \"20250114_034855_66\"\n        ],\n        \"pos\": [\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                23.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                26.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                29.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                32.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                35.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                38.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                41.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                44.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                47.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                50.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                53.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                56.215125399936845\n            ],\n            [\n                1656.9879176960242,\n                -1375.1445922454573,\n                59.215125399936845\n            ],\n            [\n                1654.3898414846708,\n                -1373.6445922454573,\n                62.215125399936845\n            ],\n            [\n                1651.7917652733174,\n                -1372.1445922454573,\n                62.215125399936845\n            ],\n            [\n                1650.2917652733174,\n                -1369.546516034104,\n                62.215125399936845\n            ],\n            [\n                1648.7917652733174,\n                -1366.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1648.7917652733174,\n                -1363.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1648.7917652733174,\n                -1360.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1648.7917652733174,\n                -1360.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1643.5956128506107,\n                -1357.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1635.8013842165506,\n                -1353.4484398227505,\n                62.215125399936845\n            ],\n            [\n                1628.0071555824904,\n                -1348.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1620.2129269484303,\n                -1344.4484398227505,\n                62.215125399936845\n            ],\n            [\n                1615.0167745257236,\n                -1341.4484398227505,\n                62.215125399936845\n            ],\n            [\n                1612.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1606.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1597.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1588.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1579.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1570.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                62.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                59.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                56.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                53.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                50.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                47.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                44.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                41.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                38.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                35.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                32.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                29.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                26.215125399936845\n            ],\n            [\n                1567.4186983143702,\n                -1339.9484398227505,\n                23.215125399936845\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_average/2025-1-11_3-19-23_1279820114\",\n        \"gpt_instruction\": \"Proceed towards the large , tall building distinguished by long vertical light streaks in grey and white , then veer right to reach the expansive open public space characterized by a paved surface in gray . Next , advance forward into the vast area featuring a grid - patterned paved surface , and slightly turn left to move ahead towards another large building with vertical light streaks , this time exhibiting a white color .\",\n        \"action\": [\n            9,\n            9,\n            3,\n            3,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_031927_2\",\n            \"20250111_031929_5\",\n            \"20250111_031929_6\",\n            \"20250111_031930_7\",\n            \"20250111_031931_10\",\n            \"20250111_031932_11\",\n            \"20250111_031933_14\",\n            \"20250111_031935_17\",\n            \"20250111_031935_18\"\n        ],\n        \"pos\": [\n            [\n                871.9997975239404,\n                -1421.4910553945256,\n                131.6344676312703\n            ],\n            [\n                880.9997975239404,\n                -1421.4910553945256,\n                131.6344676312703\n            ],\n            [\n                883.9997975239404,\n                -1421.4910553945256,\n                131.6344676312703\n            ],\n            [\n                883.9997975239404,\n                -1421.4910553945256,\n                131.6344676312703\n            ],\n            [\n                886.9997975239404,\n                -1426.6872078172323,\n                131.6344676312703\n            ],\n            [\n                888.4997975239404,\n                -1429.2852840285857,\n                131.6344676312703\n            ],\n            [\n                893.695949946647,\n                -1432.2852840285857,\n                131.6344676312703\n            ],\n            [\n                901.4901785807072,\n                -1436.7852840285857,\n                131.6344676312703\n            ],\n            [\n                904.0882547920605,\n                -1438.2852840285857,\n                131.6344676312703\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_short_updown/2025-1-14_1-8-37_1432114613\",\n        \"gpt_instruction\": \"Ascend to a gray apartment building with large rectangular windows , proceed towards a dark grey apartment block characterized by its tall residential structure , and then slightly descend to a location distinguished by its dark grey color , tall size , and rectangular windows , identifiable as a residential tower .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_010837_0\",\n            \"20250114_010840_1\",\n            \"20250114_010841_2\",\n            \"20250114_010841_3\",\n            \"20250114_010842_4\",\n            \"20250114_010842_5\",\n            \"20250114_010843_6\",\n            \"20250114_010843_7\",\n            \"20250114_010843_8\",\n            \"20250114_010845_11\",\n            \"20250114_010845_12\",\n            \"20250114_010846_13\",\n            \"20250114_010846_14\",\n            \"20250114_010847_15\",\n            \"20250114_010847_16\",\n            \"20250114_010848_17\",\n            \"20250114_010848_18\",\n            \"20250114_010848_19\",\n            \"20250114_010849_20\",\n            \"20250114_010849_21\",\n            \"20250114_010850_22\"\n        ],\n        \"pos\": [\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                11.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                14.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                17.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                20.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                23.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                26.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                29.240699457872743\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                32.24069945787274\n            ],\n            [\n                -1492.8115298759117,\n                559.7320005105503,\n                35.24069945787274\n            ],\n            [\n                -1495.8115298759117,\n                554.5358480878435,\n                38.24069945787274\n            ],\n            [\n                -1497.3115298759117,\n                551.9377718764902,\n                38.24069945787274\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                38.24069945787274\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                35.24069945787274\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                32.24069945787274\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                29.240699457872743\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                26.240699457872743\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                23.240699457872743\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                20.240699457872743\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                17.240699457872743\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                14.240699457872743\n            ],\n            [\n                -1498.8115298759117,\n                549.3396956651368,\n                11.240699457872743\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_average/2025-1-1_22-17-19_1900553541\",\n        \"gpt_instruction\": \"Proceed straight to the large dark parking structure with multiple levels , then slightly turn left and move ahead to the tall dark blue modern office building featuring repetitive horizontal and vertical patterns .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_221723_2\",\n            \"20250101_221724_5\",\n            \"20250101_221726_8\",\n            \"20250101_221728_11\",\n            \"20250101_221729_14\",\n            \"20250101_221731_17\",\n            \"20250101_221733_20\",\n            \"20250101_221734_23\",\n            \"20250101_221736_26\",\n            \"20250101_221737_28\",\n            \"20250101_221737_29\",\n            \"20250101_221739_32\",\n            \"20250101_221740_35\",\n            \"20250101_221742_38\",\n            \"20250101_221743_41\",\n            \"20250101_221745_44\",\n            \"20250101_221745_45\",\n            \"20250101_221746_46\"\n        ],\n        \"pos\": [\n            [\n                350.81280245221865,\n                989.3668234980504,\n                14.093679782636976\n            ],\n            [\n                343.0185738181587,\n                984.8668234980504,\n                14.093679782636976\n            ],\n            [\n                335.22434518409875,\n                980.3668234980504,\n                14.093679782636976\n            ],\n            [\n                327.4301165500388,\n                975.8668234980504,\n                14.093679782636976\n            ],\n            [\n                319.63588791597886,\n                971.3668234980504,\n                14.093679782636976\n            ],\n            [\n                311.8416592819189,\n                966.8668234980504,\n                14.093679782636976\n            ],\n            [\n                304.04743064785896,\n                962.3668234980504,\n                14.093679782636976\n            ],\n            [\n                296.253202013799,\n                957.8668234980504,\n                14.093679782636976\n            ],\n            [\n                288.45897337973906,\n                953.3668234980504,\n                14.093679782636976\n            ],\n            [\n                283.26282095703243,\n                950.3668234980504,\n                14.093679782636976\n            ],\n            [\n                280.6647447456791,\n                948.8668234980504,\n                14.093679782636976\n            ],\n            [\n                277.6647447456791,\n                943.6706710753438,\n                14.093679782636976\n            ],\n            [\n                273.1647447456791,\n                935.8764424412836,\n                14.093679782636976\n            ],\n            [\n                268.6647447456791,\n                928.0822138072235,\n                14.093679782636976\n            ],\n            [\n                264.1647447456791,\n                920.2879851731634,\n                14.093679782636976\n            ],\n            [\n                259.6647447456791,\n                912.4937565391033,\n                14.093679782636976\n            ],\n            [\n                258.1647447456791,\n                909.8956803277499,\n                14.093679782636976\n            ],\n            [\n                256.6647447456791,\n                907.2976041163965,\n                14.093679782636976\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_average/2025-1-11_4-54-8_1823025015\",\n        \"gpt_instruction\": \"Advance forward to a large blue building adorned with illuminated white vertical stripes , featuring a modern high - rise appearance and vertical striped lighting . Then , slightly turn left and walk straight toward it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_045411_2\",\n            \"20250111_045413_5\",\n            \"20250111_045414_8\",\n            \"20250111_045416_11\",\n            \"20250111_045417_14\",\n            \"20250111_045419_17\",\n            \"20250111_045419_18\",\n            \"20250111_045421_21\",\n            \"20250111_045422_24\",\n            \"20250111_045424_27\",\n            \"20250111_045426_30\",\n            \"20250111_045427_32\",\n            \"20250111_045427_33\"\n        ],\n        \"pos\": [\n            [\n                -1120.7824162193044,\n                137.21640819375787,\n                131.6344676312703\n            ],\n            [\n                -1116.2824162193044,\n                129.42217955969792,\n                131.6344676312703\n            ],\n            [\n                -1111.7824162193044,\n                121.62795092563798,\n                131.6344676312703\n            ],\n            [\n                -1107.2824162193044,\n                113.83372229157803,\n                131.6344676312703\n            ],\n            [\n                -1102.7824162193044,\n                106.03949365751808,\n                131.6344676312703\n            ],\n            [\n                -1098.2824162193044,\n                98.24526502345813,\n                131.6344676312703\n            ],\n            [\n                -1096.7824162193044,\n                95.64718881210482,\n                131.6344676312703\n            ],\n            [\n                -1091.5862637965977,\n                92.64718881210482,\n                131.6344676312703\n            ],\n            [\n                -1083.7920351625376,\n                88.14718881210482,\n                131.6344676312703\n            ],\n            [\n                -1075.9978065284774,\n                83.64718881210482,\n                131.6344676312703\n            ],\n            [\n                -1068.2035778944173,\n                79.14718881210482,\n                131.6344676312703\n            ],\n            [\n                -1063.0074254717106,\n                76.14718881210482,\n                131.6344676312703\n            ],\n            [\n                -1060.4093492603572,\n                74.64718881210482,\n                131.6344676312703\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_average/2025-1-7_6-13-46_1906423611\",\n        \"gpt_instruction\": \"Proceed straight to a large , modern high - rise commercial building with a gray facade featuring horizontal white lighting and then slightly turn right . Continue straight towards a medium - sized commercial or office building that features blue and white horizontal stripes and an emblem - like design on its side , which stands out compared to the surrounding structures .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_061350_2\",\n            \"20250107_061351_5\",\n            \"20250107_061353_8\",\n            \"20250107_061354_11\",\n            \"20250107_061355_14\",\n            \"20250107_061357_17\",\n            \"20250107_061358_20\",\n            \"20250107_061359_21\",\n            \"20250107_061359_22\",\n            \"20250107_061400_25\",\n            \"20250107_061401_26\",\n            \"20250107_061401_27\"\n        ],\n        \"pos\": [\n            [\n                -2129.5137888365807,\n                -1279.448894295511,\n                48.37802515399777\n            ],\n            [\n                -2134.0137888365807,\n                -1271.654665661451,\n                48.37802515399777\n            ],\n            [\n                -2138.5137888365807,\n                -1263.8604370273908,\n                48.37802515399777\n            ],\n            [\n                -2143.0137888365807,\n                -1256.0662083933307,\n                48.37802515399777\n            ],\n            [\n                -2147.5137888365807,\n                -1248.2719797592706,\n                48.37802515399777\n            ],\n            [\n                -2152.0137888365807,\n                -1240.4777511252105,\n                48.37802515399777\n            ],\n            [\n                -2156.5137888365807,\n                -1232.6835224911504,\n                48.37802515399777\n            ],\n            [\n                -2158.0137888365807,\n                -1230.085446279797,\n                48.37802515399777\n            ],\n            [\n                -2159.5137888365807,\n                -1227.4873700684436,\n                48.37802515399777\n            ],\n            [\n                -2159.5137888365807,\n                -1221.4873700684436,\n                48.37802515399777\n            ],\n            [\n                -2159.5137888365807,\n                -1218.4873700684436,\n                48.37802515399777\n            ],\n            [\n                -2159.5137888365807,\n                -1215.4873700684436,\n                48.37802515399777\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_average/2025-1-7_6-53-31_1354575023\",\n        \"gpt_instruction\": \"Advance forward to a medium - sized gray building characterized by a flat rooftop with a grid - like panel design . Slightly turn left and proceed straight toward a large white skyscraper with illuminated highlights and a distinct spire on top . Shift right to a large white building patterned with a grid design . Move ahead to a large blue building featuring reflective grid - like windows . Slightly turn left and move forward again to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            3,\n            8,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_065335_2\",\n            \"20250107_065336_5\",\n            \"20250107_065337_8\",\n            \"20250107_065338_9\",\n            \"20250107_065338_10\",\n            \"20250107_065340_13\",\n            \"20250107_065341_16\",\n            \"20250107_065343_19\",\n            \"20250107_065344_22\",\n            \"20250107_065345_23\",\n            \"20250107_065345_24\",\n            \"20250107_065346_25\",\n            \"20250107_065347_27\",\n            \"20250107_065347_28\",\n            \"20250107_065348_31\",\n            \"20250107_065349_33\",\n            \"20250107_065350_34\"\n        ],\n        \"pos\": [\n            [\n                3135.2787121521064,\n                -57.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3127.484483518047,\n                -52.58548160827021,\n                43.87781837101636\n            ],\n            [\n                3119.6902548839876,\n                -48.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3117.0921786726344,\n                -46.58548160827021,\n                43.87781837101636\n            ],\n            [\n                3114.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3108.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3099.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3090.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3081.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3078.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3075.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3075.4941024612813,\n                -45.08548160827021,\n                43.87781837101636\n            ],\n            [\n                3073.9941024612813,\n                -42.48740539691689,\n                43.87781837101636\n            ],\n            [\n                3072.4941024612813,\n                -39.889329185563575,\n                43.87781837101636\n            ],\n            [\n                3067.297950038575,\n                -36.889329185563575,\n                43.87781837101636\n            ],\n            [\n                3062.1017976158687,\n                -33.889329185563575,\n                43.87781837101636\n            ],\n            [\n                3059.5037214045155,\n                -32.389329185563575,\n                43.87781837101636\n            ]\n        ],\n        \"yaw\": [\n            2.617993877991466,\n            2.617993877991466,\n            2.617993877991466,\n            2.617993877991466,\n            2.617993877991466,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.617993877991466,\n            2.617993877991466,\n            2.617993877991466\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_average/2025-1-2_21-52-32_382926234\",\n        \"gpt_instruction\": \"Continue straight until you reach a dark modern multi - story high - rise featuring distinct grid - like windows and large dimensions , then veer right towards a blue office building with a grid - like window pattern . Proceed straight to it . Slightly turn left and walk straight towards it . Slightly turn right and move forward to it . Slightly turn right and head straight towards it, then slightly turn left and move forward to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            3,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            3,\n            9,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_215235_2\",\n            \"20250102_215237_5\",\n            \"20250102_215238_8\",\n            \"20250102_215239_11\",\n            \"20250102_215240_14\",\n            \"20250102_215242_17\",\n            \"20250102_215243_20\",\n            \"20250102_215245_23\",\n            \"20250102_215246_26\",\n            \"20250102_215246_27\",\n            \"20250102_215247_28\",\n            \"20250102_215248_31\",\n            \"20250102_215249_33\",\n            \"20250102_215250_34\",\n            \"20250102_215251_37\",\n            \"20250102_215252_40\",\n            \"20250102_215254_43\",\n            \"20250102_215254_45\",\n            \"20250102_215255_46\",\n            \"20250102_215256_49\",\n            \"20250102_215257_50\",\n            \"20250102_215258_53\",\n            \"20250102_215259_54\",\n            \"20250102_215259_55\",\n            \"20250102_215300_56\"\n        ],\n        \"pos\": [\n            [\n                3975.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                3984.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                3993.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4002.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4011.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4020.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4029.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4038.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4047.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4050.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4050.801071107755,\n                455.574205409804,\n                18.789541262554774\n            ],\n            [\n                4053.801071107755,\n                450.37805298709736,\n                18.789541262554774\n            ],\n            [\n                4056.801071107755,\n                445.18190056439073,\n                18.789541262554774\n            ],\n            [\n                4058.301071107755,\n                442.5838243530374,\n                18.789541262554774\n            ],\n            [\n                4063.4972235304613,\n                439.5838243530374,\n                18.789541262554774\n            ],\n            [\n                4071.2914521645207,\n                435.0838243530374,\n                18.789541262554774\n            ],\n            [\n                4079.08568079858,\n                430.5838243530374,\n                18.789541262554774\n            ],\n            [\n                4084.2818332212864,\n                427.5838243530374,\n                18.789541262554774\n            ],\n            [\n                4086.8799094326396,\n                426.0838243530374,\n                18.789541262554774\n            ],\n            [\n                4089.8799094326396,\n                420.8876719303308,\n                18.789541262554774\n            ],\n            [\n                4091.3799094326396,\n                418.28959571897747,\n                18.789541262554774\n            ],\n            [\n                4091.3799094326396,\n                412.28959571897747,\n                18.789541262554774\n            ],\n            [\n                4091.3799094326396,\n                409.28959571897747,\n                18.789541262554774\n            ],\n            [\n                4091.3799094326396,\n                409.28959571897747,\n                18.789541262554774\n            ],\n            [\n                4092.8799094326396,\n                406.69151950762415,\n                18.789541262554774\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -0.5235987755983272,\n            -0.5235987755983272,\n            -0.5235987755983272,\n            -0.5235987755983272,\n            -0.5235987755983272,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_long/2025-1-7_4-7-36_335558628\",\n        \"gpt_instruction\": \"Move forward to a large , dark gray apartment building characterized by grid - like windows divided horizontally and vertically . Then , slightly turn right and move ahead to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_040740_2\",\n            \"20250107_040741_5\",\n            \"20250107_040742_8\",\n            \"20250107_040744_11\",\n            \"20250107_040745_14\",\n            \"20250107_040746_16\",\n            \"20250107_040747_17\",\n            \"20250107_040748_20\",\n            \"20250107_040749_23\",\n            \"20250107_040751_26\",\n            \"20250107_040752_29\",\n            \"20250107_040753_30\",\n            \"20250107_040753_31\"\n        ],\n        \"pos\": [\n            [\n                948.2031544776402,\n                215.40665632591356,\n                65.50053373794236\n            ],\n            [\n                943.7031544776402,\n                223.2008849599735,\n                65.50053373794236\n            ],\n            [\n                939.2031544776402,\n                230.99511359403346,\n                65.50053373794236\n            ],\n            [\n                934.7031544776402,\n                238.7893422280934,\n                65.50053373794236\n            ],\n            [\n                930.2031544776402,\n                246.58357086215335,\n                65.50053373794236\n            ],\n            [\n                927.2031544776402,\n                251.77972328485998,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                254.3777994962133,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                260.3777994962133,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                269.3777994962133,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                278.3777994962133,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                287.3777994962133,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                290.3777994962133,\n                65.50053373794236\n            ],\n            [\n                925.7031544776402,\n                293.3777994962133,\n                65.50053373794236\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_short_updown/2025-1-14_4-9-55_1215828993\",\n        \"gpt_instruction\": \"Start by moving upwards towards a grey residential building with a striped design featuring white balconies . Then , proceed straight to another large building , this one characterized by a dark gray color and horizontal stripe patterns . Slightly turn left and continue straight to a grey residential building with a concrete facade adorned with horizontal lines . Finally , slightly pause and drop by a skyscraper , notable for its grey color with distinct lighter and darker shades , and a zigzag striped pattern with vertical lines , towering over most of the image 's right side .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            8,\n            2,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_040956_0\",\n            \"20250114_040959_1\",\n            \"20250114_040959_2\",\n            \"20250114_040959_3\",\n            \"20250114_041000_4\",\n            \"20250114_041000_5\",\n            \"20250114_041001_6\",\n            \"20250114_041001_7\",\n            \"20250114_041002_8\",\n            \"20250114_041003_11\",\n            \"20250114_041004_14\",\n            \"20250114_041006_17\",\n            \"20250114_041007_19\",\n            \"20250114_041007_20\",\n            \"20250114_041008_22\",\n            \"20250114_041009_23\",\n            \"20250114_041009_24\",\n            \"20250114_041010_25\",\n            \"20250114_041010_26\",\n            \"20250114_041010_27\",\n            \"20250114_041011_28\",\n            \"20250114_041011_29\",\n            \"20250114_041012_30\",\n            \"20250114_041012_31\",\n            \"20250114_041013_32\"\n        ],\n        \"pos\": [\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                11.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                14.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                17.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                20.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                23.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                26.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                29.240699457872743\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                32.24069945787274\n            ],\n            [\n                1661.2360410296494,\n                -412.69928183987554,\n                35.24069945787274\n            ],\n            [\n                1661.2360410296494,\n                -406.69928183987554,\n                38.24069945787274\n            ],\n            [\n                1661.2360410296494,\n                -397.69928183987554,\n                38.24069945787274\n            ],\n            [\n                1661.2360410296494,\n                -388.69928183987554,\n                38.24069945787274\n            ],\n            [\n                1661.2360410296494,\n                -382.69928183987554,\n                38.24069945787274\n            ],\n            [\n                1661.2360410296494,\n                -379.69928183987554,\n                38.24069945787274\n            ],\n            [\n                1659.7360410296494,\n                -377.1012056285222,\n                38.24069945787274\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                38.24069945787274\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                35.24069945787274\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                32.24069945787274\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                29.240699457872743\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                26.240699457872743\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                23.240699457872743\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                20.240699457872743\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                17.240699457872743\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                14.240699457872743\n            ],\n            [\n                1658.2360410296494,\n                -374.5031294171689,\n                11.240699457872743\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_short/2025-1-6_16-19-46_1466942491\",\n        \"gpt_instruction\": \"Move forward to a structure characterized by a beige hue that combines a soft glow emanating from its windows , featuring a horizontal striped pattern on its fa\\u00e7ade and illuminated rectangular windows . This tall and wide building , dominating the central part of the scene , exemplifies a modern high - rise design .\",\n        \"action\": [\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_161949_2\",\n            \"20250106_161950_3\"\n        ],\n        \"pos\": [\n            [\n                -254.98348049621612,\n                -2691.127128483303,\n                53.28564612900604\n            ],\n            [\n                -257.58155670756946,\n                -2689.627128483303,\n                53.28564612900604\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_long/2025-1-2_23-7-14_358625421\",\n        \"gpt_instruction\": \"Head straight toward the large , dark gray residential building characterized by its tall structure with vertical windows . Then , slightly turn right and move forward to reach the tall and narrow dark blue building that appears to have small protruding frames on its surface , which may function as a residential or commercial building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_230718_2\",\n            \"20250102_230719_5\",\n            \"20250102_230720_8\",\n            \"20250102_230722_11\",\n            \"20250102_230723_14\",\n            \"20250102_230724_17\",\n            \"20250102_230725_20\",\n            \"20250102_230727_23\",\n            \"20250102_230728_26\",\n            \"20250102_230729_29\",\n            \"20250102_230731_32\",\n            \"20250102_230732_35\",\n            \"20250102_230734_38\",\n            \"20250102_230735_41\",\n            \"20250102_230736_44\",\n            \"20250102_230737_46\",\n            \"20250102_230738_47\",\n            \"20250102_230739_50\",\n            \"20250102_230739_51\",\n            \"20250102_230740_52\"\n        ],\n        \"pos\": [\n            [\n                4849.547460453452,\n                1245.4375811708042,\n                22.50176068530714\n            ],\n            [\n                4854.047460453452,\n                1237.643352536744,\n                22.50176068530714\n            ],\n            [\n                4858.547460453452,\n                1229.849123902684,\n                22.50176068530714\n            ],\n            [\n                4863.047460453452,\n                1222.0548952686238,\n                22.50176068530714\n            ],\n            [\n                4867.547460453452,\n                1214.2606666345637,\n                22.50176068530714\n            ],\n            [\n                4872.047460453452,\n                1206.4664380005036,\n                22.50176068530714\n            ],\n            [\n                4876.547460453452,\n                1198.6722093664434,\n                22.50176068530714\n            ],\n            [\n                4881.047460453452,\n                1190.8779807323833,\n                22.50176068530714\n            ],\n            [\n                4885.547460453452,\n                1183.0837520983232,\n                22.50176068530714\n            ],\n            [\n                4890.047460453452,\n                1175.289523464263,\n                22.50176068530714\n            ],\n            [\n                4894.547460453452,\n                1167.495294830203,\n                22.50176068530714\n            ],\n            [\n                4899.047460453452,\n                1159.7010661961428,\n                22.50176068530714\n            ],\n            [\n                4903.547460453452,\n                1151.9068375620827,\n                22.50176068530714\n            ],\n            [\n                4908.047460453452,\n                1144.1126089280226,\n                22.50176068530714\n            ],\n            [\n                4912.547460453452,\n                1136.3183802939625,\n                22.50176068530714\n            ],\n            [\n                4915.547460453452,\n                1131.1222278712557,\n                22.50176068530714\n            ],\n            [\n                4917.047460453452,\n                1128.5241516599024,\n                22.50176068530714\n            ],\n            [\n                4917.047460453452,\n                1122.5241516599024,\n                22.50176068530714\n            ],\n            [\n                4917.047460453452,\n                1119.5241516599024,\n                22.50176068530714\n            ],\n            [\n                4917.047460453452,\n                1116.5241516599024,\n                22.50176068530714\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_short/2025-1-7_6-7-57_1532530123\",\n        \"gpt_instruction\": \"Move forward toward a medium - high building with a bright , illuminated facade and a modern design , lit up in white . Then , make a left turn toward a tall , green building with vertical , lighted windows and a rectangular shape . Continue straight toward it . Slightly turn right , and head straight toward another tall green structure , this one cylindrical in shape with an antenna on top , functioning as an office building .\",\n        \"action\": [\n            9,\n            8,\n            2,\n            2,\n            9,\n            8,\n            3,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_060801_2\",\n            \"20250107_060802_4\",\n            \"20250107_060802_5\",\n            \"20250107_060803_6\",\n            \"20250107_060804_9\",\n            \"20250107_060805_11\",\n            \"20250107_060805_12\",\n            \"20250107_060807_15\",\n            \"20250107_060808_18\",\n            \"20250107_060809_19\",\n            \"20250107_060809_20\"\n        ],\n        \"pos\": [\n            [\n                -17.37924602932324,\n                1333.4030696419186,\n                56.8698709162838\n            ],\n            [\n                -11.379246029323241,\n                1333.4030696419186,\n                56.8698709162838\n            ],\n            [\n                -8.379246029323241,\n                1333.4030696419186,\n                56.8698709162838\n            ],\n            [\n                -8.379246029323241,\n                1333.4030696419186,\n                56.8698709162838\n            ],\n            [\n                -5.379246029323241,\n                1338.5992220646253,\n                56.8698709162838\n            ],\n            [\n                -2.37924602932324,\n                1343.795374487332,\n                56.8698709162838\n            ],\n            [\n                -0.8792460293232396,\n                1346.3934506986855,\n                56.8698709162838\n            ],\n            [\n                4.316906393383393,\n                1349.3934506986855,\n                56.8698709162838\n            ],\n            [\n                12.111135027443341,\n                1353.8934506986855,\n                56.8698709162838\n            ],\n            [\n                14.709211238796657,\n                1355.3934506986855,\n                56.8698709162838\n            ],\n            [\n                17.30728745014997,\n                1356.8934506986855,\n                56.8698709162838\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_short/2025-1-7_3-46-3_1923933022\",\n        \"gpt_instruction\": \"Proceed straight towards the illuminated skyscraper , which is tall and bathed in bright lights .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_034606_2\",\n            \"20250107_034607_5\",\n            \"20250107_034609_8\",\n            \"20250107_034610_11\",\n            \"20250107_034611_14\",\n            \"20250107_034612_15\",\n            \"20250107_034612_16\"\n        ],\n        \"pos\": [\n            [\n                4761.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ],\n            [\n                4770.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ],\n            [\n                4779.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ],\n            [\n                4788.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ],\n            [\n                4797.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ],\n            [\n                4800.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ],\n            [\n                4803.213471709481,\n                -1736.3590486193418,\n                55.812851761851405\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_short_updown/2025-1-14_18-22-10_771476364\",\n        \"gpt_instruction\": \"\\\" Move upward to a tall , modern skyscraper characterized by a white color , an illuminated rectangular shape with vertical light patterns . Then , slightly shift to the right , heading toward a large modern high - rise building distinguished by its silver and blue shades , grid - like glass panels with illuminated horizontal lights . Next , continue forward to a large modern office building featuring gray hues with illuminated white lights , a glass facade with reflective surfaces , and linear light patterns . Finally , stop slightly and descend toward a tall , wide , modern skyscraper with a dark color accented by illuminated light strips , a central dark area with a glass facade , and horizontal light strips . \\\"\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            1,\n            3,\n            3,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_182210_0\",\n            \"20250114_182213_1\",\n            \"20250114_182214_2\",\n            \"20250114_182214_3\",\n            \"20250114_182214_4\",\n            \"20250114_182215_5\",\n            \"20250114_182215_6\",\n            \"20250114_182216_7\",\n            \"20250114_182216_8\",\n            \"20250114_182217_9\",\n            \"20250114_182217_10\",\n            \"20250114_182218_11\",\n            \"20250114_182218_12\",\n            \"20250114_182219_13\",\n            \"20250114_182219_14\",\n            \"20250114_182220_15\",\n            \"20250114_182220_16\",\n            \"20250114_182221_17\",\n            \"20250114_182221_18\",\n            \"20250114_182221_19\",\n            \"20250114_182222_20\",\n            \"20250114_182222_21\",\n            \"20250114_182223_22\",\n            \"20250114_182223_23\",\n            \"20250114_182224_25\",\n            \"20250114_182225_26\",\n            \"20250114_182225_27\",\n            \"20250114_182226_28\",\n            \"20250114_182226_29\",\n            \"20250114_182227_30\",\n            \"20250114_182227_31\",\n            \"20250114_182228_32\",\n            \"20250114_182228_33\",\n            \"20250114_182229_34\",\n            \"20250114_182229_35\",\n            \"20250114_182229_36\",\n            \"20250114_182230_37\",\n            \"20250114_182230_38\",\n            \"20250114_182231_39\",\n            \"20250114_182231_40\",\n            \"20250114_182232_41\",\n            \"20250114_182232_42\",\n            \"20250114_182233_43\",\n            \"20250114_182233_44\",\n            \"20250114_182234_45\",\n            \"20250114_182234_46\",\n            \"20250114_182235_47\"\n        ],\n        \"pos\": [\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                11.030811659892251\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                14.030811659892251\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                17.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                20.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                23.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                26.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                29.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                32.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                35.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                38.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                41.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                44.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                47.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                50.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                53.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                56.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                59.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                62.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                65.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                68.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                71.03081165989225\n            ],\n            [\n                235.6396636657705,\n                -1494.7302961166997,\n                74.03081165989225\n            ],\n            [\n                238.6396636657705,\n                -1494.7302961166997,\n                74.03081165989225\n            ],\n            [\n                238.6396636657705,\n                -1494.7302961166997,\n                74.03081165989225\n            ],\n            [\n                240.1396636657705,\n                -1497.328372328053,\n                74.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                74.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                71.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                68.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                65.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                62.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                59.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                56.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                53.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                50.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                47.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                44.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                41.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                38.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                35.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                32.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                29.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                26.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                23.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                20.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                17.03081165989225\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                14.030811659892251\n            ],\n            [\n                241.6396636657705,\n                -1499.9264485394065,\n                11.030811659892251\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_long/2025-1-2_11-55-27_511583958\",\n        \"gpt_instruction\": \"Move forward to the gray , medium - sized lamp post that is tall , round - topped , and emits light ; then slightly turn left and advance towards the light gray skyscraper , which is very tall with evenly spaced horizontal lines of light , significantly taller than the surrounding buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_115531_2\",\n            \"20250102_115532_5\",\n            \"20250102_115534_8\",\n            \"20250102_115536_11\",\n            \"20250102_115537_14\",\n            \"20250102_115539_17\",\n            \"20250102_115540_20\",\n            \"20250102_115542_23\",\n            \"20250102_115543_26\",\n            \"20250102_115545_29\",\n            \"20250102_115546_32\",\n            \"20250102_115548_35\",\n            \"20250102_115548_36\",\n            \"20250102_115549_37\",\n            \"20250102_115550_40\",\n            \"20250102_115552_43\",\n            \"20250102_115553_46\",\n            \"20250102_115555_49\",\n            \"20250102_115555_50\"\n        ],\n        \"pos\": [\n            [\n                -2404.8652573806917,\n                -961.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -952.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -943.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -934.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -925.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -916.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -907.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -898.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -889.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -880.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -871.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -862.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -859.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2404.8652573806917,\n                -856.7477013005973,\n                39.10115722644696\n            ],\n            [\n                -2407.8652573806917,\n                -851.5515488778906,\n                39.10115722644696\n            ],\n            [\n                -2412.3652573806917,\n                -843.7573202438305,\n                39.10115722644696\n            ],\n            [\n                -2416.8652573806917,\n                -835.9630916097703,\n                39.10115722644696\n            ],\n            [\n                -2421.3652573806917,\n                -828.1688629757102,\n                39.10115722644696\n            ],\n            [\n                -2422.8652573806917,\n                -825.5707867643569,\n                39.10115722644696\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_long/2025-1-2_3-52-58_695466127\",\n        \"gpt_instruction\": \"Proceed forward towards a beige residential building characterized by rectangular windows and multiple stories . Then , turn left to find a dark blue large building with tall , rectangular features . Slightly veer right and head towards a grey high - rise building with evenly spaced windows . Continue straight to reach it . Take a right towards a tall grey building , and then keep going straight until you see a dark blue apartment structure with rectangular windows . Slightly turn right and proceed to a gray skyscraper with tall and narrow features . Eventually , take a left to encounter a large residential apartment tower with horizontal lines . Walk straight towards a tall , dark - colored skyscraper with vertical windows and a flat roof . Then , slightly turn right and head straight to a white building with vertical green light strips . Finally , turn left to approach a tall grey residential or office high - rise building with vertical Asian script lettering and columns of windows .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            8,\n            2,\n            2,\n            1,\n            3,\n            3,\n            9,\n            1,\n            3,\n            3,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            2,\n            1,\n            3,\n            8,\n            2,\n            2,\n            9,\n            8,\n            3,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250102_035302_2\",\n            \"20250102_035303_5\",\n            \"20250102_035304_8\",\n            \"20250102_035305_10\",\n            \"20250102_035306_11\",\n            \"20250102_035306_12\",\n            \"20250102_035307_13\",\n            \"20250102_035307_14\",\n            \"20250102_035308_15\",\n            \"20250102_035309_18\",\n            \"20250102_035309_19\",\n            \"20250102_035310_20\",\n            \"20250102_035310_21\",\n            \"20250102_035312_24\",\n            \"20250102_035313_27\",\n            \"20250102_035313_28\",\n            \"20250102_035314_29\",\n            \"20250102_035315_32\",\n            \"20250102_035316_35\",\n            \"20250102_035318_38\",\n            \"20250102_035319_41\",\n            \"20250102_035320_42\",\n            \"20250102_035320_43\",\n            \"20250102_035321_44\",\n            \"20250102_035321_45\",\n            \"20250102_035322_46\",\n            \"20250102_035322_48\",\n            \"20250102_035323_49\",\n            \"20250102_035323_50\",\n            \"20250102_035325_53\",\n            \"20250102_035326_55\",\n            \"20250102_035326_56\",\n            \"20250102_035327_59\",\n            \"20250102_035328_60\",\n            \"20250102_035328_61\"\n        ],\n        \"pos\": [\n            [\n                4337.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4328.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4319.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4313.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4310.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4310.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4310.358155401375,\n                1499.9665607955228,\n                30.70900899326205\n            ],\n            [\n                4308.858155401375,\n                1497.3684845841694,\n                30.70900899326205\n            ],\n            [\n                4308.858155401375,\n                1497.3684845841694,\n                30.70900899326205\n            ],\n            [\n                4302.858155401375,\n                1497.3684845841694,\n                30.70900899326205\n            ],\n            [\n                4299.858155401375,\n                1497.3684845841694,\n                30.70900899326205\n            ],\n            [\n                4296.858155401375,\n                1497.3684845841694,\n                30.70900899326205\n            ],\n            [\n                4296.858155401375,\n                1497.3684845841694,\n                30.70900899326205\n            ],\n            [\n                4293.858155401375,\n                1502.5646370068762,\n                30.70900899326205\n            ],\n            [\n                4289.358155401375,\n                1510.3588656409363,\n                30.70900899326205\n            ],\n            [\n                4287.858155401375,\n                1512.9569418522897,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1515.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1521.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1530.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1539.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1548.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1551.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1554.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1554.555018063643,\n                30.70900899326205\n            ],\n            [\n                4286.358155401375,\n                1554.555018063643,\n                30.70900899326205\n            ],\n            [\n                4283.760079190022,\n                1556.055018063643,\n                30.70900899326205\n            ],\n            [\n                4282.260079190022,\n                1558.6530942749964,\n                30.70900899326205\n            ],\n            [\n                4280.760079190022,\n                1561.2511704863498,\n                30.70900899326205\n            ],\n            [\n                4280.760079190022,\n                1561.2511704863498,\n                30.70900899326205\n            ],\n            [\n                4274.760079190022,\n                1561.2511704863498,\n                30.70900899326205\n            ],\n            [\n                4268.760079190022,\n                1561.2511704863498,\n                30.70900899326205\n            ],\n            [\n                4265.760079190022,\n                1561.2511704863498,\n                30.70900899326205\n            ],\n            [\n                4260.563926767316,\n                1564.2511704863498,\n                30.70900899326205\n            ],\n            [\n                4257.965850555963,\n                1565.7511704863498,\n                30.70900899326205\n            ],\n            [\n                4255.36777434461,\n                1567.2511704863498,\n                30.70900899326205\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.665191429188092,\n            4.1887902047863905,\n            -2.094395102393186,\n            -2.6179938779914846,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.617993877991494,\n            2.617993877991466,\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779914846,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.617993877991466,\n            2.617993877991466,\n            2.617993877991466\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_low/2025-1-11_13-57-44_1564633137\",\n        \"gpt_instruction\": \"Continue straight toward the medium - sized modern pavilion characterized by its white color and curved glass architecture with a lit facade . Then , slightly turn left and move ahead to the large blue building featuring a grid - like glass facade and bright interior lighting .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_135748_2\",\n            \"20250111_135749_5\",\n            \"20250111_135751_8\",\n            \"20250111_135752_11\",\n            \"20250111_135754_14\",\n            \"20250111_135755_17\",\n            \"20250111_135757_20\",\n            \"20250111_135757_21\",\n            \"20250111_135758_24\",\n            \"20250111_135800_27\",\n            \"20250111_135800_28\",\n            \"20250111_135801_29\"\n        ],\n        \"pos\": [\n            [\n                -602.7978475230611,\n                -305.835487563342,\n                129.01292753601064\n            ],\n            [\n                -598.2978475230611,\n                -298.04125892928204,\n                129.01292753601064\n            ],\n            [\n                -593.7978475230611,\n                -290.2470302952221,\n                129.01292753601064\n            ],\n            [\n                -589.2978475230611,\n                -282.45280166116214,\n                129.01292753601064\n            ],\n            [\n                -584.7978475230611,\n                -274.6585730271022,\n                129.01292753601064\n            ],\n            [\n                -580.2978475230611,\n                -266.86434439304224,\n                129.01292753601064\n            ],\n            [\n                -575.7978475230611,\n                -259.0701157589823,\n                129.01292753601064\n            ],\n            [\n                -574.2978475230611,\n                -256.472039547629,\n                129.01292753601064\n            ],\n            [\n                -574.2978475230611,\n                -250.47203954762898,\n                129.01292753601064\n            ],\n            [\n                -574.2978475230611,\n                -241.47203954762898,\n                129.01292753601064\n            ],\n            [\n                -574.2978475230611,\n                -238.47203954762898,\n                129.01292753601064\n            ],\n            [\n                -574.2978475230611,\n                -235.47203954762898,\n                129.01292753601064\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_average/2025-1-7_7-26-2_2014126950\",\n        \"gpt_instruction\": \"Move forward to the dark - colored building , characterized by a flat roof and horizontal lines , which is large in size ; then slightly turn left and advance to the gray - colored modern residential block with a flat roof that is medium in size .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_072605_2\",\n            \"20250107_072606_5\",\n            \"20250107_072608_8\",\n            \"20250107_072609_11\",\n            \"20250107_072610_14\",\n            \"20250107_072612_17\",\n            \"20250107_072613_20\",\n            \"20250107_072614_23\",\n            \"20250107_072615_24\",\n            \"20250107_072616_27\",\n            \"20250107_072617_30\",\n            \"20250107_072618_33\",\n            \"20250107_072619_34\"\n        ],\n        \"pos\": [\n            [\n                -2916.5406451600784,\n                2115.6068762134746,\n                45.6310366593038\n            ],\n            [\n                -2924.334873794138,\n                2111.1068762134746,\n                45.6310366593038\n            ],\n            [\n                -2932.1291024281973,\n                2106.6068762134746,\n                45.6310366593038\n            ],\n            [\n                -2939.9233310622567,\n                2102.1068762134746,\n                45.6310366593038\n            ],\n            [\n                -2947.717559696316,\n                2097.6068762134746,\n                45.6310366593038\n            ],\n            [\n                -2955.5117883303756,\n                2093.1068762134746,\n                45.6310366593038\n            ],\n            [\n                -2963.306016964435,\n                2088.6068762134746,\n                45.6310366593038\n            ],\n            [\n                -2971.1002455984944,\n                2084.1068762134746,\n                45.6310366593038\n            ],\n            [\n                -2973.6983218098476,\n                2082.6068762134746,\n                45.6310366593038\n            ],\n            [\n                -2976.6983218098476,\n                2077.4107237907683,\n                45.6310366593038\n            ],\n            [\n                -2981.1983218098476,\n                2069.616495156709,\n                45.6310366593038\n            ],\n            [\n                -2985.6983218098476,\n                2061.8222665226494,\n                45.6310366593038\n            ],\n            [\n                -2987.1983218098476,\n                2059.2241903112963,\n                45.6310366593038\n            ]\n        ],\n        \"yaw\": [\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.617993877991466,\n            -2.0943951023932237,\n            -2.0943951023932237,\n            -2.0943951023932237,\n            -2.0943951023932237\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_low/2025-1-11_4-44-28_2085308422\",\n        \"gpt_instruction\": \"Move forward to the light blue , tall , sleek , and reflective structure with vertical lines , which is the dominant feature in the image , suggesting a large and tall building described as a modern skyscraper .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_044432_2\",\n            \"20250111_044433_5\",\n            \"20250111_044435_8\",\n            \"20250111_044436_11\",\n            \"20250111_044438_14\",\n            \"20250111_044439_17\",\n            \"20250111_044440_20\",\n            \"20250111_044442_23\",\n            \"20250111_044443_26\",\n            \"20250111_044445_29\",\n            \"20250111_044447_32\",\n            \"20250111_044447_33\",\n            \"20250111_044447_34\"\n        ],\n        \"pos\": [\n            [\n                -1401.9596927986906,\n                393.89217409669527,\n                129.01292753601064\n            ],\n            [\n                -1397.4596927986906,\n                401.6864027307552,\n                129.01292753601064\n            ],\n            [\n                -1392.9596927986906,\n                409.48063136481517,\n                129.01292753601064\n            ],\n            [\n                -1388.4596927986906,\n                417.2748599988751,\n                129.01292753601064\n            ],\n            [\n                -1383.9596927986906,\n                425.06908863293506,\n                129.01292753601064\n            ],\n            [\n                -1379.4596927986906,\n                432.863317266995,\n                129.01292753601064\n            ],\n            [\n                -1374.9596927986906,\n                440.65754590105496,\n                129.01292753601064\n            ],\n            [\n                -1370.4596927986906,\n                448.4517745351149,\n                129.01292753601064\n            ],\n            [\n                -1365.9596927986906,\n                456.24600316917486,\n                129.01292753601064\n            ],\n            [\n                -1361.4596927986906,\n                464.0402318032348,\n                129.01292753601064\n            ],\n            [\n                -1356.9596927986906,\n                471.83446043729475,\n                129.01292753601064\n            ],\n            [\n                -1355.4596927986906,\n                474.43253664864807,\n                129.01292753601064\n            ],\n            [\n                -1353.9596927986906,\n                477.0306128600014,\n                129.01292753601064\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_average/2025-1-11_10-55-2_1547449638\",\n        \"gpt_instruction\": \"Continue straight towards a large , multi - story skyscraper characterized by its dark blue color , stepped and layered design , distinctive overhanging floors , and modern high - rise architecture .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_105506_2\",\n            \"20250111_105508_5\",\n            \"20250111_105509_8\",\n            \"20250111_105510_11\",\n            \"20250111_105512_14\",\n            \"20250111_105513_17\",\n            \"20250111_105515_20\",\n            \"20250111_105516_23\",\n            \"20250111_105518_26\",\n            \"20250111_105519_29\",\n            \"20250111_105521_32\",\n            \"20250111_105522_35\",\n            \"20250111_105524_38\",\n            \"20250111_105524_39\",\n            \"20250111_105525_40\"\n        ],\n        \"pos\": [\n            [\n                -858.3893378745549,\n                -74.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -850.5951092404948,\n                -78.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -842.8008806064347,\n                -83.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -835.0066519723746,\n                -87.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -827.2124233383145,\n                -92.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -819.4181947042543,\n                -96.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -811.6239660701942,\n                -101.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -803.8297374361341,\n                -105.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -796.035508802074,\n                -110.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -788.2412801680139,\n                -114.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -780.4470515339538,\n                -119.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -772.6528228998936,\n                -123.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -764.8585942658335,\n                -128.33027717376712,\n                131.6344676312703\n            ],\n            [\n                -762.2605180544801,\n                -129.83027717376712,\n                131.6344676312703\n            ],\n            [\n                -759.6624418431268,\n                -131.33027717376712,\n                131.6344676312703\n            ]\n        ],\n        \"yaw\": [\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_average_updown/2025-1-14_20-13-57_609707131\",\n        \"gpt_instruction\": \"Begin by ascending towards a large , modern skyscraper characterized by white vertical illuminated light panels . Then , proceed straight to it . Slightly turn left and go directly ahead to a large building with a white smooth facade highlighted by a dark window grid . Slightly turn right and advance forward to a tall , modern skyscraper with blue and white vertical lighting . Finally , slightly turn left , move straight , and stop at it .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            8,\n            2,\n            9,\n            3,\n            9,\n            8,\n            2,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_201358_0\",\n            \"20250114_201401_1\",\n            \"20250114_201401_2\",\n            \"20250114_201402_3\",\n            \"20250114_201402_4\",\n            \"20250114_201402_5\",\n            \"20250114_201403_6\",\n            \"20250114_201403_7\",\n            \"20250114_201404_8\",\n            \"20250114_201404_9\",\n            \"20250114_201405_10\",\n            \"20250114_201405_11\",\n            \"20250114_201406_12\",\n            \"20250114_201406_13\",\n            \"20250114_201407_14\",\n            \"20250114_201408_17\",\n            \"20250114_201409_19\",\n            \"20250114_201409_20\",\n            \"20250114_201411_23\",\n            \"20250114_201411_24\",\n            \"20250114_201413_27\",\n            \"20250114_201414_29\",\n            \"20250114_201414_30\",\n            \"20250114_201415_31\",\n            \"20250114_201415_32\",\n            \"20250114_201416_33\",\n            \"20250114_201416_34\",\n            \"20250114_201417_35\",\n            \"20250114_201417_36\",\n            \"20250114_201418_37\",\n            \"20250114_201418_38\",\n            \"20250114_201418_39\",\n            \"20250114_201419_40\",\n            \"20250114_201419_41\",\n            \"20250114_201420_42\",\n            \"20250114_201420_43\",\n            \"20250114_201421_44\",\n            \"20250114_201421_45\",\n            \"20250114_201422_46\",\n            \"20250114_201422_47\"\n        ],\n        \"pos\": [\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                10.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                13.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                16.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                19.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                22.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                25.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                28.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                31.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                34.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                37.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                40.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                43.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                46.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                49.68080633296421\n            ],\n            [\n                -25.921574610603457,\n                639.2265540737291,\n                52.68080633296421\n            ],\n            [\n                -22.921574610603457,\n                634.0304016510224,\n                55.68080633296421\n            ],\n            [\n                -19.921574610603457,\n                628.8342492283157,\n                55.68080633296421\n            ],\n            [\n                -18.421574610603457,\n                626.2361730169623,\n                55.68080633296421\n            ],\n            [\n                -13.225422187896825,\n                623.2361730169623,\n                55.68080633296421\n            ],\n            [\n                -10.62734597654351,\n                621.7361730169623,\n                55.68080633296421\n            ],\n            [\n                -7.627345976543509,\n                616.5400205942556,\n                55.68080633296421\n            ],\n            [\n                -4.627345976543509,\n                611.3438681715488,\n                55.68080633296421\n            ],\n            [\n                -3.1273459765435088,\n                608.7457919601954,\n                55.68080633296421\n            ],\n            [\n                -3.1273459765435088,\n                608.7457919601954,\n                55.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                55.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                52.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                49.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                46.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                43.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                40.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                37.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                34.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                31.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                28.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                25.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                22.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                19.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                16.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                13.68080633296421\n            ],\n            [\n                -0.5292697651901928,\n                607.2457919601954,\n                10.68080633296421\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511965883,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084,\n            -0.5235987755983084\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_average/2025-1-11_16-45-53_150238230\",\n        \"gpt_instruction\": \"Proceed straight to the large gray building characterized by a rectangular grid - like pattern of windows , then slightly turn right and proceed straight again to a sizable skyscraper with a dark facade featuring illuminated windows and bright horizontal lines .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_164556_2\",\n            \"20250111_164558_5\",\n            \"20250111_164558_7\",\n            \"20250111_164559_8\",\n            \"20250111_164600_11\",\n            \"20250111_164601_12\"\n        ],\n        \"pos\": [\n            [\n                21.688337507234582,\n                51.96315682509044,\n                131.6344676312703\n            ],\n            [\n                21.688337507234582,\n                42.96315682509044,\n                131.6344676312703\n            ],\n            [\n                21.688337507234582,\n                36.96315682509044,\n                131.6344676312703\n            ],\n            [\n                21.688337507234582,\n                33.96315682509044,\n                131.6344676312703\n            ],\n            [\n                18.688337507234582,\n                28.767004402383805,\n                131.6344676312703\n            ],\n            [\n                17.188337507234582,\n                26.16892819103049,\n                131.6344676312703\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_average/2025-1-7_4-47-32_1648609\",\n        \"gpt_instruction\": \"Proceed straight to the modern building characterized by large blue horizontal lines .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_044736_2\",\n            \"20250107_044737_5\",\n            \"20250107_044738_8\",\n            \"20250107_044740_11\",\n            \"20250107_044741_14\",\n            \"20250107_044742_17\",\n            \"20250107_044743_20\",\n            \"20250107_044744_21\",\n            \"20250107_044744_22\"\n        ],\n        \"pos\": [\n            [\n                4050.1942169842405,\n                -205.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -196.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -187.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -178.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -169.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -160.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -151.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -148.72242848452566,\n                48.08248088092334\n            ],\n            [\n                4050.1942169842405,\n                -145.72242848452566,\n                48.08248088092334\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/medium_average/2025-1-7_10-55-44_965620727\",\n        \"gpt_instruction\": \"Continue walking straight towards a large , brown building characterized by its rectangular shape with glass windows . Then , turn right slightly and proceed to it . Walk straight ahead to it . Next , slightly turn right and walk straight to it . Shift left to it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            3,\n            3,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            1,\n            2,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250107_105547_2\",\n            \"20250107_105549_5\",\n            \"20250107_105550_7\",\n            \"20250107_105550_8\",\n            \"20250107_105551_9\",\n            \"20250107_105552_12\",\n            \"20250107_105553_15\",\n            \"20250107_105555_18\",\n            \"20250107_105556_20\",\n            \"20250107_105556_21\",\n            \"20250107_105558_24\",\n            \"20250107_105600_27\",\n            \"20250107_105600_28\",\n            \"20250107_105601_29\",\n            \"20250107_105601_30\",\n            \"20250107_105603_33\",\n            \"20250107_105604_36\",\n            \"20250107_105605_38\",\n            \"20250107_105606_39\"\n        ],\n        \"pos\": [\n            [\n                -884.4313211341869,\n                168.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -884.4313211341869,\n                177.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -884.4313211341869,\n                183.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -884.4313211341869,\n                186.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -884.4313211341869,\n                186.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -879.2351687114801,\n                189.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -871.44094007742,\n                194.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -863.6467114433599,\n                198.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -858.4505590206531,\n                201.67300050287042,\n                59.336223894097685\n            ],\n            [\n                -855.8524828092998,\n                203.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -849.8524828092998,\n                203.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -840.8524828092998,\n                203.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -837.8524828092998,\n                203.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -834.8524828092998,\n                203.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -834.8524828092998,\n                203.17300050287042,\n                59.336223894097685\n            ],\n            [\n                -831.8524828092998,\n                208.36915292557705,\n                59.336223894097685\n            ],\n            [\n                -827.3524828092998,\n                216.163381559637,\n                59.336223894097685\n            ],\n            [\n                -824.3524828092998,\n                221.35953398234363,\n                59.336223894097685\n            ],\n            [\n                -822.8524828092998,\n                223.95761019369695,\n                59.336223894097685\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_average/2025-1-11_10-6-33_915989944\",\n        \"gpt_instruction\": \"Walk straight toward a towering silver skyscraper accented with bright lighting , featuring elongated vertical structures illuminated with light streaks , surrounded by other buildings . Slightly turn left and move ahead toward a modern architectural facade characterized by a large vertical span of integrated blue lighting with bright white and yellow streaks , and sharply defined dark patterns on the left side .\",\n        \"action\": [\n            9,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250111_100637_2\",\n            \"20250111_100638_5\",\n            \"20250111_100639_6\",\n            \"20250111_100640_9\",\n            \"20250111_100642_12\",\n            \"20250111_100642_13\"\n        ],\n        \"pos\": [\n            [\n                -635.0550474849192,\n                -167.22576143189056,\n                131.6344676312703\n            ],\n            [\n                -642.8492761189793,\n                -162.72576143189056,\n                131.6344676312703\n            ],\n            [\n                -645.4473523303327,\n                -161.22576143189056,\n                131.6344676312703\n            ],\n            [\n                -651.4473523303327,\n                -161.22576143189056,\n                131.6344676312703\n            ],\n            [\n                -660.4473523303327,\n                -161.22576143189056,\n                131.6344676312703\n            ],\n            [\n                -663.4473523303327,\n                -161.22576143189056,\n                131.6344676312703\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_short/2025-01-19_12-27-41_656877\",\n        \"gpt_instruction\": \"Begin by ascending to the large civil structure , a red bridge with light trails . Slightly turn left and head straight towards the large high - rise building , a modern skyscraper with a blue hue and white accents , featuring a rectangular structure with many illuminated windows . Then , slightly turn right and proceed directly to it . Next , gently descend by slightly turning left and continue straight towards the medium to large commercial or residential building . It occupies a significant portion of the scene , showcasing a dark blue , reflective surface with faint window outlines .\",\n        \"action\": [\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            4,\n            2,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            1,\n            5,\n            2,\n            1,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_122742_0\",\n            \"20250119_122743_1\",\n            \"20250119_122744_2\",\n            \"20250119_122745_3\",\n            \"20250119_122746_4\",\n            \"20250119_122746_5\",\n            \"20250119_122747_6\",\n            \"20250119_122748_7\",\n            \"20250119_122749_8\",\n            \"20250119_122749_9\",\n            \"20250119_122750_10\",\n            \"20250119_122751_11\",\n            \"20250119_122752_12\",\n            \"20250119_122753_13\",\n            \"20250119_122753_14\",\n            \"20250119_122754_15\",\n            \"20250119_122755_16\",\n            \"20250119_122756_17\",\n            \"20250119_122757_18\",\n            \"20250119_122757_19\",\n            \"20250119_122758_20\",\n            \"20250119_122759_21\",\n            \"20250119_122802_24\",\n            \"20250119_122804_27\",\n            \"20250119_122807_30\",\n            \"20250119_122808_31\",\n            \"20250119_122811_34\",\n            \"20250119_122813_37\",\n            \"20250119_122814_38\",\n            \"20250119_122815_39\",\n            \"20250119_122816_40\",\n            \"20250119_122817_41\",\n            \"20250119_122818_42\",\n            \"20250119_122819_43\",\n            \"20250119_122819_44\",\n            \"20250119_122820_45\",\n            \"20250119_122821_46\",\n            \"20250119_122822_47\",\n            \"20250119_122823_48\",\n            \"20250119_122824_49\",\n            \"20250119_122825_50\",\n            \"20250119_122826_51\",\n            \"20250119_122826_52\",\n            \"20250119_122827_53\",\n            \"20250119_122828_54\",\n            \"20250119_122829_55\",\n            \"20250119_122830_56\",\n            \"20250119_122831_57\",\n            \"20250119_122831_58\",\n            \"20250119_122832_59\",\n            \"20250119_122833_60\",\n            \"20250119_122834_61\",\n            \"20250119_122835_62\"\n        ],\n        \"pos\": [\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                36.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                39.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                42.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                45.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                48.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                51.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                54.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                57.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                60.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                63.237171090501306\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                66.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                69.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                72.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                75.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                78.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                81.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                84.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                87.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                90.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                93.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                96.2371710905013\n            ],\n            [\n                -67.76073620936468,\n                -2101.092085122408,\n                99.2371710905013\n            ],\n            [\n                -62.56458378665805,\n                -2098.092085122408,\n                99.2371710905013\n            ],\n            [\n                -54.7703551525981,\n                -2093.592085122408,\n                99.2371710905013\n            ],\n            [\n                -46.97612651853815,\n                -2089.092085122408,\n                99.2371710905013\n            ],\n            [\n                -44.378050307184836,\n                -2087.592085122408,\n                99.2371710905013\n            ],\n            [\n                -38.378050307184836,\n                -2087.592085122408,\n                99.2371710905013\n            ],\n            [\n                -29.378050307184836,\n                -2087.592085122408,\n                99.2371710905013\n            ],\n            [\n                -26.378050307184836,\n                -2087.592085122408,\n                99.2371710905013\n            ],\n            [\n                -23.378050307184836,\n                -2087.592085122408,\n                99.2371710905013\n            ],\n            [\n                -23.378050307184836,\n                -2087.592085122408,\n                96.2371710905013\n            ],\n            [\n                -23.378050307184836,\n                -2087.592085122408,\n                96.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                96.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                93.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                90.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                87.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                84.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                81.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                78.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                75.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                72.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                69.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                66.2371710905013\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                63.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                60.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                57.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                54.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                51.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                48.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                45.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                42.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                39.237171090501306\n            ],\n            [\n                -20.77997409583152,\n                -2086.092085122408,\n                36.237171090501306\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_long/2025-1-1_20-13-36_2053999932\",\n        \"gpt_instruction\": \"Proceed towards the tall structure characterized by a light beige color and a vertical row of small windows , which could be either a residential or office building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_201340_2\",\n            \"20250101_201341_5\",\n            \"20250101_201343_8\",\n            \"20250101_201344_11\",\n            \"20250101_201345_14\",\n            \"20250101_201347_17\",\n            \"20250101_201348_20\",\n            \"20250101_201350_23\",\n            \"20250101_201351_26\",\n            \"20250101_201353_29\",\n            \"20250101_201354_32\",\n            \"20250101_201355_35\",\n            \"20250101_201357_38\",\n            \"20250101_201358_41\",\n            \"20250101_201359_42\"\n        ],\n        \"pos\": [\n            [\n                -1161.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1170.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1179.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1188.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1197.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1206.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1215.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1224.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1233.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1242.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1251.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1260.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1269.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1278.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ],\n            [\n                -1281.612244943831,\n                -2006.334499315077,\n                33.6779973277971\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_long_updown/2025-01-19_12-39-45_527006\",\n        \"gpt_instruction\": \"Begin at a tall , gray apartment building , characterized by its large size and multiple balconies , then continue straight towards a large , modern skyscraper with blue and black colors and glowing sections . Slightly veer left , heading straight to a medium - sized building with square windows and several balconies . Ascend to witness a very large , dark gray cylindrical skyscraper adorned with line patterns . Proceed forward to another large building in gray , featuring rectangular windows and dark glass . Slightly turn right and continue ahead to reach it . Descend to a large , dark grey building with a wavy concrete facade . Turn right to approach a high - rise structure , recognizable for its tall silhouette , illuminated windows , and large size . Gently move straight ahead , pausing at a medium - sized , dark residential building distinguished by vertical lines and fewer lights .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            1,\n            2,\n            8,\n            4,\n            4,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            5,\n            5,\n            3,\n            3,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250119_123946_0\",\n            \"20250119_123947_1\",\n            \"20250119_123947_2\",\n            \"20250119_123948_3\",\n            \"20250119_123949_4\",\n            \"20250119_123950_5\",\n            \"20250119_123951_6\",\n            \"20250119_123952_7\",\n            \"20250119_123954_10\",\n            \"20250119_123955_11\",\n            \"20250119_123956_12\",\n            \"20250119_123958_14\",\n            \"20250119_123958_15\",\n            \"20250119_123959_16\",\n            \"20250119_124002_19\",\n            \"20250119_124003_20\",\n            \"20250119_124005_23\",\n            \"20250119_124008_26\",\n            \"20250119_124011_29\",\n            \"20250119_124013_32\",\n            \"20250119_124016_35\",\n            \"20250119_124018_38\",\n            \"20250119_124020_40\",\n            \"20250119_124021_41\",\n            \"20250119_124024_44\",\n            \"20250119_124026_47\",\n            \"20250119_124029_50\",\n            \"20250119_124032_53\",\n            \"20250119_124034_56\",\n            \"20250119_124037_59\",\n            \"20250119_124040_62\",\n            \"20250119_124041_63\",\n            \"20250119_124042_64\",\n            \"20250119_124042_65\",\n            \"20250119_124043_66\",\n            \"20250119_124044_67\",\n            \"20250119_124045_68\",\n            \"20250119_124046_69\",\n            \"20250119_124047_70\",\n            \"20250119_124048_71\",\n            \"20250119_124048_72\",\n            \"20250119_124049_73\",\n            \"20250119_124050_74\",\n            \"20250119_124051_75\",\n            \"20250119_124052_76\",\n            \"20250119_124053_77\"\n        ],\n        \"pos\": [\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                11.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                14.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                17.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                20.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                23.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                26.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                29.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -353.5119647244643,\n                32.144912705837626,\n                1.5707963267948966\n            ],\n            [\n                901.9389444370291,\n                -347.5119647244643,\n                35.144912705837626\n            ],\n            [\n                901.9389444370291,\n                -344.5119647244643,\n                35.144912705837626\n            ],\n            [\n                901.9389444370291,\n                -341.5119647244643,\n                35.144912705837626\n            ],\n            [\n                900.4389444370291,\n                -338.913888513111,\n                35.144912705837626\n            ],\n            [\n                898.9389444370291,\n                -336.31581230175766,\n                35.144912705837626\n            ],\n            [\n                898.9389444370291,\n                -336.31581230175766,\n                38.144912705837626\n            ],\n            [\n                895.9389444370291,\n                -331.119659879051,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -328.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -322.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -313.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -304.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -295.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -286.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -277.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -271.5215836676977,\n                41.144912705837626\n            ],\n            [\n                894.4389444370291,\n                -268.5215836676977,\n                41.144912705837626\n            ],\n            [\n                891.4389444370291,\n                -263.3254312449911,\n                41.144912705837626\n            ],\n            [\n                886.9389444370291,\n                -255.53120261093113,\n                41.144912705837626\n            ],\n            [\n                882.4389444370291,\n                -247.73697397687118,\n                41.144912705837626\n            ],\n            [\n                877.9389444370291,\n                -239.94274534281124,\n                41.144912705837626\n            ],\n            [\n                873.4389444370291,\n                -232.1485167087513,\n                41.144912705837626\n            ],\n            [\n                868.9389444370291,\n                -224.35428807469134,\n                41.144912705837626\n            ],\n            [\n                864.4389444370291,\n                -216.5600594406314,\n                41.144912705837626\n            ],\n            [\n                862.9389444370291,\n                -213.96198322927808,\n                41.144912705837626\n            ],\n            [\n                861.4389444370291,\n                -211.36390701792476,\n                41.144912705837626\n            ],\n            [\n                861.4389444370291,\n                -211.36390701792476,\n                38.144912705837626\n            ],\n            [\n                861.4389444370291,\n                -211.36390701792476,\n                35.144912705837626\n            ],\n            [\n                861.4389444370291,\n                -211.36390701792476,\n                35.144912705837626\n            ],\n            [\n                861.4389444370291,\n                -211.36390701792476,\n                35.144912705837626\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                35.144912705837626\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                32.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                29.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                26.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                23.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                20.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                17.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                14.144912705837626,\n                1.0471975511965976\n            ],\n            [\n                862.9389444370291,\n                -208.76583080657144,\n                11.144912705837626,\n                1.0471975511965976\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.0471975511965976,\n            1.0471975511965976,\n            1.0471975511965976,\n            1.0471975511965976,\n            1.0471975511965976,\n            1.0471975511965976,\n            1.0471975511965976,\n            1.0471975511965976\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/low_short/2025-1-1_15-17-11_1911165193\",\n        \"gpt_instruction\": \"Proceed directly towards the tall dark blue building characterized by horizontal stripes and rectangular windows . Then , slightly turn left and continue heading straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            2,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250101_151714_2\",\n            \"20250101_151716_5\",\n            \"20250101_151717_7\",\n            \"20250101_151717_8\",\n            \"20250101_151719_11\",\n            \"20250101_151720_13\",\n            \"20250101_151720_14\"\n        ],\n        \"pos\": [\n            [\n                1593.9332704284807,\n                1709.9746972161895,\n                19.653067371893176\n            ],\n            [\n                1584.9332704284807,\n                1709.9746972161895,\n                19.653067371893176\n            ],\n            [\n                1578.9332704284807,\n                1709.9746972161895,\n                19.653067371893176\n            ],\n            [\n                1575.9332704284807,\n                1709.9746972161895,\n                19.653067371893176\n            ],\n            [\n                1570.737118005774,\n                1706.9746972161895,\n                19.653067371893176\n            ],\n            [\n                1565.5409655830672,\n                1703.9746972161895,\n                19.653067371893176\n            ],\n            [\n                1562.9428893717138,\n                1702.4746972161895,\n                19.653067371893176\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_airsim_gz/astar_data/high_low/2025-1-10_21-11-6_1605539862\",\n        \"gpt_instruction\": \"Head straight towards the blue building characterized by large windows with illuminated interiors and a tall , prominent structure . Slightly turn right and move forward to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250110_211110_2\",\n            \"20250110_211111_5\",\n            \"20250110_211113_8\",\n            \"20250110_211115_11\",\n            \"20250110_211116_13\",\n            \"20250110_211116_14\",\n            \"20250110_211118_17\",\n            \"20250110_211119_20\",\n            \"20250110_211120_21\",\n            \"20250110_211120_22\"\n        ],\n        \"pos\": [\n            [\n                -654.0712389730089,\n                -38.82296639388285,\n                129.01292753601064\n            ],\n            [\n                -649.5712389730089,\n                -31.028737759822903,\n                129.01292753601064\n            ],\n            [\n                -645.0712389730089,\n                -23.234509125762955,\n                129.01292753601064\n            ],\n            [\n                -640.5712389730089,\n                -15.440280491703007,\n                129.01292753601064\n            ],\n            [\n                -637.5712389730089,\n                -10.244128068996375,\n                129.01292753601064\n            ],\n            [\n                -636.0712389730089,\n                -7.646051857643059,\n                129.01292753601064\n            ],\n            [\n                -630.8750865503023,\n                -4.646051857643059,\n                129.01292753601064\n            ],\n            [\n                -623.0808579162422,\n                -0.14605185764305917,\n                129.01292753601064\n            ],\n            [\n                -620.4827817048888,\n                1.3539481423569406,\n                129.01292753601064\n            ],\n            [\n                -617.8847054935354,\n                2.8539481423569404,\n                129.01292753601064\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982894,\n            0.5235987755982893,\n            0.5235987755982893,\n            0.5235987755982893\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-3_23-23-56_595311776\",\n        \"gpt_instruction\": \"Move towards a structure characterized by a mix of darker tones , featuring a rectangular building with a rooftop water tower . It is medium - sized compared to nearby structures and functions as a commercial building .\",\n        \"action\": [\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_232405\",\n            \"20250103_232407\",\n            \"20250103_232410\"\n        ],\n        \"pos\": [\n            [\n                5.1651547905946495,\n                232.63656852085492,\n                79.67866618707271\n            ],\n            [\n                6.66515479059465,\n                235.23464473220824,\n                79.67866618707271\n            ],\n            [\n                8.16515479059465,\n                237.83272094356155,\n                79.67866618707271\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965976,\n            1.0471975511965974,\n            1.0471975511965974\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_long/2025-1-5_11-16-41_196095815\",\n        \"gpt_instruction\": \"Proceed ahead to the gray building with large grid - pattern windows , then slightly turn right slightly and advance to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250105_111649\",\n            \"20250105_111655\",\n            \"20250105_111702\",\n            \"20250105_111709\",\n            \"20250105_111715\",\n            \"20250105_111722\",\n            \"20250105_111728\",\n            \"20250105_111735\",\n            \"20250105_111737\",\n            \"20250105_111739\",\n            \"20250105_111745\",\n            \"20250105_111752\",\n            \"20250105_111754\",\n            \"20250105_111756\"\n        ],\n        \"pos\": [\n            [\n                421.2078283556424,\n                672.3568777984472,\n                78.637749639727\n            ],\n            [\n                416.7078283556424,\n                664.562649164387,\n                78.637749639727\n            ],\n            [\n                412.2078283556424,\n                656.7684205303269,\n                78.637749639727\n            ],\n            [\n                407.7078283556424,\n                648.9741918962668,\n                78.637749639727\n            ],\n            [\n                403.2078283556424,\n                641.1799632622067,\n                78.637749639727\n            ],\n            [\n                398.7078283556424,\n                633.3857346281466,\n                78.637749639727\n            ],\n            [\n                394.2078283556424,\n                625.5915059940864,\n                78.637749639727\n            ],\n            [\n                389.7078283556424,\n                617.7972773600263,\n                78.637749639727\n            ],\n            [\n                388.2078283556424,\n                615.199201148673,\n                78.637749639727\n            ],\n            [\n                386.7078283556424,\n                612.6011249373196,\n                78.637749639727\n            ],\n            [\n                381.51167593293576,\n                609.6011249373196,\n                78.637749639727\n            ],\n            [\n                373.7174472988758,\n                605.1011249373196,\n                78.637749639727\n            ],\n            [\n                371.1193710875225,\n                603.6011249373196,\n                78.637749639727\n            ],\n            [\n                368.5212948761692,\n                602.1011249373196,\n                78.637749639727\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_average/2025-1-3_19-2-34_221558440\",\n        \"gpt_instruction\": \"Proceed directly to the large building with a black color and a glass facade .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_190243\",\n            \"20250103_190250\",\n            \"20250103_190256\",\n            \"20250103_190302\",\n            \"20250103_190309\",\n            \"20250103_190315\",\n            \"20250103_190321\",\n            \"20250103_190328\",\n            \"20250103_190334\",\n            \"20250103_190336\"\n        ],\n        \"pos\": [\n            [\n                293.0981869498808,\n                -322.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -331.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -340.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -349.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -358.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -367.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -376.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -385.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -394.7023922997004,\n                95.5145564600975\n            ],\n            [\n                293.0981869498808,\n                -397.7023922997004,\n                95.5145564600975\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-4_16-42-4_189258051\",\n        \"gpt_instruction\": \"Walk straight towards the large beige building , which is rectangular with rows of windows , then slightly turn right and move ahead to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_164211\",\n            \"20250104_164218\",\n            \"20250104_164224\",\n            \"20250104_164229\",\n            \"20250104_164231\",\n            \"20250104_164238\",\n            \"20250104_164240\",\n            \"20250104_164242\"\n        ],\n        \"pos\": [\n            [\n                -468.69065274966846,\n                -410.9519354692834,\n                96.76720498654923\n            ],\n            [\n                -473.19065274966846,\n                -403.15770683522345,\n                96.76720498654923\n            ],\n            [\n                -477.69065274966846,\n                -395.3634782011635,\n                96.76720498654923\n            ],\n            [\n                -480.69065274966846,\n                -390.16732577845687,\n                96.76720498654923\n            ],\n            [\n                -482.19065274966846,\n                -387.56924956710355,\n                96.76720498654923\n            ],\n            [\n                -482.19065274966846,\n                -381.56924956710355,\n                96.76720498654923\n            ],\n            [\n                -482.19065274966846,\n                -378.56924956710355,\n                96.76720498654923\n            ],\n            [\n                -482.19065274966846,\n                -375.56924956710355,\n                96.76720498654923\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-4_19-19-55_1542106687\",\n        \"gpt_instruction\": \"Proceed towards the gray structure , which features a grid - like window pattern , is tall in size , and is classified as a building .\",\n        \"action\": [\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_192002\",\n            \"20250104_192007\",\n            \"20250104_192009\"\n        ],\n        \"pos\": [\n            [\n                10.568214225930031,\n                -386.7185272801795,\n                71.23460750023\n            ],\n            [\n                7.568214225930032,\n                -381.52237485747287,\n                71.23460750023\n            ],\n            [\n                6.068214225930033,\n                -378.92429864611955,\n                71.23460750023\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_average/2025-1-3_19-52-19_1977648522\",\n        \"gpt_instruction\": \"Move forward and slightly turn right while heading straight towards a large , gray historic government building characterized by a green dome and classical architecture .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_195228\",\n            \"20250103_195235\",\n            \"20250103_195241\",\n            \"20250103_195248\",\n            \"20250103_195254\",\n            \"20250103_195301\",\n            \"20250103_195307\",\n            \"20250103_195311\",\n            \"20250103_195313\",\n            \"20250103_195320\",\n            \"20250103_195326\",\n            \"20250103_195332\",\n            \"20250103_195339\",\n            \"20250103_195345\",\n            \"20250103_195352\",\n            \"20250103_195354\"\n        ],\n        \"pos\": [\n            [\n                599.169534452051,\n                343.00366797559707,\n                70.38710987305464\n            ],\n            [\n                591.3753058179909,\n                338.50366797559707,\n                70.38710987305464\n            ],\n            [\n                583.5810771839308,\n                334.00366797559707,\n                70.38710987305464\n            ],\n            [\n                575.7868485498707,\n                329.50366797559707,\n                70.38710987305464\n            ],\n            [\n                567.9926199158106,\n                325.00366797559707,\n                70.38710987305464\n            ],\n            [\n                560.1983912817504,\n                320.50366797559707,\n                70.38710987305464\n            ],\n            [\n                552.4041626476903,\n                316.00366797559707,\n                70.38710987305464\n            ],\n            [\n                547.2080102249836,\n                313.00366797559707,\n                70.38710987305464\n            ],\n            [\n                544.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                538.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                529.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                520.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                511.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                502.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                493.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ],\n            [\n                490.6099340136302,\n                311.50366797559707,\n                70.38710987305464\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long/2025-1-4_9-29-48_952062949\",\n        \"gpt_instruction\": \"Move forward to the dark brown building characterized by rectangular windows and medium height , then slightly turn left and keep going straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_092957\",\n            \"20250104_093003\",\n            \"20250104_093009\",\n            \"20250104_093016\",\n            \"20250104_093022\",\n            \"20250104_093029\",\n            \"20250104_093035\",\n            \"20250104_093037\",\n            \"20250104_093044\",\n            \"20250104_093050\",\n            \"20250104_093056\",\n            \"20250104_093103\",\n            \"20250104_093105\",\n            \"20250104_093107\"\n        ],\n        \"pos\": [\n            [\n                198.59822752652224,\n                695.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                686.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                677.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                668.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                659.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                650.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                641.7797471376131,\n                90.99843839120234\n            ],\n            [\n                198.59822752652224,\n                638.7797471376131,\n                90.99843839120234\n            ],\n            [\n                201.59822752652224,\n                633.5835947149064,\n                90.99843839120234\n            ],\n            [\n                206.09822752652224,\n                625.7893660808463,\n                90.99843839120234\n            ],\n            [\n                210.59822752652224,\n                617.9951374467862,\n                90.99843839120234\n            ],\n            [\n                215.09822752652224,\n                610.2009088127261,\n                90.99843839120234\n            ],\n            [\n                216.59822752652224,\n                607.6028326013727,\n                90.99843839120234\n            ],\n            [\n                218.09822752652224,\n                605.0047563900193,\n                90.99843839120234\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long_updown/2025-1-15_5-6-29_1977648522\",\n        \"gpt_instruction\": \"Ascend to the dark building with large rectangular windows , move forward to the dark skyscraper skyline , slightly turn left and go directly ahead to the gray building featuring many windows , slightly stop and fall to the grey commercial edifice with mid-size windows and a grey brick facade .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250115_050633\",\n            \"20250115_050635\",\n            \"20250115_050637\",\n            \"20250115_050640\",\n            \"20250115_050642\",\n            \"20250115_050644\",\n            \"20250115_050646\",\n            \"20250115_050648\",\n            \"20250115_050650\",\n            \"20250115_050652\",\n            \"20250115_050654\",\n            \"20250115_050656\",\n            \"20250115_050703\",\n            \"20250115_050709\",\n            \"20250115_050715\",\n            \"20250115_050721\",\n            \"20250115_050724\",\n            \"20250115_050730\",\n            \"20250115_050737\",\n            \"20250115_050743\",\n            \"20250115_050750\",\n            \"20250115_050756\",\n            \"20250115_050802\",\n            \"20250115_050809\",\n            \"20250115_050813\",\n            \"20250115_050815\",\n            \"20250115_050817\",\n            \"20250115_050819\",\n            \"20250115_050821\",\n            \"20250115_050823\",\n            \"20250115_050826\",\n            \"20250115_050828\",\n            \"20250115_050830\",\n            \"20250115_050832\",\n            \"20250115_050834\",\n            \"20250115_050836\",\n            \"20250115_050838\",\n            \"20250115_050841\"\n        ],\n        \"pos\": [\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                1.0794876578951502\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                4.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                7.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                10.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                13.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                16.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                19.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                22.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                25.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                28.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                31.07948765789515\n            ],\n            [\n                -560.211835251634,\n                -423.9082034837962,\n                34.07948765789515\n            ],\n            [\n                -554.211835251634,\n                -423.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -545.211835251634,\n                -423.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -536.211835251634,\n                -423.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -527.211835251634,\n                -423.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -524.211835251634,\n                -423.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -519.0156828289273,\n                -420.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -511.22145419486725,\n                -416.4082034837962,\n                37.07948765789515\n            ],\n            [\n                -503.4272255608073,\n                -411.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -495.63299692674735,\n                -407.4082034837962,\n                37.07948765789515\n            ],\n            [\n                -487.8387682926874,\n                -402.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -480.04453965862746,\n                -398.4082034837962,\n                37.07948765789515\n            ],\n            [\n                -472.2503110245675,\n                -393.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -467.0541586018609,\n                -390.9082034837962,\n                37.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                37.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                34.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                31.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                28.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                25.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                22.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                19.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                16.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                13.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                10.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                7.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                4.07948765789515\n            ],\n            [\n                -464.45608239050756,\n                -389.4082034837962,\n                1.0794876578951502\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_average/2025-1-6_2-59-33_1632381616\",\n        \"gpt_instruction\": \"Proceed to the large white building with a triangular roof , then slightly turn left and move forward to it . Continue straight to the medium brown and green historic building featuring a decorative roof with a dome structure .\",\n        \"action\": [\n            8,\n            2,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_025940\",\n            \"20250106_025942\",\n            \"20250106_025949\",\n            \"20250106_025955\",\n            \"20250106_030000\",\n            \"20250106_030002\",\n            \"20250106_030009\",\n            \"20250106_030015\",\n            \"20250106_030022\",\n            \"20250106_030028\",\n            \"20250106_030035\",\n            \"20250106_030041\",\n            \"20250106_030048\",\n            \"20250106_030054\",\n            \"20250106_030101\",\n            \"20250106_030105\",\n            \"20250106_030107\"\n        ],\n        \"pos\": [\n            [\n                470.54674208018054,\n                532.7507825698112,\n                80.845339087311\n            ],\n            [\n                467.9486658688272,\n                531.2507825698112,\n                80.845339087311\n            ],\n            [\n                464.9486658688272,\n                526.0546301471045,\n                80.845339087311\n            ],\n            [\n                460.4486658688272,\n                518.2604015130444,\n                80.845339087311\n            ],\n            [\n                457.4486658688272,\n                513.0642490903376,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                510.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                504.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                495.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                486.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                477.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                468.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                459.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                450.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                441.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                432.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                426.4661728789843,\n                80.845339087311\n            ],\n            [\n                455.9486658688272,\n                423.4661728789843,\n                80.845339087311\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_average/2025-1-3_20-47-25_1065103348\",\n        \"gpt_instruction\": \"Proceed directly to the gray modern skyscraper with a tall stature and a grid - like window pattern .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_204733\",\n            \"20250103_204739\",\n            \"20250103_204745\",\n            \"20250103_204751\",\n            \"20250103_204753\"\n        ],\n        \"pos\": [\n            [\n                -128.78885989477348,\n                614.6686280492661,\n                82.11600561838574\n            ],\n            [\n                -136.58308852883343,\n                610.1686280492661,\n                82.11600561838574\n            ],\n            [\n                -144.37731716289338,\n                605.6686280492661,\n                82.11600561838574\n            ],\n            [\n                -152.17154579695332,\n                601.1686280492661,\n                82.11600561838574\n            ],\n            [\n                -154.76962200830664,\n                599.6686280492661,\n                82.11600561838574\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long_updown/2025-1-15_22-59-8_216588711\",\n        \"gpt_instruction\": \"\\\" Rise towards the gray skyscraper , a tall and large building , then head straight , slightly turn left , and move ahead towards the red building with a brick facade , which is also large in size . Slightly stop in front of the building and fall towards the structure with a dark gray exterior featuring large windows and awnings displaying ' Fenico Bank ' signage , which is medium to large in size and of commercial type . \\\"\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250115_225913\",\n            \"20250115_225915\",\n            \"20250115_225918\",\n            \"20250115_225920\",\n            \"20250115_225922\",\n            \"20250115_225924\",\n            \"20250115_225926\",\n            \"20250115_225928\",\n            \"20250115_225931\",\n            \"20250115_225933\",\n            \"20250115_225935\",\n            \"20250115_225937\",\n            \"20250115_225939\",\n            \"20250115_225941\",\n            \"20250115_225948\",\n            \"20250115_225954\",\n            \"20250115_230001\",\n            \"20250115_230007\",\n            \"20250115_230013\",\n            \"20250115_230020\",\n            \"20250115_230026\",\n            \"20250115_230032\",\n            \"20250115_230039\",\n            \"20250115_230046\",\n            \"20250115_230052\",\n            \"20250115_230056\",\n            \"20250115_230059\",\n            \"20250115_230105\",\n            \"20250115_230112\",\n            \"20250115_230118\",\n            \"20250115_230122\",\n            \"20250115_230125\",\n            \"20250115_230127\",\n            \"20250115_230129\",\n            \"20250115_230131\",\n            \"20250115_230133\",\n            \"20250115_230135\",\n            \"20250115_230137\",\n            \"20250115_230139\",\n            \"20250115_230142\",\n            \"20250115_230144\",\n            \"20250115_230146\",\n            \"20250115_230148\",\n            \"20250115_230150\",\n            \"20250115_230152\",\n            \"20250115_230154\"\n        ],\n        \"pos\": [\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                1.5191206106099457\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                4.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                7.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                10.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                13.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                16.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                19.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                22.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                25.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                28.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                31.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                34.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                37.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -370.9493506643503,\n                40.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -364.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -355.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -346.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -337.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -328.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -319.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -310.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -301.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -292.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -283.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -274.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -268.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -381.20997872842986,\n                -265.9493506643503,\n                43.519120610609946\n            ],\n            [\n                -384.20997872842986,\n                -260.75319824164364,\n                43.519120610609946\n            ],\n            [\n                -388.70997872842986,\n                -252.9589696075837,\n                43.519120610609946\n            ],\n            [\n                -393.20997872842986,\n                -245.16474097352375,\n                43.519120610609946\n            ],\n            [\n                -396.20997872842986,\n                -239.96858855081712,\n                43.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                43.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                40.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                37.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                34.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                31.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                28.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                25.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                22.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                19.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                16.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                13.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                10.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                7.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                4.519120610609946\n            ],\n            [\n                -397.70997872842986,\n                -237.3705123394638,\n                1.5191206106099457\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long/2025-1-4_3-21-50_389040743\",\n        \"gpt_instruction\": \"Proceed directly to the dark structure with lighter window frames , characterized by a glass facade revealing visible interiors . It is a high - rise commercial office building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_032157\",\n            \"20250104_032204\",\n            \"20250104_032210\",\n            \"20250104_032217\",\n            \"20250104_032223\",\n            \"20250104_032229\",\n            \"20250104_032236\",\n            \"20250104_032242\",\n            \"20250104_032249\",\n            \"20250104_032256\",\n            \"20250104_032302\",\n            \"20250104_032308\",\n            \"20250104_032315\",\n            \"20250104_032321\",\n            \"20250104_032325\",\n            \"20250104_032327\"\n        ],\n        \"pos\": [\n            [\n                439.45185083660954,\n                -191.35277375877666,\n                82.11637147865034\n            ],\n            [\n                431.6576222025496,\n                -186.85277375877666,\n                82.11637147865034\n            ],\n            [\n                423.86339356848964,\n                -182.35277375877666,\n                82.11637147865034\n            ],\n            [\n                416.0691649344297,\n                -177.85277375877666,\n                82.11637147865034\n            ],\n            [\n                408.27493630036975,\n                -173.35277375877666,\n                82.11637147865034\n            ],\n            [\n                400.4807076663098,\n                -168.85277375877666,\n                82.11637147865034\n            ],\n            [\n                392.68647903224985,\n                -164.35277375877666,\n                82.11637147865034\n            ],\n            [\n                384.8922503981899,\n                -159.85277375877666,\n                82.11637147865034\n            ],\n            [\n                377.09802176412995,\n                -155.35277375877666,\n                82.11637147865034\n            ],\n            [\n                369.30379313007,\n                -150.85277375877666,\n                82.11637147865034\n            ],\n            [\n                361.50956449601006,\n                -146.35277375877666,\n                82.11637147865034\n            ],\n            [\n                353.7153358619501,\n                -141.85277375877666,\n                82.11637147865034\n            ],\n            [\n                345.92110722789016,\n                -137.35277375877666,\n                82.11637147865034\n            ],\n            [\n                338.1268785938302,\n                -132.85277375877666,\n                82.11637147865034\n            ],\n            [\n                332.9307261711236,\n                -129.85277375877666,\n                82.11637147865034\n            ],\n            [\n                330.33264995977027,\n                -128.35277375877666,\n                82.11637147865034\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-4_15-11-34_2138704485\",\n        \"gpt_instruction\": \"Proceed directly to the building characterized by its red - brick color , tall grid windows , and large size . Then slightly turn right and proceed straight to it .\",\n        \"action\": [\n            9,\n            8,\n            3,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_151141\",\n            \"20250104_151145\",\n            \"20250104_151147\",\n            \"20250104_151154\",\n            \"20250104_151201\",\n            \"20250104_151203\"\n        ],\n        \"pos\": [\n            [\n                -838.0910923583169,\n                -202.69309224157075,\n                94.75972096224503\n            ],\n            [\n                -835.0910923583169,\n                -197.49693981886412,\n                94.75972096224503\n            ],\n            [\n                -833.5910923583169,\n                -194.8988636075108,\n                94.75972096224503\n            ],\n            [\n                -828.3949399356102,\n                -191.8988636075108,\n                94.75972096224503\n            ],\n            [\n                -820.6007113015501,\n                -187.3988636075108,\n                94.75972096224503\n            ],\n            [\n                -818.0026350901967,\n                -185.8988636075108,\n                94.75972096224503\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-3_22-8-45_409544918\",\n        \"gpt_instruction\": \"Walk straight to a large gray building featuring a flat rooftop with structures , then slightly turn right and head straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            1,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_220854\",\n            \"20250103_220901\",\n            \"20250103_220907\",\n            \"20250103_220909\",\n            \"20250103_220911\",\n            \"20250103_220917\",\n            \"20250103_220919\"\n        ],\n        \"pos\": [\n            [\n                58.695027473040795,\n                35.145871449412915,\n                84.1107394771899\n            ],\n            [\n                58.695027473040795,\n                26.145871449412915,\n                84.1107394771899\n            ],\n            [\n                58.695027473040795,\n                17.145871449412915,\n                84.1107394771899\n            ],\n            [\n                58.695027473040795,\n                14.145871449412915,\n                84.1107394771899\n            ],\n            [\n                58.695027473040795,\n                11.145871449412915,\n                84.1107394771899\n            ],\n            [\n                55.695027473040795,\n                5.949719026706283,\n                84.1107394771899\n            ],\n            [\n                54.195027473040795,\n                3.3516428153529674,\n                84.1107394771899\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_average/2025-1-4_13-43-1_86086317\",\n        \"gpt_instruction\": \"Proceed by heading directly towards the dark brown skyscraper that is tall with many windows and large in size , then slightly turn left to face the gray , tall rectangular building , also large and filled with numerous windows . Finally , slightly turn right and continue straight towards the dark red skyscraper , a tall brick structure with detailed ornamental designs .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_134310\",\n            \"20250104_134317\",\n            \"20250104_134323\",\n            \"20250104_134330\",\n            \"20250104_134336\",\n            \"20250104_134343\",\n            \"20250104_134345\",\n            \"20250104_134351\",\n            \"20250104_134358\",\n            \"20250104_134404\",\n            \"20250104_134411\",\n            \"20250104_134418\",\n            \"20250104_134420\",\n            \"20250104_134422\",\n            \"20250104_134426\",\n            \"20250104_134428\"\n        ],\n        \"pos\": [\n            [\n                -252.13808922506223,\n                -57.94175259329754,\n                72.94860843083018\n            ],\n            [\n                -244.34386059100228,\n                -53.44175259329754,\n                72.94860843083018\n            ],\n            [\n                -236.54963195694233,\n                -48.94175259329754,\n                72.94860843083018\n            ],\n            [\n                -228.7554033228824,\n                -44.44175259329754,\n                72.94860843083018\n            ],\n            [\n                -220.96117468882244,\n                -39.94175259329754,\n                72.94860843083018\n            ],\n            [\n                -213.1669460547625,\n                -35.44175259329754,\n                72.94860843083018\n            ],\n            [\n                -210.56886984340917,\n                -33.94175259329754,\n                72.94860843083018\n            ],\n            [\n                -207.56886984340917,\n                -28.745600170590905,\n                72.94860843083018\n            ],\n            [\n                -203.06886984340917,\n                -20.951371536530957,\n                72.94860843083018\n            ],\n            [\n                -198.56886984340917,\n                -13.157142902471008,\n                72.94860843083018\n            ],\n            [\n                -194.06886984340917,\n                -5.3629142684110604,\n                72.94860843083018\n            ],\n            [\n                -189.56886984340917,\n                2.4313143656488876,\n                72.94860843083018\n            ],\n            [\n                -188.06886984340917,\n                5.029390577002204,\n                72.94860843083018\n            ],\n            [\n                -186.56886984340917,\n                7.62746678835552,\n                72.94860843083018\n            ],\n            [\n                -183.97079363205586,\n                9.12746678835552,\n                72.94860843083018\n            ],\n            [\n                -181.37271742070254,\n                10.62746678835552,\n                72.94860843083018\n            ]\n        ],\n        \"yaw\": [\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_average/2025-1-5_18-20-15_675161631\",\n        \"gpt_instruction\": \"Begin by advancing to a location characterized by various shades of grey and beige , marked by a cluster of large high - rise skyscrapers . Then , slightly turn left and proceed straight towards a site with gray color , featuring medium - sized rectangular windows typical of an office building .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250105_182025\",\n            \"20250105_182031\",\n            \"20250105_182035\",\n            \"20250105_182038\",\n            \"20250105_182044\",\n            \"20250105_182051\",\n            \"20250105_182057\",\n            \"20250105_182103\",\n            \"20250105_182110\",\n            \"20250105_182116\",\n            \"20250105_182123\",\n            \"20250105_182129\",\n            \"20250105_182136\",\n            \"20250105_182138\"\n        ],\n        \"pos\": [\n            [\n                -777.6641705931704,\n                -304.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -768.6641705931704,\n                -304.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -762.6641705931704,\n                -304.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -759.6641705931704,\n                -304.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -754.4680181704637,\n                -301.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -746.6737895364035,\n                -296.9699917085836,\n                85.13619182295818\n            ],\n            [\n                -738.8795609023434,\n                -292.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -731.0853322682833,\n                -287.9699917085836,\n                85.13619182295818\n            ],\n            [\n                -723.2911036342232,\n                -283.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -715.4968750001631,\n                -278.9699917085836,\n                85.13619182295818\n            ],\n            [\n                -707.702646366103,\n                -274.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -699.9084177320428,\n                -269.9699917085836,\n                85.13619182295818\n            ],\n            [\n                -692.1141890979827,\n                -265.4699917085836,\n                85.13619182295818\n            ],\n            [\n                -689.5161128866293,\n                -263.9699917085836,\n                85.13619182295818\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_average/2025-01-19_09-30-44_574392\",\n        \"gpt_instruction\": \"Proceed straight to the large grey building with a grid - like window pattern , then go upwards to the large gray building with a similar grid - like pattern of windows . Slightly turn left and walk straight to the medium gray building featuring a rooftop with structures and equipment . Climb to it . Slightly move straight and slightly upward , then advance forward to it . Proceed downwards to it . Slightly turn right and move forward to it . Finally , lower down to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            4,\n            4,\n            2,\n            9,\n            1,\n            4,\n            4,\n            1,\n            4,\n            9,\n            9,\n            9,\n            8,\n            5,\n            5,\n            5,\n            5,\n            3,\n            8,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_093055\",\n            \"20250119_093102\",\n            \"20250119_093109\",\n            \"20250119_093115\",\n            \"20250119_093122\",\n            \"20250119_093128\",\n            \"20250119_093132\",\n            \"20250119_093135\",\n            \"20250119_093137\",\n            \"20250119_093139\",\n            \"20250119_093146\",\n            \"20250119_093148\",\n            \"20250119_093150\",\n            \"20250119_093152\",\n            \"20250119_093154\",\n            \"20250119_093157\",\n            \"20250119_093203\",\n            \"20250119_093210\",\n            \"20250119_093216\",\n            \"20250119_093221\",\n            \"20250119_093223\",\n            \"20250119_093225\",\n            \"20250119_093228\",\n            \"20250119_093230\",\n            \"20250119_093232\",\n            \"20250119_093236\",\n            \"20250119_093238\",\n            \"20250119_093241\"\n        ],\n        \"pos\": [\n            [\n                39.909236746682595,\n                133.3944718950617,\n                74.57452441666221\n            ],\n            [\n                44.409236746682595,\n                141.18870052912166,\n                74.57452441666221\n            ],\n            [\n                48.909236746682595,\n                148.9829291631816,\n                74.57452441666221\n            ],\n            [\n                53.409236746682595,\n                156.77715779724156,\n                74.57452441666221\n            ],\n            [\n                57.909236746682595,\n                164.5713864313015,\n                74.57452441666221\n            ],\n            [\n                62.409236746682595,\n                172.36561506536145,\n                74.57452441666221\n            ],\n            [\n                65.4092367466826,\n                177.56176748806809,\n                74.57452441666221\n            ],\n            [\n                66.9092367466826,\n                180.1598436994214,\n                74.57452441666221\n            ],\n            [\n                66.9092367466826,\n                180.1598436994214,\n                77.57452441666221\n            ],\n            [\n                66.9092367466826,\n                180.1598436994214,\n                80.57452441666221\n            ],\n            [\n                66.9092367466826,\n                186.1598436994214,\n                80.57452441666221\n            ],\n            [\n                66.9092367466826,\n                189.1598436994214,\n                80.57452441666221\n            ],\n            [\n                66.9092367466826,\n                192.1598436994214,\n                80.57452441666221\n            ],\n            [\n                66.9092367466826,\n                192.1598436994214,\n                83.57452441666221\n            ],\n            [\n                66.9092367466826,\n                192.1598436994214,\n                86.57452441666221\n            ],\n            [\n                66.9092367466826,\n                195.1598436994214,\n                86.57452441666221\n            ],\n            [\n                66.9092367466826,\n                201.1598436994214,\n                89.57452441666221\n            ],\n            [\n                66.9092367466826,\n                210.1598436994214,\n                89.57452441666221\n            ],\n            [\n                66.9092367466826,\n                219.1598436994214,\n                89.57452441666221\n            ],\n            [\n                66.9092367466826,\n                225.1598436994214,\n                89.57452441666221\n            ],\n            [\n                66.9092367466826,\n                228.1598436994214,\n                89.57452441666221\n            ],\n            [\n                66.9092367466826,\n                228.1598436994214,\n                86.57452441666221\n            ],\n            [\n                66.9092367466826,\n                228.1598436994214,\n                83.57452441666221\n            ],\n            [\n                66.9092367466826,\n                228.1598436994214,\n                80.57452441666221\n            ],\n            [\n                66.9092367466826,\n                228.1598436994214,\n                77.57452441666221\n            ],\n            [\n                68.4092367466826,\n                230.75791991077472,\n                77.57452441666221\n            ],\n            [\n                69.9092367466826,\n                233.35599612212803,\n                77.57452441666221\n            ],\n            [\n                69.9092367466826,\n                233.35599612212803,\n                74.57452441666221\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_long/2025-01-19_10-44-52_474712\",\n        \"gpt_instruction\": \"Advance forward to a large gray cylindrical structure with conical roofs , which is a water tower , then slightly ascend and continue straight towards another large gray structure with a rooftop containing HVAC units , identified as a building . Finally , move down to a medium - height , wide building with a red and gray ornate facade and decorative elements .\",\n        \"action\": [\n            9,\n            9,\n            4,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_104503\",\n            \"20250119_104510\",\n            \"20250119_104512\",\n            \"20250119_104518\",\n            \"20250119_104524\",\n            \"20250119_104531\",\n            \"20250119_104537\",\n            \"20250119_104544\",\n            \"20250119_104550\",\n            \"20250119_104557\",\n            \"20250119_104603\",\n            \"20250119_104610\",\n            \"20250119_104616\",\n            \"20250119_104622\",\n            \"20250119_104629\",\n            \"20250119_104635\",\n            \"20250119_104642\",\n            \"20250119_104644\",\n            \"20250119_104646\"\n        ],\n        \"pos\": [\n            [\n                -303.99642103056436,\n                220.05263045047758,\n                70.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                211.05263045047758,\n                70.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                208.05263045047758,\n                70.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                202.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                193.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                184.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                175.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                166.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                157.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                148.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                139.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                130.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                121.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                112.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                103.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                94.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                85.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                82.05263045047758,\n                73.69270354394726\n            ],\n            [\n                -303.99642103056436,\n                82.05263045047758,\n                70.69270354394726\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long_updown/2025-1-15_23-1-56_1319041805\",\n        \"gpt_instruction\": \"Start by ascending to a large light gray building featuring rectangular windows with black accents . Then proceed in it . Slightly turn left and continue forward to it . Take a right turn towards it . Continue directly onward to it . Slightly turn right and move directly forward to a large brown building with rectangular windows . Slightly turn right again and head straight towards a large green building with a dome and spire . Finally , come to a gentle stop at a medium - sized gray commercial building adorned with ornate detailing and large windows .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            2,\n            8,\n            3,\n            3,\n            9,\n            1,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250115_230159\",\n            \"20250115_230201\",\n            \"20250115_230203\",\n            \"20250115_230205\",\n            \"20250115_230208\",\n            \"20250115_230210\",\n            \"20250115_230212\",\n            \"20250115_230214\",\n            \"20250115_230216\",\n            \"20250115_230218\",\n            \"20250115_230221\",\n            \"20250115_230223\",\n            \"20250115_230229\",\n            \"20250115_230236\",\n            \"20250115_230242\",\n            \"20250115_230245\",\n            \"20250115_230249\",\n            \"20250115_230251\",\n            \"20250115_230253\",\n            \"20250115_230259\",\n            \"20250115_230302\",\n            \"20250115_230304\",\n            \"20250115_230310\",\n            \"20250115_230317\",\n            \"20250115_230324\",\n            \"20250115_230331\",\n            \"20250115_230337\",\n            \"20250115_230344\",\n            \"20250115_230346\",\n            \"20250115_230353\",\n            \"20250115_230359\",\n            \"20250115_230406\",\n            \"20250115_230412\",\n            \"20250115_230419\",\n            \"20250115_230425\",\n            \"20250115_230429\",\n            \"20250115_230432\",\n            \"20250115_230434\",\n            \"20250115_230436\",\n            \"20250115_230438\",\n            \"20250115_230440\",\n            \"20250115_230443\",\n            \"20250115_230445\",\n            \"20250115_230447\",\n            \"20250115_230449\",\n            \"20250115_230452\",\n            \"20250115_230454\",\n            \"20250115_230456\",\n            \"20250115_230458\"\n        ],\n        \"pos\": [\n            [\n                600.1596131613109,\n                288.86784316565695,\n                0.7757794567648659\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                3.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                6.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                9.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                12.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                15.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                18.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                21.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                24.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                27.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                30.775779456764866\n            ],\n            [\n                600.1596131613109,\n                288.86784316565695,\n                33.775779456764866\n            ],\n            [\n                600.1596131613109,\n                282.86784316565695,\n                36.775779456764866\n            ],\n            [\n                600.1596131613109,\n                273.86784316565695,\n                36.775779456764866\n            ],\n            [\n                600.1596131613109,\n                264.86784316565695,\n                36.775779456764866\n            ],\n            [\n                600.1596131613109,\n                261.86784316565695,\n                36.775779456764866\n            ],\n            [\n                601.6596131613109,\n                259.26976695430363,\n                36.775779456764866\n            ],\n            [\n                603.1596131613109,\n                256.6716907429503,\n                36.775779456764866\n            ],\n            [\n                603.1596131613109,\n                256.6716907429503,\n                36.775779456764866\n            ],\n            [\n                600.1596131613109,\n                251.4755383202437,\n                36.775779456764866\n            ],\n            [\n                598.6596131613109,\n                248.87746210889037,\n                36.775779456764866\n            ],\n            [\n                597.1596131613109,\n                246.27938589753705,\n                36.775779456764866\n            ],\n            [\n                591.9634607386042,\n                243.27938589753705,\n                36.775779456764866\n            ],\n            [\n                584.1692321045441,\n                238.77938589753705,\n                36.775779456764866\n            ],\n            [\n                576.375003470484,\n                234.27938589753705,\n                36.775779456764866\n            ],\n            [\n                568.5807748364239,\n                229.77938589753705,\n                36.775779456764866\n            ],\n            [\n                560.7865462023638,\n                225.27938589753705,\n                36.775779456764866\n            ],\n            [\n                552.9923175683036,\n                220.77938589753705,\n                36.775779456764866\n            ],\n            [\n                550.3942413569503,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                544.3942413569503,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                535.3942413569503,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                526.3942413569503,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                517.3942413569503,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                508.39424135695026,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                499.39424135695026,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                493.39424135695026,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                36.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                33.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                30.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                27.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                24.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                21.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                18.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                15.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                12.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                9.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                6.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                3.775779456764866\n            ],\n            [\n                490.39424135695026,\n                219.27938589753705,\n                0.7757794567648659\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_short/2025-1-4_14-39-17_1277970780\",\n        \"gpt_instruction\": \"Proceed directly to the left and then slightly turn towards a tall beige skyscraper , characterized by an ornate spire and intricate architectural details .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_143926\",\n            \"20250104_143933\",\n            \"20250104_143939\",\n            \"20250104_143945\",\n            \"20250104_143952\",\n            \"20250104_143954\",\n            \"20250104_143958\",\n            \"20250104_144001\"\n        ],\n        \"pos\": [\n            [\n                150.47475170911932,\n                204.84698379535808,\n                97.72387833616992\n            ],\n            [\n                145.97475170911932,\n                212.64121242941803,\n                97.72387833616992\n            ],\n            [\n                141.47475170911932,\n                220.43544106347798,\n                97.72387833616992\n            ],\n            [\n                136.97475170911932,\n                228.22966969753793,\n                97.72387833616992\n            ],\n            [\n                132.47475170911932,\n                236.02389833159788,\n                97.72387833616992\n            ],\n            [\n                130.97475170911932,\n                238.6219745429512,\n                97.72387833616992\n            ],\n            [\n                128.376675497766,\n                240.1219745429512,\n                97.72387833616992\n            ],\n            [\n                125.77859928641269,\n                241.6219745429512,\n                97.72387833616992\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.0943951023931953,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_short/2025-1-4_1-55-50_359147515\",\n        \"gpt_instruction\": \"Proceed directly to the medium - sized commercial building characterized by its gray and black color , featuring prominent vertical lines .\",\n        \"action\": [\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_015557\",\n            \"20250104_015600\",\n            \"20250104_015602\"\n        ],\n        \"pos\": [\n            [\n                -249.4591753887446,\n                -153.99245310257587,\n                81.49479121817774\n            ],\n            [\n                -246.86109917739128,\n                -152.49245310257587,\n                81.49479121817774\n            ],\n            [\n                -244.26302296603797,\n                -150.99245310257587,\n                81.49479121817774\n            ]\n        ],\n        \"yaw\": [\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-4_9-56-15_1680687005\",\n        \"gpt_instruction\": \"Proceed straight to the gray , large building with a rectangular rooftop covered in gravel .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_095622\",\n            \"20250104_095628\",\n            \"20250104_095635\",\n            \"20250104_095637\"\n        ],\n        \"pos\": [\n            [\n                354.56570356310056,\n                316.06109974769066,\n                82.00319576742102\n            ],\n            [\n                346.7714749290406,\n                320.56109974769066,\n                82.00319576742102\n            ],\n            [\n                338.97724629498066,\n                325.06109974769066,\n                82.00319576742102\n            ],\n            [\n                336.37917008362734,\n                326.56109974769066,\n                82.00319576742102\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long/2025-01-19_01-26-07_496226\",\n        \"gpt_instruction\": \"Begin by keeping straight towards the large gray building characterized by balconies with railings . After proceeding slightly upwards , head forward to it . Next , turn right slightly and proceed straight to reach a large dark gray skyscraper with a glass facade . Climb towards the brown building adorned with detailed cornices , then make a slight left turn and continue straight to a large brown building with a brick facade and large windows . Continue by slightly turning left and proceeding straight towards it . Ascend slightly , make a left turn , and move ahead to a large gray building with grid - like windows . Fall slightly towards it . Finally , slightly turn right and head straight towards it .\",\n        \"action\": [\n            9,\n            9,\n            4,\n            8,\n            3,\n            9,\n            9,\n            1,\n            4,\n            4,\n            2,\n            9,\n            1,\n            2,\n            9,\n            8,\n            4,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            5,\n            5,\n            5,\n            5,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_012618\",\n            \"20250119_012625\",\n            \"20250119_012627\",\n            \"20250119_012632\",\n            \"20250119_012634\",\n            \"20250119_012641\",\n            \"20250119_012647\",\n            \"20250119_012649\",\n            \"20250119_012652\",\n            \"20250119_012654\",\n            \"20250119_012656\",\n            \"20250119_012703\",\n            \"20250119_012705\",\n            \"20250119_012707\",\n            \"20250119_012714\",\n            \"20250119_012718\",\n            \"20250119_012721\",\n            \"20250119_012723\",\n            \"20250119_012729\",\n            \"20250119_012736\",\n            \"20250119_012743\",\n            \"20250119_012749\",\n            \"20250119_012755\",\n            \"20250119_012802\",\n            \"20250119_012809\",\n            \"20250119_012815\",\n            \"20250119_012817\",\n            \"20250119_012819\",\n            \"20250119_012822\",\n            \"20250119_012824\",\n            \"20250119_012826\",\n            \"20250119_012828\",\n            \"20250119_012830\",\n            \"20250119_012832\"\n        ],\n        \"pos\": [\n            [\n                59.98954253325998,\n                -301.88714702417224,\n                73.86332906422297\n            ],\n            [\n                50.98954253325998,\n                -301.88714702417224,\n                73.86332906422297\n            ],\n            [\n                47.98954253325998,\n                -301.88714702417224,\n                73.86332906422297\n            ],\n            [\n                44.98954253325998,\n                -301.88714702417224,\n                76.86332906422297\n            ],\n            [\n                41.98954253325998,\n                -301.88714702417224,\n                76.86332906422297\n            ],\n            [\n                36.79339011055335,\n                -298.88714702417224,\n                76.86332906422297\n            ],\n            [\n                28.9991614764934,\n                -294.38714702417224,\n                76.86332906422297\n            ],\n            [\n                26.401085265140082,\n                -292.88714702417224,\n                76.86332906422297\n            ],\n            [\n                23.803009053786766,\n                -291.38714702417224,\n                76.86332906422297\n            ],\n            [\n                23.803009053786766,\n                -291.38714702417224,\n                79.86332906422297\n            ],\n            [\n                23.803009053786766,\n                -291.38714702417224,\n                82.86332906422297\n            ],\n            [\n                17.803009053786766,\n                -291.38714702417224,\n                82.86332906422297\n            ],\n            [\n                14.803009053786766,\n                -291.38714702417224,\n                82.86332906422297\n            ],\n            [\n                11.803009053786766,\n                -291.38714702417224,\n                82.86332906422297\n            ],\n            [\n                6.606856631080134,\n                -294.38714702417224,\n                82.86332906422297\n            ],\n            [\n                1.4107042083735024,\n                -297.38714702417224,\n                82.86332906422297\n            ],\n            [\n                -1.1873720029798136,\n                -298.88714702417224,\n                82.86332906422297\n            ],\n            [\n                -1.1873720029798136,\n                -298.88714702417224,\n                85.86332906422297\n            ],\n            [\n                -4.187372002979812,\n                -304.08329944687887,\n                85.86332906422297\n            ],\n            [\n                -8.68737200297981,\n                -311.8775280809388,\n                85.86332906422297\n            ],\n            [\n                -13.18737200297981,\n                -319.67175671499876,\n                85.86332906422297\n            ],\n            [\n                -17.68737200297981,\n                -327.4659853490587,\n                85.86332906422297\n            ],\n            [\n                -22.18737200297981,\n                -335.26021398311866,\n                85.86332906422297\n            ],\n            [\n                -26.68737200297981,\n                -343.0544426171786,\n                85.86332906422297\n            ],\n            [\n                -31.18737200297981,\n                -350.84867125123856,\n                85.86332906422297\n            ],\n            [\n                -35.68737200297981,\n                -358.6428998852985,\n                85.86332906422297\n            ],\n            [\n                -37.18737200297981,\n                -361.2409760966518,\n                85.86332906422297\n            ],\n            [\n                -38.68737200297981,\n                -363.83905230800514,\n                85.86332906422297\n            ],\n            [\n                -38.68737200297981,\n                -363.83905230800514,\n                82.86332906422297\n            ],\n            [\n                -38.68737200297981,\n                -363.83905230800514,\n                79.86332906422297\n            ],\n            [\n                -38.68737200297981,\n                -363.83905230800514,\n                76.86332906422297\n            ],\n            [\n                -38.68737200297981,\n                -363.83905230800514,\n                73.86332906422297\n            ],\n            [\n                -38.68737200297981,\n                -363.83905230800514,\n                73.86332906422297\n            ],\n            [\n                -41.28544821433312,\n                -365.33905230800514,\n                73.86332906422297\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            2.6179938779914944,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.6179938779914944,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.617993877991494,\n            -2.617993877991494\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/high_short/2025-1-3_22-18-21_1725138377\",\n        \"gpt_instruction\": \"Proceed forward to the gray building with a modern facade featuring geometric patterns , then slightly turn left slightly to it . Both structures are medium in size .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_221829\",\n            \"20250103_221836\",\n            \"20250103_221842\",\n            \"20250103_221845\",\n            \"20250103_221851\",\n            \"20250103_221858\",\n            \"20250103_221900\"\n        ],\n        \"pos\": [\n            [\n                -278.1984049614343,\n                599.8699225452929,\n                97.6121602361494\n            ],\n            [\n                -273.6984049614343,\n                592.0756939112327,\n                97.6121602361494\n            ],\n            [\n                -269.1984049614343,\n                584.2814652771726,\n                97.6121602361494\n            ],\n            [\n                -267.6984049614343,\n                581.6833890658193,\n                97.6121602361494\n            ],\n            [\n                -262.50225253872765,\n                578.6833890658193,\n                97.6121602361494\n            ],\n            [\n                -254.7080239046677,\n                574.1833890658193,\n                97.6121602361494\n            ],\n            [\n                -252.10994769331438,\n                572.6833890658193,\n                97.6121602361494\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_short/2025-1-4_2-19-15_1230769829\",\n        \"gpt_instruction\": \"Proceed straight towards the gray building with a large rooftop adorned with decorative elements , then make a slight left turn and continue moving forward towards the tall brown skyscraper featuring a grid - like pattern and large windows .\",\n        \"action\": [\n            9,\n            9,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_021924\",\n            \"20250104_021930\",\n            \"20250104_021932\",\n            \"20250104_021934\",\n            \"20250104_021936\"\n        ],\n        \"pos\": [\n            [\n                447.81704693546067,\n                -483.85421809935195,\n                95.8115992095833\n            ],\n            [\n                456.81704693546067,\n                -483.85421809935195,\n                95.8115992095833\n            ],\n            [\n                459.81704693546067,\n                -483.85421809935195,\n                95.8115992095833\n            ],\n            [\n                459.81704693546067,\n                -483.85421809935195,\n                95.8115992095833\n            ],\n            [\n                462.415123146814,\n                -482.35421809935195,\n                95.8115992095833\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long/2025-1-4_10-3-20_2059344234\",\n        \"gpt_instruction\": \"Begin by walking straight , then slightly turn left and move forward towards a building that is light grey in color , featuring a modern design with large windows , medium in size , and used for commercial purposes .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_100329\",\n            \"20250104_100336\",\n            \"20250104_100342\",\n            \"20250104_100348\",\n            \"20250104_100354\",\n            \"20250104_100401\",\n            \"20250104_100407\",\n            \"20250104_100413\",\n            \"20250104_100419\",\n            \"20250104_100422\",\n            \"20250104_100424\",\n            \"20250104_100430\",\n            \"20250104_100436\",\n            \"20250104_100442\",\n            \"20250104_100449\",\n            \"20250104_100455\",\n            \"20250104_100501\",\n            \"20250104_100503\"\n        ],\n        \"pos\": [\n            [\n                -782.2299802063391,\n                -351.91991318124144,\n                75.2001444904755\n            ],\n            [\n                -777.7299802063391,\n                -344.1256845471815,\n                75.2001444904755\n            ],\n            [\n                -773.2299802063391,\n                -336.33145591312154,\n                75.2001444904755\n            ],\n            [\n                -768.7299802063391,\n                -328.5372272790616,\n                75.2001444904755\n            ],\n            [\n                -764.2299802063391,\n                -320.74299864500165,\n                75.2001444904755\n            ],\n            [\n                -759.7299802063391,\n                -312.9487700109417,\n                75.2001444904755\n            ],\n            [\n                -755.2299802063391,\n                -305.15454137688175,\n                75.2001444904755\n            ],\n            [\n                -750.7299802063391,\n                -297.3603127428218,\n                75.2001444904755\n            ],\n            [\n                -746.2299802063391,\n                -289.56608410876186,\n                75.2001444904755\n            ],\n            [\n                -744.7299802063391,\n                -286.96800789740854,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -284.3699316860552,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -278.3699316860552,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -269.3699316860552,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -260.3699316860552,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -251.36993168605522,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -242.36993168605522,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -233.36993168605522,\n                75.2001444904755\n            ],\n            [\n                -743.2299802063391,\n                -230.36993168605522,\n                75.2001444904755\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_short/2025-1-4_15-26-30_1450967523\",\n        \"gpt_instruction\": \"Proceed straight to the medium - sized , white , historical dome .\",\n        \"action\": [\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_152638\",\n            \"20250104_152640\",\n            \"20250104_152643\"\n        ],\n        \"pos\": [\n            [\n                170.0471964454638,\n                110.40879574298776,\n                73.47055209501686\n            ],\n            [\n                171.5471964454638,\n                113.00687195434108,\n                73.47055209501686\n            ],\n            [\n                173.0471964454638,\n                115.6049481656944,\n                73.47055209501686\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_short/2025-1-4_2-9-25_1835342733\",\n        \"gpt_instruction\": \"Proceed directly towards a large gray building with a flat rooftop , then slightly turn left and continue straight to a medium - sized commercial building characterized by its light brown color and a grid - like window pattern .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_020933\",\n            \"20250104_020940\",\n            \"20250104_020946\",\n            \"20250104_020952\",\n            \"20250104_020954\",\n            \"20250104_020958\",\n            \"20250104_021000\"\n        ],\n        \"pos\": [\n            [\n                -614.932129899286,\n                47.83549177732935,\n                71.0806903159789\n            ],\n            [\n                -614.932129899286,\n                38.83549177732935,\n                71.0806903159789\n            ],\n            [\n                -614.932129899286,\n                29.835491777329352,\n                71.0806903159789\n            ],\n            [\n                -614.932129899286,\n                20.835491777329352,\n                71.0806903159789\n            ],\n            [\n                -614.932129899286,\n                17.835491777329352,\n                71.0806903159789\n            ],\n            [\n                -613.432129899286,\n                15.237415565976036,\n                71.0806903159789\n            ],\n            [\n                -611.932129899286,\n                12.63933935462272,\n                71.0806903159789\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_average/2025-01-19_00-05-49_898076\",\n        \"gpt_instruction\": \"Start by walking straight and then slightly turn right to reach a gray building ornamented with medium decorative architectural designs . Then , proceed slightly upwards and continue moving straight to a gray building featuring a large grid - like pattern with windows . Climb slightly to it . Again , turn slightly left and progress to a tall red skyscraper with a brick fa\\u00e7ade . Drop down to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            8,\n            4,\n            8,\n            4,\n            4,\n            2,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            5,\n            5,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_000559\",\n            \"20250119_000606\",\n            \"20250119_000612\",\n            \"20250119_000619\",\n            \"20250119_000621\",\n            \"20250119_000628\",\n            \"20250119_000632\",\n            \"20250119_000634\",\n            \"20250119_000638\",\n            \"20250119_000640\",\n            \"20250119_000642\",\n            \"20250119_000644\",\n            \"20250119_000648\",\n            \"20250119_000651\",\n            \"20250119_000657\",\n            \"20250119_000704\",\n            \"20250119_000710\",\n            \"20250119_000716\",\n            \"20250119_000718\",\n            \"20250119_000721\",\n            \"20250119_000725\",\n            \"20250119_000727\"\n        ],\n        \"pos\": [\n            [\n                28.806791841440017,\n                142.90988479343326,\n                79.5618257476354\n            ],\n            [\n                33.30679184144002,\n                150.7041134274932,\n                79.5618257476354\n            ],\n            [\n                37.80679184144002,\n                158.49834206155316,\n                79.5618257476354\n            ],\n            [\n                42.30679184144002,\n                166.2925706956131,\n                79.5618257476354\n            ],\n            [\n                43.80679184144002,\n                168.89064690696642,\n                79.5618257476354\n            ],\n            [\n                49.00294426414665,\n                171.89064690696642,\n                79.5618257476354\n            ],\n            [\n                54.19909668685328,\n                174.89064690696642,\n                79.5618257476354\n            ],\n            [\n                56.7971728982066,\n                176.39064690696642,\n                79.5618257476354\n            ],\n            [\n                59.39524910955991,\n                177.89064690696642,\n                82.5618257476354\n            ],\n            [\n                61.99332532091323,\n                179.39064690696642,\n                82.5618257476354\n            ],\n            [\n                61.99332532091323,\n                179.39064690696642,\n                85.5618257476354\n            ],\n            [\n                61.99332532091323,\n                179.39064690696642,\n                88.5618257476354\n            ],\n            [\n                63.49332532091323,\n                181.98872311831974,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                184.58679932967306,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                190.58679932967306,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                199.58679932967306,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                208.58679932967306,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                217.58679932967306,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                220.58679932967306,\n                88.5618257476354\n            ],\n            [\n                64.99332532091323,\n                220.58679932967306,\n                85.5618257476354\n            ],\n            [\n                64.99332532091323,\n                223.58679932967306,\n                82.5618257476354\n            ],\n            [\n                64.99332532091323,\n                226.58679932967306,\n                82.5618257476354\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_smallcity/astar_data/low_long/2025-1-4_2-42-37_669908538\",\n        \"gpt_instruction\": \"Begin by walking straight towards a large gray building with an ornate design featuring vertical lines , then slightly turn right and continue straight until you reach a tall light - colored skyscraper adorned with an ornate facade , including vertical columns and decorative details .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_024246\",\n            \"20250104_024253\",\n            \"20250104_024259\",\n            \"20250104_024305\",\n            \"20250104_024307\",\n            \"20250104_024309\",\n            \"20250104_024314\",\n            \"20250104_024316\"\n        ],\n        \"pos\": [\n            [\n                62.986501307984334,\n                641.1338158317301,\n                71.7455514149568\n            ],\n            [\n                62.986501307984334,\n                632.1338158317301,\n                71.7455514149568\n            ],\n            [\n                62.986501307984334,\n                623.1338158317301,\n                71.7455514149568\n            ],\n            [\n                62.986501307984334,\n                614.1338158317301,\n                71.7455514149568\n            ],\n            [\n                62.986501307984334,\n                611.1338158317301,\n                71.7455514149568\n            ],\n            [\n                62.986501307984334,\n                608.1338158317301,\n                71.7455514149568\n            ],\n            [\n                61.486501307984334,\n                605.5357396203767,\n                71.7455514149568\n            ],\n            [\n                59.986501307984334,\n                602.9376634090233,\n                71.7455514149568\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_short/2025-1-4_0-6-53_1371499336\",\n        \"gpt_instruction\": \"Proceed straight towards a large gray building characterized by an ornate vertical design with arched windows . Then , slightly turn left and move ahead to reach another building , which is tall and grey , featuring a Gothic - inspired ornate facade with a large square rooftop .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_000702\",\n            \"20250104_000708\",\n            \"20250104_000715\",\n            \"20250104_000717\",\n            \"20250104_000724\",\n            \"20250104_000730\",\n            \"20250104_000732\"\n        ],\n        \"pos\": [\n            [\n                -898.3217820074168,\n                1860.4073062156806,\n                81.31847177484579\n            ],\n            [\n                -893.8217820074168,\n                1852.6130775816205,\n                81.31847177484579\n            ],\n            [\n                -889.3217820074168,\n                1844.8188489475604,\n                81.31847177484579\n            ],\n            [\n                -887.8217820074168,\n                1842.220772736207,\n                81.31847177484579\n            ],\n            [\n                -882.6256295847102,\n                1839.220772736207,\n                81.31847177484579\n            ],\n            [\n                -874.83140095065,\n                1834.720772736207,\n                81.31847177484579\n            ],\n            [\n                -872.2333247392967,\n                1833.220772736207,\n                81.31847177484579\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982894,\n            -0.5235987755982894,\n            -0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-4_18-54-31_896925393\",\n        \"gpt_instruction\": \"Move ahead to a grey structure characterized by multiple buildings with flat roofs and visible rooftop equipment ; these urban buildings are medium - sized . Slightly turn left and proceed straight to a red structure featuring multiple stories with a flat roof , which is large and occupies a significant portion of the city block , simply categorized as a building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_185440\",\n            \"20250104_185447\",\n            \"20250104_185454\",\n            \"20250104_185501\",\n            \"20250104_185508\",\n            \"20250104_185515\",\n            \"20250104_185521\",\n            \"20250104_185528\",\n            \"20250104_185535\",\n            \"20250104_185542\",\n            \"20250104_185548\",\n            \"20250104_185555\",\n            \"20250104_185557\",\n            \"20250104_185559\",\n            \"20250104_185602\"\n        ],\n        \"pos\": [\n            [\n                227.44577715727644,\n                1191.476408419026,\n                74.38574823545414\n            ],\n            [\n                222.94577715727644,\n                1199.2706370530861,\n                74.38574823545414\n            ],\n            [\n                218.44577715727644,\n                1207.0648656871463,\n                74.38574823545414\n            ],\n            [\n                213.94577715727644,\n                1214.8590943212064,\n                74.38574823545414\n            ],\n            [\n                209.44577715727644,\n                1222.6533229552665,\n                74.38574823545414\n            ],\n            [\n                204.94577715727644,\n                1230.4475515893266,\n                74.38574823545414\n            ],\n            [\n                200.44577715727644,\n                1238.2417802233867,\n                74.38574823545414\n            ],\n            [\n                195.94577715727644,\n                1246.0360088574469,\n                74.38574823545414\n            ],\n            [\n                191.44577715727644,\n                1253.830237491507,\n                74.38574823545414\n            ],\n            [\n                186.94577715727644,\n                1261.624466125567,\n                74.38574823545414\n            ],\n            [\n                182.44577715727644,\n                1269.4186947596272,\n                74.38574823545414\n            ],\n            [\n                177.94577715727644,\n                1277.2129233936873,\n                74.38574823545414\n            ],\n            [\n                176.44577715727644,\n                1279.8109996050407,\n                74.38574823545414\n            ],\n            [\n                176.44577715727644,\n                1279.8109996050407,\n                74.38574823545414\n            ],\n            [\n                173.84770094592312,\n                1281.3109996050407,\n                74.38574823545414\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779914846,\n            2.6179938779914846\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-5_16-24-9_193088109\",\n        \"gpt_instruction\": \"Begin by walking straight towards the white building with colonial architecture and a columned facade . Then , slightly turn right and advance forward to another white building featuring decorative rooftop structures . Finally , slightly turn left slightly and proceed to it . All three buildings are medium - sized .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250105_162419\",\n            \"20250105_162425\",\n            \"20250105_162432\",\n            \"20250105_162438\",\n            \"20250105_162445\",\n            \"20250105_162449\",\n            \"20250105_162451\",\n            \"20250105_162458\",\n            \"20250105_162504\",\n            \"20250105_162511\",\n            \"20250105_162515\",\n            \"20250105_162518\",\n            \"20250105_162524\",\n            \"20250105_162527\"\n        ],\n        \"pos\": [\n            [\n                1768.3698216426194,\n                222.33732963247493,\n                86.56676127637375\n            ],\n            [\n                1763.8698216426194,\n                214.54310099841499,\n                86.56676127637375\n            ],\n            [\n                1759.3698216426194,\n                206.74887236435504,\n                86.56676127637375\n            ],\n            [\n                1754.8698216426194,\n                198.9546437302951,\n                86.56676127637375\n            ],\n            [\n                1750.3698216426194,\n                191.16041509623514,\n                86.56676127637375\n            ],\n            [\n                1747.3698216426194,\n                185.9642626735285,\n                86.56676127637375\n            ],\n            [\n                1745.8698216426194,\n                183.3661864621752,\n                86.56676127637375\n            ],\n            [\n                1740.6736692199127,\n                180.3661864621752,\n                86.56676127637375\n            ],\n            [\n                1732.8794405858525,\n                175.8661864621752,\n                86.56676127637375\n            ],\n            [\n                1725.0852119517924,\n                171.3661864621752,\n                86.56676127637375\n            ],\n            [\n                1719.8890595290857,\n                168.3661864621752,\n                86.56676127637375\n            ],\n            [\n                1717.2909833177323,\n                166.8661864621752,\n                86.56676127637375\n            ],\n            [\n                1714.2909833177323,\n                161.67003403946856,\n                86.56676127637375\n            ],\n            [\n                1712.7909833177323,\n                159.07195782811525,\n                86.56676127637375\n            ]\n        ],\n        \"yaw\": [\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-6_4-44-51_1730441138\",\n        \"gpt_instruction\": \"Proceed forward to the tall , pink historic building with an ornamental facade . Slightly turn left and keep going straight until you reach the medium - sized , reddish pink building featuring an ornate architectural design with decorative elements , prominent on the final right side .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_044458\",\n            \"20250106_044505\",\n            \"20250106_044511\",\n            \"20250106_044518\",\n            \"20250106_044525\",\n            \"20250106_044531\",\n            \"20250106_044538\",\n            \"20250106_044545\",\n            \"20250106_044551\",\n            \"20250106_044558\",\n            \"20250106_044602\",\n            \"20250106_044605\",\n            \"20250106_044611\",\n            \"20250106_044618\",\n            \"20250106_044622\",\n            \"20250106_044625\"\n        ],\n        \"pos\": [\n            [\n                1825.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1816.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1807.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1798.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1789.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1780.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1771.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1762.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1753.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1744.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1738.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1735.7409910475005,\n                171.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1730.5448386247938,\n                168.31579249528082,\n                91.99465667141385\n            ],\n            [\n                1722.7506099907337,\n                163.81579249528082,\n                91.99465667141385\n            ],\n            [\n                1717.554457568027,\n                160.81579249528082,\n                91.99465667141385\n            ],\n            [\n                1714.9563813566735,\n                159.31579249528082,\n                91.99465667141385\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_short/2025-1-4_4-17-28_395191309\",\n        \"gpt_instruction\": \"Move forward to a large white building with a rectangular layout featuring rows of columns in the facade . Slightly turn right and move forward again to it .\",\n        \"action\": [\n            9,\n            1,\n            3,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_041737\",\n            \"20250104_041739\",\n            \"20250104_041741\",\n            \"20250104_041746\",\n            \"20250104_041748\"\n        ],\n        \"pos\": [\n            [\n                -1342.9601467623686,\n                1818.488084044419,\n                98.99946561469405\n            ],\n            [\n                -1344.4601467623686,\n                1821.0861602557725,\n                98.99946561469405\n            ],\n            [\n                -1345.9601467623686,\n                1823.6842364671259,\n                98.99946561469405\n            ],\n            [\n                -1345.9601467623686,\n                1826.6842364671259,\n                98.99946561469405\n            ],\n            [\n                -1345.9601467623686,\n                1829.6842364671259,\n                98.99946561469405\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_average/2025-1-5_21-39-44_1804723202\",\n        \"gpt_instruction\": \"Proceed straight until you reach a large beige building characterized by rectangular windows and detailed cornices , then slightly turn right and move ahead towards another large beige building notable for its decorative detailing along the roofline and classic architectural design ; this structure is a large , historic , or classical - style building with multiple stories .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250105_213954\",\n            \"20250105_214000\",\n            \"20250105_214007\",\n            \"20250105_214013\",\n            \"20250105_214020\",\n            \"20250105_214024\",\n            \"20250105_214026\",\n            \"20250105_214028\",\n            \"20250105_214030\"\n        ],\n        \"pos\": [\n            [\n                439.7954800150849,\n                -1009.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -1000.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -991.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -982.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -973.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -967.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -964.5293732393421,\n                36.56868291696178\n            ],\n            [\n                439.7954800150849,\n                -964.5293732393421,\n                36.56868291696178\n            ],\n            [\n                441.2954800150849,\n                -961.9312970279889,\n                36.56868291696178\n            ]\n        ],\n        \"yaw\": [\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.5707963267948966,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-5_0-56-50_1591564428\",\n        \"gpt_instruction\": \"Head straight towards the brick red building characterized by ornate Gothic architecture with decorative spires and intricate stonework . Then , slightly turn left and move forward to the reddish - brown structure , which also features Gothic architecture with vertical lines and detailed intricacy . Both buildings are tall structures .\",\n        \"action\": [\n            9,\n            9,\n            1,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250105_005659\",\n            \"20250105_005706\",\n            \"20250105_005708\",\n            \"20250105_005710\",\n            \"20250105_005717\",\n            \"20250105_005719\"\n        ],\n        \"pos\": [\n            [\n                -971.5717354058067,\n                1895.7826594388087,\n                72.53484314768916\n            ],\n            [\n                -967.0717354058067,\n                1903.5768880728688,\n                72.53484314768916\n            ],\n            [\n                -965.5717354058067,\n                1906.1749642842221,\n                72.53484314768916\n            ],\n            [\n                -964.0717354058067,\n                1908.7730404955755,\n                72.53484314768916\n            ],\n            [\n                -964.0717354058067,\n                1914.7730404955755,\n                72.53484314768916\n            ],\n            [\n                -964.0717354058067,\n                1917.7730404955755,\n                72.53484314768916\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.5707963267948966,\n            1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_short/2025-1-3_14-52-57_726371155\",\n        \"gpt_instruction\": \"Proceed directly forward towards the gray clock tower , a medium - height historical building .\",\n        \"action\": [\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_145303\",\n            \"20250103_145306\"\n        ],\n        \"pos\": [\n            [\n                1696.7334892038268,\n                97.64722128250712,\n                41.39784707293993\n            ],\n            [\n                1695.2334892038268,\n                100.24529749386043,\n                41.39784707293993\n            ]\n        ],\n        \"yaw\": [\n            2.0943951023931953,\n            2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_average/2025-1-6_2-21-39_774318984\",\n        \"gpt_instruction\": \"Proceed straight to a large , modern commercial / office building characterized by its dark gray color , flat - roofed trapezoidal shape , and multiple rooftop HVAC installations . Then , slightly turn left and go directly ahead to a medium - sized building with a gray color and a distinctive rooftop water tank .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            2,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_022147\",\n            \"20250106_022153\",\n            \"20250106_022158\",\n            \"20250106_022200\",\n            \"20250106_022206\",\n            \"20250106_022213\",\n            \"20250106_022220\",\n            \"20250106_022226\",\n            \"20250106_022233\",\n            \"20250106_022240\",\n            \"20250106_022247\",\n            \"20250106_022253\",\n            \"20250106_022300\",\n            \"20250106_022302\",\n            \"20250106_022304\"\n        ],\n        \"pos\": [\n            [\n                -1715.5349065111607,\n                357.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1706.5349065111607,\n                357.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1700.5349065111607,\n                357.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1697.5349065111607,\n                357.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1692.338754088454,\n                360.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1684.5445254543938,\n                364.7613237734558,\n                74.84131733431539\n            ],\n            [\n                -1676.7502968203337,\n                369.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1668.9560681862736,\n                373.7613237734558,\n                74.84131733431539\n            ],\n            [\n                -1661.1618395522135,\n                378.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1653.3676109181533,\n                382.7613237734558,\n                74.84131733431539\n            ],\n            [\n                -1645.5733822840932,\n                387.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1637.779153650033,\n                391.7613237734558,\n                74.84131733431539\n            ],\n            [\n                -1629.984925015973,\n                396.2613237734558,\n                74.84131733431539\n            ],\n            [\n                -1627.3868488046196,\n                397.7613237734558,\n                74.84131733431539\n            ],\n            [\n                -1624.7887725932662,\n                399.2613237734558,\n                74.84131733431539\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_short/2025-1-4_10-51-16_1899542553\",\n        \"gpt_instruction\": \"Walk directly to the light beige structure with a dark roof , which is a medium - sized rectangular commercial building characterized by multiple windows and rooftop features , compared to the surrounding buildings .\",\n        \"action\": [\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_105122\",\n            \"20250104_105124\"\n        ],\n        \"pos\": [\n            [\n                1580.6586621072818,\n                -231.55103703131405,\n                94.4942190595873\n            ],\n            [\n                1578.0605858959284,\n                -230.05103703131405,\n                94.4942190595873\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_long/2025-1-4_10-24-59_699460008\",\n        \"gpt_instruction\": \"Continue ahead towards the brown clock tower building , slightly veer right and move directly towards the tall beige building with a water tower on top , then slightly veer right and proceed straight to the beige building with arched windows .\",\n        \"action\": [\n            9,\n            3,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_102508\",\n            \"20250104_102510\",\n            \"20250104_102517\",\n            \"20250104_102524\",\n            \"20250104_102526\",\n            \"20250104_102533\",\n            \"20250104_102539\",\n            \"20250104_102546\",\n            \"20250104_102553\",\n            \"20250104_102559\",\n            \"20250104_102606\",\n            \"20250104_102608\"\n        ],\n        \"pos\": [\n            [\n                1945.004749892771,\n                1138.7322428004052,\n                19.180366660026248\n            ],\n            [\n                1943.504749892771,\n                1136.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1938.3085974700643,\n                1133.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1930.5143688360042,\n                1128.6341665890518,\n                19.180366660026248\n            ],\n            [\n                1927.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1921.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1912.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1903.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1894.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1885.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1876.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ],\n            [\n                1873.9162926246509,\n                1127.1341665890518,\n                19.180366660026248\n            ]\n        ],\n        \"yaw\": [\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-01-19_10-37-42_336192\",\n        \"gpt_instruction\": \"Proceed straight towards the gray , medium - sized office building featuring modern architecture with a flat rooftop and window patterns . Slightly turn left and continue ahead , then slightly incline upwards to another gray , medium - sized building rooftop with concrete tiles and shadows . Make a slight right and move forward to a dark gray building rooftop with mechanical structures and panels , then descend to the dark gray , large office building with a flat roof accommodating various structures such as HVAC units and vents , spanning multiple floors .\",\n        \"action\": [\n            9,\n            9,\n            1,\n            2,\n            1,\n            4,\n            9,\n            8,\n            3,\n            9,\n            9,\n            1,\n            5,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_103756\",\n            \"20250119_103802\",\n            \"20250119_103805\",\n            \"20250119_103807\",\n            \"20250119_103809\",\n            \"20250119_103811\",\n            \"20250119_103817\",\n            \"20250119_103822\",\n            \"20250119_103824\",\n            \"20250119_103830\",\n            \"20250119_103837\",\n            \"20250119_103839\",\n            \"20250119_103841\",\n            \"20250119_103843\"\n        ],\n        \"pos\": [\n            [\n                575.7493945320062,\n                -223.50727489053966,\n                85.6111452585056\n            ],\n            [\n                584.7493945320062,\n                -223.50727489053966,\n                85.6111452585056\n            ],\n            [\n                587.7493945320062,\n                -223.50727489053966,\n                85.6111452585056\n            ],\n            [\n                590.7493945320062,\n                -223.50727489053966,\n                85.6111452585056\n            ],\n            [\n                590.7493945320062,\n                -223.50727489053966,\n                85.6111452585056\n            ],\n            [\n                593.3474707433595,\n                -222.00727489053966,\n                85.6111452585056\n            ],\n            [\n                598.5436231660663,\n                -219.00727489053966,\n                88.6111452585056\n            ],\n            [\n                603.739775588773,\n                -216.00727489053966,\n                88.6111452585056\n            ],\n            [\n                606.3378518001264,\n                -214.50727489053966,\n                88.6111452585056\n            ],\n            [\n                612.3378518001264,\n                -214.50727489053966,\n                88.6111452585056\n            ],\n            [\n                621.3378518001264,\n                -214.50727489053966,\n                88.6111452585056\n            ],\n            [\n                624.3378518001264,\n                -214.50727489053966,\n                88.6111452585056\n            ],\n            [\n                627.3378518001264,\n                -214.50727489053966,\n                88.6111452585056\n            ],\n            [\n                627.3378518001264,\n                -214.50727489053966,\n                85.6111452585056\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.5235987755982988,\n            0.5235987755982988,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.5235987755982894,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/medium_average_updown/2025-1-15_17-25-25_1747844822\",\n        \"gpt_instruction\": \"Move upward toward a green medium - sized landmark building characterized by a domed roof adorned with intricate architectural detailing . Proceed directly ahead to reach it . Then , make a slight right turn and head straight toward a white medium - sized historic building with a decorative rooftop balustrade intricately carved . Finally , slightly pause and descend to a gray medium - sized commercial building distinguished by a decorative fa\\u00e7ade featuring a fire escape ladder .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250115_172529\",\n            \"20250115_172531\",\n            \"20250115_172533\",\n            \"20250115_172535\",\n            \"20250115_172538\",\n            \"20250115_172540\",\n            \"20250115_172542\",\n            \"20250115_172544\",\n            \"20250115_172546\",\n            \"20250115_172548\",\n            \"20250115_172550\",\n            \"20250115_172553\",\n            \"20250115_172555\",\n            \"20250115_172557\",\n            \"20250115_172559\",\n            \"20250115_172601\",\n            \"20250115_172603\",\n            \"20250115_172605\",\n            \"20250115_172612\",\n            \"20250115_172618\",\n            \"20250115_172624\",\n            \"20250115_172630\",\n            \"20250115_172636\",\n            \"20250115_172643\",\n            \"20250115_172645\",\n            \"20250115_172651\",\n            \"20250115_172658\",\n            \"20250115_172704\",\n            \"20250115_172710\",\n            \"20250115_172712\",\n            \"20250115_172714\",\n            \"20250115_172716\",\n            \"20250115_172719\",\n            \"20250115_172721\",\n            \"20250115_172723\",\n            \"20250115_172725\",\n            \"20250115_172727\",\n            \"20250115_172729\",\n            \"20250115_172732\",\n            \"20250115_172734\",\n            \"20250115_172736\",\n            \"20250115_172738\",\n            \"20250115_172740\",\n            \"20250115_172742\",\n            \"20250115_172744\",\n            \"20250115_172746\",\n            \"20250115_172749\",\n            \"20250115_172751\",\n            \"20250115_172753\"\n        ],\n        \"pos\": [\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                2.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                5.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                8.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                11.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                14.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                17.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                20.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                23.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                26.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                29.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                32.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                35.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                38.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                41.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                44.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                47.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                50.337885636574974\n            ],\n            [\n                -1896.685538285488,\n                1812.087783325339,\n                53.337885636574974\n            ],\n            [\n                -1902.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1911.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1920.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1929.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1938.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1947.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1950.685538285488,\n                1812.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1955.8816907081948,\n                1815.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1963.675919342255,\n                1819.587783325339,\n                56.337885636574974\n            ],\n            [\n                -1971.470147976315,\n                1824.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1979.2643766103752,\n                1828.587783325339,\n                56.337885636574974\n            ],\n            [\n                -1981.8624528217285,\n                1830.087783325339,\n                56.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                56.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                53.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                50.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                47.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                44.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                41.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                38.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                35.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                32.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                29.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                26.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                23.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                20.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                17.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                14.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                11.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                8.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                5.337885636574974\n            ],\n            [\n                -1984.460529033082,\n                1831.587783325339,\n                2.337885636574974\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_short/2025-1-3_21-25-46_1059961393\",\n        \"gpt_instruction\": \"Proceed straight to a structure characterized by a light gray color , a grid - like repeating pattern of windows with protruding balconies , and a rectangular shape . It stands tall and dominant in the center , and it is identified as a building .\",\n        \"action\": [\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_212553\",\n            \"20250103_212555\"\n        ],\n        \"pos\": [\n            [\n                961.8257141297589,\n                -417.7557658351854,\n                71.58283910457016\n            ],\n            [\n                964.8257141297589,\n                -417.7557658351854,\n                71.58283910457016\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_short_updown/2025-1-14_21-11-29_1789366143\",\n        \"gpt_instruction\": \"Starting at a white medium - sized building featuring large rectangular windows , proceed to a beige large historic building characterized by an arched entrance with intricate architectural details and multiple windows . Slightly turn right to reach a medium - sized modern building with white structural columns and rectangular windows made of black glass panels . Then , slightly turn left slightly and move toward it . Finally , slightly stop at it .\",\n        \"action\": [\n            -1,\n            -1,\n            -1,\n            -1,\n            9,\n            9,\n            3,\n            9,\n            8,\n            2,\n            8,\n            0,\n            -2,\n            -2,\n            -2,\n            -2\n        ],\n        \"index_list\": [\n            \"20250114_211134\",\n            \"20250114_211136\",\n            \"20250114_211138\",\n            \"20250114_211140\",\n            \"20250114_211147\",\n            \"20250114_211153\",\n            \"20250114_211155\",\n            \"20250114_211202\",\n            \"20250114_211206\",\n            \"20250114_211208\",\n            \"20250114_211213\",\n            \"20250114_211215\",\n            \"20250114_211217\",\n            \"20250114_211219\",\n            \"20250114_211221\",\n            \"20250114_211223\"\n        ],\n        \"pos\": [\n            [\n                1750.6001996351235,\n                137.87976023132885,\n                2.8883443354256\n            ],\n            [\n                1750.6001996351235,\n                137.87976023132885,\n                5.8883443354256\n            ],\n            [\n                1750.6001996351235,\n                137.87976023132885,\n                8.8883443354256\n            ],\n            [\n                1750.6001996351235,\n                137.87976023132885,\n                11.8883443354256\n            ],\n            [\n                1744.6001996351235,\n                137.87976023132885,\n                14.8883443354256\n            ],\n            [\n                1735.6001996351235,\n                137.87976023132885,\n                14.8883443354256\n            ],\n            [\n                1732.6001996351235,\n                137.87976023132885,\n                14.8883443354256\n            ],\n            [\n                1727.4040472124168,\n                140.87976023132885,\n                14.8883443354256\n            ],\n            [\n                1722.20789478971,\n                143.87976023132885,\n                14.8883443354256\n            ],\n            [\n                1719.6098185783567,\n                145.37976023132885,\n                14.8883443354256\n            ],\n            [\n                1716.6098185783567,\n                145.37976023132885,\n                14.8883443354256\n            ],\n            [\n                1713.6098185783567,\n                145.37976023132885,\n                14.8883443354256\n            ],\n            [\n                1713.6098185783567,\n                145.37976023132885,\n                11.8883443354256\n            ],\n            [\n                1713.6098185783567,\n                145.37976023132885,\n                8.8883443354256\n            ],\n            [\n                1713.6098185783567,\n                145.37976023132885,\n                5.8883443354256\n            ],\n            [\n                1713.6098185783567,\n                145.37976023132885,\n                2.8883443354256\n            ]\n        ],\n        \"yaw\": [\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_short/2025-1-3_5-28-7_404259631\",\n        \"gpt_instruction\": \"Proceed to a medium - large office building , approximately 12 - 15 stories tall , characterized by a modern design with evenly spaced rectangular windows covering significant portions of its brown facade .\",\n        \"action\": [\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_052817\",\n            \"20250103_052820\",\n            \"20250103_052823\"\n        ],\n        \"pos\": [\n            [\n                1297.8897274165588,\n                1733.9813544883482,\n                54.9164581490014\n            ],\n            [\n                1295.2916512052054,\n                1732.4813544883482,\n                54.9164581490014\n            ],\n            [\n                1292.693574993852,\n                1730.9813544883482,\n                54.9164581490014\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-6_10-42-11_1041655229\",\n        \"gpt_instruction\": \"Proceed straight towards the gray , medium - sized rectangular courtyard of the building .\",\n        \"action\": [\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_104217\",\n            \"20250106_104219\"\n        ],\n        \"pos\": [\n            [\n                -305.7225644802392,\n                1465.944588364129,\n                81.8063351983269\n            ],\n            [\n                -305.7225644802392,\n                1462.944588364129,\n                81.8063351983269\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_average/2025-01-19_09-45-09_92227\",\n        \"gpt_instruction\": \"First , move forward to a skyscraper or office building with a large reflective glass facade of gray and black hues , and window repetition . Then , go upwards to it . Next , walk straight to a flat rooftop space of large size , defined by its grey gravel feature . Slightly turning left , continue directly ahead to a medium - sized communication tower of tapered design , presenting a grey color . Then , slightly turn right and proceed directly ahead to a medium - sized commercial building with visible windows in horizontal bands , featuring white and dark horizontal stripes . Again turn right , moving ahead to a large building featuring a flat roof with rooftop structures and ventilation units , in grey color . Continue to fall towards it . Finally , proceed straight to it .\",\n        \"action\": [\n            9,\n            9,\n            8,\n            4,\n            4,\n            9,\n            2,\n            8,\n            3,\n            9,\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            9,\n            5,\n            5,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250119_094522\",\n            \"20250119_094528\",\n            \"20250119_094533\",\n            \"20250119_094535\",\n            \"20250119_094537\",\n            \"20250119_094544\",\n            \"20250119_094546\",\n            \"20250119_094551\",\n            \"20250119_094553\",\n            \"20250119_094600\",\n            \"20250119_094607\",\n            \"20250119_094614\",\n            \"20250119_094620\",\n            \"20250119_094622\",\n            \"20250119_094629\",\n            \"20250119_094635\",\n            \"20250119_094642\",\n            \"20250119_094644\",\n            \"20250119_094646\",\n            \"20250119_094648\",\n            \"20250119_094650\"\n        ],\n        \"pos\": [\n            [\n                -1389.166181019654,\n                1454.5038518153435,\n                70.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1445.5038518153435,\n                70.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1439.5038518153435,\n                70.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1436.5038518153435,\n                70.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1436.5038518153435,\n                73.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1430.5038518153435,\n                76.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1427.5038518153435,\n                76.41343146830145\n            ],\n            [\n                -1387.666181019654,\n                1424.9057756039902,\n                76.41343146830145\n            ],\n            [\n                -1386.166181019654,\n                1422.3076993926368,\n                76.41343146830145\n            ],\n            [\n                -1386.166181019654,\n                1416.3076993926368,\n                76.41343146830145\n            ],\n            [\n                -1386.166181019654,\n                1407.3076993926368,\n                76.41343146830145\n            ],\n            [\n                -1386.166181019654,\n                1398.3076993926368,\n                76.41343146830145\n            ],\n            [\n                -1386.166181019654,\n                1389.3076993926368,\n                76.41343146830145\n            ],\n            [\n                -1386.166181019654,\n                1386.3076993926368,\n                76.41343146830145\n            ],\n            [\n                -1389.166181019654,\n                1381.11154696993,\n                76.41343146830145\n            ],\n            [\n                -1393.666181019654,\n                1373.31731833587,\n                76.41343146830145\n            ],\n            [\n                -1398.166181019654,\n                1365.5230897018098,\n                76.41343146830145\n            ],\n            [\n                -1399.666181019654,\n                1362.9250134904564,\n                76.41343146830145\n            ],\n            [\n                -1399.666181019654,\n                1362.9250134904564,\n                73.41343146830145\n            ],\n            [\n                -1399.666181019654,\n                1362.9250134904564,\n                70.41343146830145\n            ],\n            [\n                -1401.166181019654,\n                1360.326937279103,\n                70.41343146830145\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186,\n            -2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_short/2024-12-28_10-59-57_235745791\",\n        \"gpt_instruction\": \"Continue straight to the large white building with an ornate facade featuring a large archway . Then slightly turn left towards the large beige building which also boasts an ornate facade , this time adorned with large windows . Next , take a right to face a large white building with a modern facade and dark windows . Proceed by slightly going straight and then slightly turning right slightly to find it . Advance forward to reach a large beige historic building with an arched entrance . Finally , slightly turn left and walk straight to it .\",\n        \"action\": [\n            9,\n            2,\n            8,\n            3,\n            3,\n            1,\n            3,\n            1,\n            2,\n            2,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20241228_110006\",\n            \"20241228_110008\",\n            \"20241228_110012\",\n            \"20241228_110014\",\n            \"20241228_110016\",\n            \"20241228_110018\",\n            \"20241228_110020\",\n            \"20241228_110022\",\n            \"20241228_110024\",\n            \"20241228_110026\",\n            \"20241228_110032\",\n            \"20241228_110034\",\n            \"20241228_110037\",\n            \"20241228_110039\"\n        ],\n        \"pos\": [\n            [\n                1736.4503016359536,\n                107.46130335229881,\n                6.703138101895044\n            ],\n            [\n                1733.8522254246002,\n                108.96130335229881,\n                6.703138101895044\n            ],\n            [\n                1730.8522254246002,\n                108.96130335229881,\n                6.703138101895044\n            ],\n            [\n                1727.8522254246002,\n                108.96130335229881,\n                6.703138101895044\n            ],\n            [\n                1727.8522254246002,\n                108.96130335229881,\n                6.703138101895044\n            ],\n            [\n                1727.8522254246002,\n                108.96130335229881,\n                6.703138101895044\n            ],\n            [\n                1726.3522254246002,\n                111.55937956365213,\n                6.703138101895044\n            ],\n            [\n                1726.3522254246002,\n                111.55937956365213,\n                6.703138101895044\n            ],\n            [\n                1726.3522254246002,\n                114.55937956365213,\n                6.703138101895044\n            ],\n            [\n                1726.3522254246002,\n                114.55937956365213,\n                6.703138101895044\n            ],\n            [\n                1721.1560730018934,\n                117.55937956365213,\n                6.703138101895044\n            ],\n            [\n                1718.55799679054,\n                119.05937956365213,\n                6.703138101895044\n            ],\n            [\n                1715.55799679054,\n                119.05937956365213,\n                6.703138101895044\n            ],\n            [\n                1712.55799679054,\n                119.05937956365213,\n                6.703138101895044\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            2.6179938779914944,\n            2.0943951023931957,\n            2.0943951023931953,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.0943951023931953,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_short/2024-12-22_17-13-11_521595368\",\n        \"gpt_instruction\": \"Proceed directly to the large black building with a glass facade , then slightly turn right and continue straight to it . Shift left to the large black building with a grid - like window pattern , then slightly continue straight to a large gray commercial building with tall arched windows . Move forward to the large dark building with a modern glass facade , slightly turn right , and proceed to a tall skyscraper with a grey dome roof and a narrow spire .\",\n        \"action\": [\n            8,\n            3,\n            1,\n            2,\n            9,\n            9,\n            8,\n            2,\n            2,\n            1,\n            3,\n            3,\n            9,\n            9,\n            9,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20241222_171318\",\n            \"20241222_171320\",\n            \"20241222_171322\",\n            \"20241222_171324\",\n            \"20241222_171330\",\n            \"20241222_171336\",\n            \"20241222_171340\",\n            \"20241222_171342\",\n            \"20241222_171344\",\n            \"20241222_171346\",\n            \"20241222_171348\",\n            \"20241222_171350\",\n            \"20241222_171356\",\n            \"20241222_171402\",\n            \"20241222_171407\",\n            \"20241222_171409\",\n            \"20241222_171411\",\n            \"20241222_171413\"\n        ],\n        \"pos\": [\n            [\n                747.5026504199778,\n                352.4922249190935,\n                9.589436923359827\n            ],\n            [\n                747.5026504199778,\n                349.4922249190935,\n                9.589436923359827\n            ],\n            [\n                747.5026504199778,\n                349.4922249190935,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                346.89414870774016,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                340.89414870774016,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                331.89414870774016,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                325.89414870774016,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                322.89414870774016,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                322.89414870774016,\n                9.589436923359827\n            ],\n            [\n                746.0026504199778,\n                322.89414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                321.39414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                321.39414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                315.39414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                306.39414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                297.39414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                294.39414870774016,\n                9.589436923359827\n            ],\n            [\n                748.6007266313311,\n                294.39414870774016,\n                9.589436923359827\n            ],\n            [\n                747.1007266313311,\n                291.79607249638684,\n                9.589436923359827\n            ]\n        ],\n        \"yaw\": [\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511965979,\n            -0.5235987755982989,\n            -0.5235987755983084,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -2.0943951023931953,\n            -2.0943951023931953\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_average/2025-1-4_22-45-43_353673391\",\n        \"gpt_instruction\": \"Move forward to a medium - sized flat - roofed building distinguished by its gray color and assorted rooftop fixtures , which also features visible rooftop structures and HVAC systems . Slightly turn left , then move forward again to reach a medium - sized rectangular building with a light beige color , highlighted by red accents on its roof edges . This structure draws attention with its elevated rooftop area , positioned near a large open square and standing out due to its spacing relative to nearby buildings .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_224553\",\n            \"20250104_224600\",\n            \"20250104_224607\",\n            \"20250104_224613\",\n            \"20250104_224620\",\n            \"20250104_224626\",\n            \"20250104_224633\",\n            \"20250104_224640\",\n            \"20250104_224646\",\n            \"20250104_224653\",\n            \"20250104_224659\",\n            \"20250104_224706\",\n            \"20250104_224708\",\n            \"20250104_224713\",\n            \"20250104_224715\"\n        ],\n        \"pos\": [\n            [\n                134.37119750646116,\n                1187.1385455665838,\n                73.53355584340784\n            ],\n            [\n                142.1654261405211,\n                1182.6385455665838,\n                73.53355584340784\n            ],\n            [\n                149.95965477458105,\n                1178.1385455665838,\n                73.53355584340784\n            ],\n            [\n                157.753883408641,\n                1173.6385455665838,\n                73.53355584340784\n            ],\n            [\n                165.54811204270095,\n                1169.1385455665838,\n                73.53355584340784\n            ],\n            [\n                173.3423406767609,\n                1164.6385455665838,\n                73.53355584340784\n            ],\n            [\n                181.13656931082085,\n                1160.1385455665838,\n                73.53355584340784\n            ],\n            [\n                188.9307979448808,\n                1155.6385455665838,\n                73.53355584340784\n            ],\n            [\n                196.72502657894074,\n                1151.1385455665838,\n                73.53355584340784\n            ],\n            [\n                204.5192552130007,\n                1146.6385455665838,\n                73.53355584340784\n            ],\n            [\n                212.31348384706064,\n                1142.1385455665838,\n                73.53355584340784\n            ],\n            [\n                220.1077124811206,\n                1137.6385455665838,\n                73.53355584340784\n            ],\n            [\n                222.7057886924739,\n                1136.1385455665838,\n                73.53355584340784\n            ],\n            [\n                225.7057886924739,\n                1136.1385455665838,\n                73.53355584340784\n            ],\n            [\n                228.7057886924739,\n                1136.1385455665838,\n                73.53355584340784\n            ]\n        ],\n        \"yaw\": [\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            0.0,\n            0.0\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_average/2025-1-4_14-57-10_221713886\",\n        \"gpt_instruction\": \"Proceed towards the large , grey building with ornate gothic architecture .\",\n        \"action\": [\n            1,\n            2,\n            2,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_145714\",\n            \"20250104_145716\",\n            \"20250104_145718\",\n            \"20250104_145725\",\n            \"20250104_145727\",\n            \"20250104_145729\"\n        ],\n        \"pos\": [\n            [\n                714.7413473961078,\n                -305.2298373207442,\n                94.8676192281302\n            ],\n            [\n                717.7413473961078,\n                -305.2298373207442,\n                94.8676192281302\n            ],\n            [\n                717.7413473961078,\n                -305.2298373207442,\n                94.8676192281302\n            ],\n            [\n                720.7413473961078,\n                -300.03368489803756,\n                94.8676192281302\n            ],\n            [\n                722.2413473961078,\n                -297.43560868668425,\n                94.8676192281302\n            ],\n            [\n                723.7413473961078,\n                -294.83753247533093,\n                94.8676192281302\n            ]\n        ],\n        \"yaw\": [\n            0.0,\n            0.0,\n            0.5235987755982988,\n            1.0471975511965979,\n            1.0471975511965979,\n            1.0471975511965979\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_short/2024-12-28_1-43-50_1129566413\",\n        \"gpt_instruction\": \"Veer left toward a large , gray and black building characterized by tall , reflective windows . Move ahead to it . Go right to it . Keep going straight to encounter it . Slightly turn right to advance forward towards a large , brown building with an ornate facade . Slightly turn right again to go directly ahead towards yet another large , black building , exhibiting a grid - like window pattern . Finally , slightly turn left and move forward to approach a tall , multi - story modern office building with a dark blue and black glass facade .\",\n        \"action\": [\n            1,\n            2,\n            2,\n            2,\n            9,\n            3,\n            3,\n            8,\n            3,\n            8,\n            3,\n            8,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20241228_014356\",\n            \"20241228_014359\",\n            \"20241228_014401\",\n            \"20241228_014403\",\n            \"20241228_014409\",\n            \"20241228_014411\",\n            \"20241228_014413\",\n            \"20241228_014417\",\n            \"20241228_014419\",\n            \"20241228_014423\",\n            \"20241228_014425\",\n            \"20241228_014429\",\n            \"20241228_014431\",\n            \"20241228_014437\",\n            \"20241228_014439\"\n        ],\n        \"pos\": [\n            [\n                -855.7977187325247,\n                1466.5161984612591,\n                9.860489917132238\n            ],\n            [\n                -857.2977187325247,\n                1469.1142746726125,\n                9.860489917132238\n            ],\n            [\n                -857.2977187325247,\n                1469.1142746726125,\n                9.860489917132238\n            ],\n            [\n                -857.2977187325247,\n                1469.1142746726125,\n                9.860489917132238\n            ],\n            [\n                -862.4938711552313,\n                1466.1142746726125,\n                9.860489917132238\n            ],\n            [\n                -865.0919473665847,\n                1464.6142746726125,\n                9.860489917132238\n            ],\n            [\n                -865.0919473665847,\n                1464.6142746726125,\n                9.860489917132238\n            ],\n            [\n                -867.6900235779381,\n                1466.1142746726125,\n                9.860489917132238\n            ],\n            [\n                -870.2880997892914,\n                1467.6142746726125,\n                9.860489917132238\n            ],\n            [\n                -871.7880997892914,\n                1470.2123508839659,\n                9.860489917132238\n            ],\n            [\n                -873.2880997892914,\n                1472.8104270953193,\n                9.860489917132238\n            ],\n            [\n                -873.2880997892914,\n                1475.8104270953193,\n                9.860489917132238\n            ],\n            [\n                -873.2880997892914,\n                1478.8104270953193,\n                9.860489917132238\n            ],\n            [\n                -876.2880997892914,\n                1484.006579518026,\n                9.860489917132238\n            ],\n            [\n                -877.7880997892914,\n                1486.6046557293794,\n                9.860489917132238\n            ]\n        ],\n        \"yaw\": [\n            2.094395102393186,\n            2.094395102393186,\n            2.6179938779914846,\n            3.1415926535897833,\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -3.1415926535898024,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.094395102393186,\n            2.094395102393186,\n            1.5707963267948966,\n            1.5707963267948966,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_long/2025-1-4_15-1-47_28264029\",\n        \"gpt_instruction\": \"Proceed straight towards the commercial building that features light brown rectangular windows arranged in a grid pattern . The building is medium in size .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            8,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_150156\",\n            \"20250104_150203\",\n            \"20250104_150209\",\n            \"20250104_150216\",\n            \"20250104_150220\",\n            \"20250104_150222\"\n        ],\n        \"pos\": [\n            [\n                1032.5630897376673,\n                1444.3271738141314,\n                29.504723423377094\n            ],\n            [\n                1037.0630897376673,\n                1452.1214024481915,\n                29.504723423377094\n            ],\n            [\n                1041.5630897376673,\n                1459.9156310822516,\n                29.504723423377094\n            ],\n            [\n                1046.0630897376673,\n                1467.7098597163117,\n                29.504723423377094\n            ],\n            [\n                1049.0630897376673,\n                1472.9060121390185,\n                29.504723423377094\n            ],\n            [\n                1050.5630897376673,\n                1475.5040883503718,\n                29.504723423377094\n            ]\n        ],\n        \"yaw\": [\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072,\n            1.0471975511966072\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-6_20-53-14_423399815\",\n        \"gpt_instruction\": \"Proceed straight to a gray building characterized by rectangular windows and a flat rooftop , then slightly turn right and move forward to it . Afterward , slightly turn left and proceed straight to a red building , notable for being tall and narrow with a wide facade and a prominent rooftop structure . Next , take a left turn to reach a gray building with a flat roof housing mechanical equipment . Go directly ahead to a yellow road intersection marked with crosswalk lines . Turn right to find a brown mid-rise building with distinct red trims and an older architectural style . Finally , move forward to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            3,\n            9,\n            9,\n            8,\n            2,\n            9,\n            1,\n            2,\n            2,\n            9,\n            9,\n            8,\n            3,\n            3,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_205325\",\n            \"20250106_205332\",\n            \"20250106_205338\",\n            \"20250106_205340\",\n            \"20250106_205346\",\n            \"20250106_205353\",\n            \"20250106_205358\",\n            \"20250106_205400\",\n            \"20250106_205406\",\n            \"20250106_205408\",\n            \"20250106_205410\",\n            \"20250106_205413\",\n            \"20250106_205419\",\n            \"20250106_205426\",\n            \"20250106_205431\",\n            \"20250106_205433\",\n            \"20250106_205435\",\n            \"20250106_205437\",\n            \"20250106_205439\"\n        ],\n        \"pos\": [\n            [\n                -1725.9813847773914,\n                1981.8167284357557,\n                70.90017127548626\n            ],\n            [\n                -1721.4813847773914,\n                1974.0224998016956,\n                70.90017127548626\n            ],\n            [\n                -1716.9813847773914,\n                1966.2282711676355,\n                70.90017127548626\n            ],\n            [\n                -1715.4813847773914,\n                1963.6301949562821,\n                70.90017127548626\n            ],\n            [\n                -1715.4813847773914,\n                1957.6301949562821,\n                70.90017127548626\n            ],\n            [\n                -1715.4813847773914,\n                1948.6301949562821,\n                70.90017127548626\n            ],\n            [\n                -1715.4813847773914,\n                1942.6301949562821,\n                70.90017127548626\n            ],\n            [\n                -1715.4813847773914,\n                1939.6301949562821,\n                70.90017127548626\n            ],\n            [\n                -1712.4813847773914,\n                1934.4340425335754,\n                70.90017127548626\n            ],\n            [\n                -1710.9813847773914,\n                1931.835966322222,\n                70.90017127548626\n            ],\n            [\n                -1709.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1709.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1703.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1694.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1688.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1685.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1685.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1685.4813847773914,\n                1929.2378901108686,\n                70.90017127548626\n            ],\n            [\n                -1683.9813847773914,\n                1926.6398138995153,\n                70.90017127548626\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.5707963267948966,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755983084,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            -0.5235987755982988,\n            -1.0471975511965976,\n            -1.0471975511965976\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_average/2025-1-4_9-2-16_2039723618\",\n        \"gpt_instruction\": \"Proceed to the gray , rectangular structure with a flat rooftop featuring visible utilities . This large building occupies a prominent position in the cityscape and serves as a commercial / office building .\",\n        \"action\": [\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250104_090226\",\n            \"20250104_090233\",\n            \"20250104_090235\"\n        ],\n        \"pos\": [\n            [\n                992.8552604852041,\n                -448.01212712706786,\n                90.81733680557895\n            ],\n            [\n                985.061031851144,\n                -452.51212712706786,\n                90.81733680557895\n            ],\n            [\n                982.4629556397906,\n                -454.01212712706786,\n                90.81733680557895\n            ]\n        ],\n        \"yaw\": [\n            -2.6179938779915037,\n            -2.6179938779915037,\n            -2.6179938779915037\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_average/2025-1-3_15-43-46_424238335\",\n        \"gpt_instruction\": \"Move forward to the gray , large building characterized by a tall , grid - patterned facade , then slightly turn right and advance forward to the brown , large historic brick building with decorative cornices and pilasters .\",\n        \"action\": [\n            9,\n            9,\n            1,\n            3,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_154355\",\n            \"20250103_154401\",\n            \"20250103_154403\",\n            \"20250103_154406\",\n            \"20250103_154412\",\n            \"20250103_154414\"\n        ],\n        \"pos\": [\n            [\n                524.5839748352435,\n                -967.4432053826174,\n                25.633935013868438\n            ],\n            [\n                516.7897462011833,\n                -962.9432053826174,\n                25.633935013868438\n            ],\n            [\n                514.19166998983,\n                -961.4432053826174,\n                25.633935013868438\n            ],\n            [\n                511.59359377847665,\n                -959.9432053826174,\n                25.633935013868438\n            ],\n            [\n                508.59359377847665,\n                -954.7470529599107,\n                25.633935013868438\n            ],\n            [\n                507.09359377847665,\n                -952.1489767485573,\n                25.633935013868438\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779914944,\n            2.094395102393186,\n            2.094395102393186\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_average/2025-1-5_8-45-45_545343058\",\n        \"gpt_instruction\": \"Proceed straight towards a building characterized by a grey color , a flat roof with mechanical equipment , and a medium size . Then , slightly turn left and move forward to it .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250105_084553\",\n            \"20250105_084600\",\n            \"20250105_084606\",\n            \"20250105_084608\",\n            \"20250105_084615\",\n            \"20250105_084621\",\n            \"20250105_084624\"\n        ],\n        \"pos\": [\n            [\n                309.66221195143083,\n                1764.5557535036996,\n                93.98483821710346\n            ],\n            [\n                314.16221195143083,\n                1756.7615248696395,\n                93.98483821710346\n            ],\n            [\n                318.66221195143083,\n                1748.9672962355794,\n                93.98483821710346\n            ],\n            [\n                320.16221195143083,\n                1746.369220024226,\n                93.98483821710346\n            ],\n            [\n                325.35836437413747,\n                1743.369220024226,\n                93.98483821710346\n            ],\n            [\n                333.1525930081974,\n                1738.869220024226,\n                93.98483821710346\n            ],\n            [\n                335.75066921955073,\n                1737.369220024226,\n                93.98483821710346\n            ]\n        ],\n        \"yaw\": [\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -1.0471975511966072,\n            -0.5235987755982988,\n            -0.5235987755982988,\n            -0.5235987755982988\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/high_long/2025-1-6_15-4-9_2020532420\",\n        \"gpt_instruction\": \"Proceed straight towards a large grey building with a flat roof , featuring a rectangular structure and additional rooftop equipment . Then , slightly turn left and move ahead to approach a light beige building with a wide facade and a repetitive rectangular window pattern . This building is medium - sized compared to other buildings , slightly wider , and also a type of building .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            8,\n            2,\n            9,\n            0\n        ],\n        \"index_list\": [\n            \"20250106_150419\",\n            \"20250106_150425\",\n            \"20250106_150431\",\n            \"20250106_150438\",\n            \"20250106_150444\",\n            \"20250106_150451\",\n            \"20250106_150457\",\n            \"20250106_150504\",\n            \"20250106_150510\",\n            \"20250106_150517\",\n            \"20250106_150524\",\n            \"20250106_150530\",\n            \"20250106_150537\",\n            \"20250106_150542\",\n            \"20250106_150544\",\n            \"20250106_150550\",\n            \"20250106_150553\"\n        ],\n        \"pos\": [\n            [\n                1985.9182918434672,\n                1080.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1978.124063209407,\n                1085.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1970.329834575347,\n                1089.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1962.5356059412868,\n                1094.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1954.7413773072267,\n                1098.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1946.9471486731666,\n                1103.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1939.1529200391064,\n                1107.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1931.3586914050463,\n                1112.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1923.5644627709862,\n                1116.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1915.770234136926,\n                1121.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1907.976005502866,\n                1125.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1900.1817768688059,\n                1130.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1892.3875482347457,\n                1134.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1887.191395812039,\n                1137.5515611086726,\n                75.0454269760777\n            ],\n            [\n                1884.5933196006856,\n                1139.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1878.5933196006856,\n                1139.0515611086726,\n                75.0454269760777\n            ],\n            [\n                1875.5933196006856,\n                1139.0515611086726,\n                75.0454269760777\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    },\n    {\n        \"image_path\": \"env_ue_bigcity/astar_data/low_average/2025-1-3_16-6-44_628175011\",\n        \"gpt_instruction\": \"Proceed directly to the large red building with fire escapes ; then , slightly turn left and continue straight to the medium - sized reddish - orange brick residential building with decorative cornice and brickwork detailing .\",\n        \"action\": [\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            9,\n            2,\n            9,\n            9,\n            9,\n            9,\n            1,\n            0\n        ],\n        \"index_list\": [\n            \"20250103_160652\",\n            \"20250103_160659\",\n            \"20250103_160705\",\n            \"20250103_160712\",\n            \"20250103_160719\",\n            \"20250103_160725\",\n            \"20250103_160732\",\n            \"20250103_160735\",\n            \"20250103_160741\",\n            \"20250103_160748\",\n            \"20250103_160755\",\n            \"20250103_160801\",\n            \"20250103_160804\",\n            \"20250103_160806\"\n        ],\n        \"pos\": [\n            [\n                1958.9729982972215,\n                1277.444688828722,\n                28.450458553347897\n            ],\n            [\n                1951.1787696631613,\n                1281.944688828722,\n                28.450458553347897\n            ],\n            [\n                1943.3845410291012,\n                1286.444688828722,\n                28.450458553347897\n            ],\n            [\n                1935.590312395041,\n                1290.944688828722,\n                28.450458553347897\n            ],\n            [\n                1927.796083760981,\n                1295.444688828722,\n                28.450458553347897\n            ],\n            [\n                1920.0018551269209,\n                1299.944688828722,\n                28.450458553347897\n            ],\n            [\n                1912.2076264928608,\n                1304.444688828722,\n                28.450458553347897\n            ],\n            [\n                1909.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ],\n            [\n                1903.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ],\n            [\n                1894.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ],\n            [\n                1885.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ],\n            [\n                1876.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ],\n            [\n                1873.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ],\n            [\n                1870.6095502815074,\n                1305.944688828722,\n                28.450458553347897\n            ]\n        ],\n        \"yaw\": [\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            2.6179938779915037,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793,\n            3.141592653589793\n        ]\n    }\n]"
  },
  {
    "path": "envs/airsim/AirSim/settings.json",
    "content": "{\n  \"SeeDocsAt\": \"https://github.com/Microsoft/AirSim/blob/main/docs/settings.md\",\n  \"SettingsVersion\": 1.2,\n  \"SimMode\": \"Multirotor\",\n  \"ClockSpeed\": 1.0,\n  \"CameraDefaults\": {\n      \"CaptureSettings\": [\n        {\n          \"ImageType\": 0,\n          \"Width\": 1920,\n          \"Height\": 1080,\n          \"FOV_Degrees\": 90,\n          \"AutoExposureSpeed\": 100,\n          \"MotionBlurAmount\": 0\n        }\n    ]\n  },\n\n  \"Vehicles\": {\n    \"drone_1\": {\n      \"VehicleType\": \"SimpleFlight\",\n      \"DefaultVehicleState\": \"Armed\",\n      \"EnableCollisionPassthrogh\": false,\n      \"EnableCollisions\": true,\n      \"AllowAPIAlways\": true,\n      \"RC\": {\n        \"RemoteControlID\": 0,\n        \"AllowAPIWhenDisconnected\": false\n      },\n      \"Sensors\": {\n        \"Imu\": {\n          \"SensorType\": 2,\n          \"Enabled\": true\n        },\n        \"LidarSensor1\": {\n          \"SensorType\": 6,\n          \"Enabled\": true,\n          \"NumberOfChannels\": 128,\n          \"Range\": 500,\n          \"RotationsPerSecond\": 10,\n          \"PointsPerSecond\": 50000,\n          \"X\": 0,\n          \"Y\": 0,\n          \"Z\": -1,\n          \"Roll\": 0,\n          \"Pitch\": 90,\n          \"Yaw\": 0,\n          \"VerticalFOVUpper\": 45,\n          \"VerticalFOVLower\": -45,\n          \"DrawDebugPoints\": true,\n          \"DataFrame\": \"SensorLocalFrame\"\n        },\n        \"LidarSensor2\": {\n          \"SensorType\": 6,\n          \"Enabled\": true,\n          \"NumberOfChannels\": 128,\n          \"Range\": 500,\n          \"RotationsPerSecond\": 10,\n          \"PointsPerSecond\": 50000,\n          \"X\": 0,\n          \"Y\": 0,\n          \"Z\": -1,\n          \"Roll\": 0,\n          \"Pitch\": 0,\n          \"Yaw\": 0,\n          \"VerticalFOVUpper\": 45,\n          \"VerticalFOVLower\": -45,\n          \"DrawDebugPoints\": true,\n          \"DataFrame\": \"SensorLocalFrame\"\n        }\n      },\n      \"Cameras\": {\n        \"front_custom\": {\n          \"CaptureSettings\": [\n            {\n              \"PublishToRos\": 1,\n              \"ImageType\": 0,\n              \"Width\": 1920,\n              \"Height\": 1080,\n              \"FOV_Degrees\": 90,\n              \"DepthOfFieldFstop\": 2.8,\n              \"DepthOfFieldFocalDistance\": 200.0, \n              \"DepthOfFieldFocalRegion\": 200.0,\n              \"TargetGamma\": 1.5\n            },\n            {\n              \"PublishToRos\": 1,\n              \"ImageType\": 1,\n              \"Width\": 640,\n              \"Height\": 480,\n              \"FOV_Degrees\": 90,\n              \"DepthOfFieldFstop\": 2.8,\n              \"DepthOfFieldFocalDistance\": 200.0, \n              \"DepthOfFieldFocalRegion\": 200.0,\n              \"TargetGamma\": 1.5\n            },\n            {\n              \"PublishToRos\": 1,\n              \"ImageType\": 5,\n              \"Width\": 640,\n              \"Height\": 480,\n              \"FOV_Degrees\": 90,\n              \"DepthOfFieldFstop\": 2.8,\n              \"DepthOfFieldFocalDistance\": 200.0, \n              \"DepthOfFieldFocalRegion\": 200.0,\n              \"TargetGamma\": 1.5\n            }\n          ],\n          \"X\": 0.0, \"Y\": 0, \"Z\": 1.0,\n          \"Pitch\": 0, \"Roll\": 0, \"Yaw\": 0\n        }\n      }\n    }\n  },\n  \"Recording\": {\n    \"RecordOnMove\": false,\n    \"RecordInterval\": 0.001,\n    \"Folder\": \"\",\n    \"Enabled\": false,\n    \"Cameras\": [\n        { \"CameraName\": \"front_custom\", \"ImageType\": 0, \"PixelsAsFloat\": false,  \"VehicleName\": \"drone_1\", \"Compress\": false }\n    ]\n  }\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/.vscode/c_cpp_properties.json",
    "content": "{\n  \"configurations\": [\n    {\n      \"browse\": {\n        \"databaseFilename\": \"${default}\",\n        \"limitSymbolsToIncludedHeaders\": false\n      },\n      \"includePath\": [\n        \"/opt/ros/humble/include/**\",\n        \"/usr/include/**\"\n      ],\n      \"name\": \"ROS\",\n      \"intelliSenseMode\": \"gcc-x64\",\n      \"compilerPath\": \"/usr/bin/gcc\",\n      \"cStandard\": \"gnu11\",\n      \"cppStandard\": \"c++14\"\n    }\n  ],\n  \"version\": 4\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/.vscode/settings.json",
    "content": "{\n    \"python.autoComplete.extraPaths\": [\n        \"/opt/ros/humble/lib/python3.10/site-packages\",\n        \"/opt/ros/humble/local/lib/python3.10/dist-packages\"\n    ],\n    \"python.analysis.extraPaths\": [\n        \"/opt/ros/humble/lib/python3.10/site-packages\",\n        \"/opt/ros/humble/local/lib/python3.10/dist-packages\"\n    ],\n    \"files.associations\": {\n        \"cctype\": \"cpp\",\n        \"clocale\": \"cpp\",\n        \"cmath\": \"cpp\",\n        \"csignal\": \"cpp\",\n        \"cstdarg\": \"cpp\",\n        \"cstddef\": \"cpp\",\n        \"cstdio\": \"cpp\",\n        \"cstdlib\": \"cpp\",\n        \"cstring\": \"cpp\",\n        \"ctime\": \"cpp\",\n        \"cwchar\": \"cpp\",\n        \"cwctype\": \"cpp\",\n        \"any\": \"cpp\",\n        \"array\": \"cpp\",\n        \"atomic\": \"cpp\",\n        \"strstream\": \"cpp\",\n        \"bit\": \"cpp\",\n        \"*.tcc\": \"cpp\",\n        \"bitset\": \"cpp\",\n        \"cfenv\": \"cpp\",\n        \"chrono\": \"cpp\",\n        \"codecvt\": \"cpp\",\n        \"compare\": \"cpp\",\n        \"complex\": \"cpp\",\n        \"concepts\": \"cpp\",\n        \"condition_variable\": \"cpp\",\n        \"coroutine\": \"cpp\",\n        \"cstdint\": \"cpp\",\n        \"deque\": \"cpp\",\n        \"forward_list\": \"cpp\",\n        \"list\": \"cpp\",\n        \"map\": \"cpp\",\n        \"set\": \"cpp\",\n        \"string\": \"cpp\",\n        \"unordered_map\": \"cpp\",\n        \"unordered_set\": \"cpp\",\n        \"vector\": \"cpp\",\n        \"exception\": \"cpp\",\n        \"algorithm\": \"cpp\",\n        \"functional\": \"cpp\",\n        \"iterator\": \"cpp\",\n        \"memory\": \"cpp\",\n        \"memory_resource\": \"cpp\",\n        \"numeric\": \"cpp\",\n        \"optional\": \"cpp\",\n        \"random\": \"cpp\",\n        \"ratio\": \"cpp\",\n        \"regex\": \"cpp\",\n        \"source_location\": \"cpp\",\n        \"string_view\": \"cpp\",\n        \"system_error\": \"cpp\",\n        \"tuple\": \"cpp\",\n        \"type_traits\": \"cpp\",\n        \"utility\": \"cpp\",\n        \"hash_map\": \"cpp\",\n        \"fstream\": \"cpp\",\n        \"future\": \"cpp\",\n        \"initializer_list\": \"cpp\",\n        \"iomanip\": \"cpp\",\n        \"iosfwd\": \"cpp\",\n        \"iostream\": \"cpp\",\n        \"istream\": \"cpp\",\n        \"limits\": \"cpp\",\n        \"mutex\": \"cpp\",\n        \"new\": \"cpp\",\n        \"numbers\": \"cpp\",\n        \"ostream\": \"cpp\",\n        \"semaphore\": \"cpp\",\n        \"shared_mutex\": \"cpp\",\n        \"sstream\": \"cpp\",\n        \"stdexcept\": \"cpp\",\n        \"stop_token\": \"cpp\",\n        \"streambuf\": \"cpp\",\n        \"thread\": \"cpp\",\n        \"cinttypes\": \"cpp\",\n        \"typeindex\": \"cpp\",\n        \"typeinfo\": \"cpp\",\n        \"valarray\": \"cpp\",\n        \"variant\": \"cpp\",\n        \"*.ipp\": \"cpp\"\n    }\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\nCMAKE_MINIMUM_REQUIRED(VERSION 3.22)\n\nset (CMAKE_SYSTEM_VERSION 10.0.15063.0 CACHE INTERNAL \"Cmake system version\" FORCE)\nPROJECT(sibr_projects)\n\nset(REQUIRED_VERSION \"3.22.0\")\nset(CHECKED_VERSION \"3.27.0\")\n\nif (CMAKE_VERSION VERSION_LESS REQUIRED_VERSION)\n    message(WARNING \"Deprecated version of cmake. Please update to at least ${REQUIRED_VERSION} (${CHECKED_VERSION} recommended).\")\nelseif (CMAKE_VERSION VERSION_GREATER CHECKED_VERSION)\n\tmessage(WARNING \"Untested version of cmake. If you checked everything is working properly, please update ${CHECKED_VERSION} in the main CmakeLists.txt with the version you tested.\")\nendif()\n\n## Include cmake stuff (functions/macros) : Modules files\nif(WIN32)\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows)\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/Modules)\nelse()\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux)\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux/Modules)\nendif()\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\n\n## To maintain cmake versions compatibilities\ninclude(cmake_policies)\nsetPolicies()\n\ninclude(git_describe)\ngit_describe(GIT_BRANCH SIBR_CORE_BRANCH GIT_COMMIT_HASH SIBR_CORE_COMMIT_HASH GIT_TAG SIBR_CORE_TAG GIT_VERSION SIBR_CORE_VERSION)\n\nmessage(STATUS \"SIBR version :\\n BRANCH ${SIBR_CORE_BRANCH}\\n COMMIT_HASH ${SIBR_CORE_COMMIT_HASH}\\n TAG ${SIBR_CORE_TAG}\\n VERSION ${SIBR_CORE_VERSION}\")\n\nif(NOT WIN32)\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nendif()\n\n\nif (WIN32)\n\t## Allow C++11 + other flags\n\tinclude(CheckCXXCompilerFlag)\n\tget_filename_component(currentBuildTool ${CMAKE_BUILD_TOOL} NAME_WE)\t# tool that can launch the native build system. returned value may be the full path\n\tif(${currentBuildTool} MATCHES \"(msdev|devenv|nmake|MSBuild)\")\n\n\t\tadd_compile_options(\"$<$<COMPILE_LANGUAGE:CXX>:/W3;/DNOMINMAX;/MP;-D_USE_MATH_DEFINES>\")\n\t    #add_definitions(/W3 /DNOMINMAX /MP -D_USE_MATH_DEFINES)# /D_ITERATOR_DEBUG_LEVEL=1 because you need all external DLl to compile with this flag too\n\t    set(CMAKE_CONFIGURATION_TYPES \"RelWithDebInfo;Release;Debug\" CACHE STRING \"\" FORCE)\n\t    set(CMAKE_CXX_STANDARD 14)\n\t\tset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\t\tset(CMAKE_CXX_EXTENSIONS OFF)\n\telseif(${currentBuildTool} MATCHES \"(make|gmake)\")\n\t    add_definitions(\"-Wall -Wno-unknown-pragmas -Wno-sign-compare -g -std=c++14 -D__forceinline=\\\"inline\\ __attribute__((always_inline))\\\"\")\n\t\t# CHECK_CXX_COMPILER_FLAG(\"-std=gnu++11\" COMPILER_SUPPORTS_CXX11)\n\t\t# CHECK_CXX_COMPILER_FLAG(\"-std=gnu++0x\" COMPILER_SUPPORTS_CXX0X)\n\t\t# if(COMPILER_SUPPORTS_CXX11)\n\t\t# \tadd_definitions(-std=gnu++11)\n\t\t# elseif(COMPILER_SUPPORTS_CXX0X)\n\t\t# \tadd_definitions(-std=gnu++0x)\n\t\t# else()\n\t\t# \tmessage(SEND_ERROR \"The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.\")\n\t\t# endif()\n\telseif(APPLE) ## \\todo TODO: do a better test and send error on unsupported c++14 compiler\n\t    add_definitions(-std=c++14 -stdlib=libc++)\n\tendif()\nelse()\n\t## Allow C++11 + other flags\n\tinclude(CheckCXXCompilerFlag)\n\tget_filename_component(currentBuildTool ${CMAKE_BUILD_TOOL} NAME_WE)\t# tool that can launch the native build system. returned value may be the full path\n\tif(${currentBuildTool} MATCHES \"(msdev|devenv|nmake|MSBuild)\")\n\n\t\tadd_compile_options(\"$<$<COMPILE_LANGUAGE:CXX>:/W3;/DNOMINMAX;/MP;-D_USE_MATH_DEFINES>\")\n\t    #add_definitions(/W3 /DNOMINMAX /MP -D_USE_MATH_DEFINES)# /D_ITERATOR_DEBUG_LEVEL=1 because you need all external DLl to compile with this flag too\n\t    set(CMAKE_CONFIGURATION_TYPES \"RelWithDebInfo;Release;Debug\" CACHE STRING \"\" FORCE)\n\t    set(CMAKE_CXX_STANDARD 14)\n\t\tset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\t\tset(CMAKE_CXX_EXTENSIONS OFF)\n\telseif(${currentBuildTool} MATCHES \"(make|gmake|ninja)\")\n\t\tadd_definitions(\"-fpermissive -fPIC -Wall -Wno-unknown-pragmas -Wno-sign-compare -g -std=c++17 -D__forceinline=\\\"inline\\ __attribute__((always_inline))\\\"\")\n\telseif(APPLE) ## \\todo TODO: do a better test and send error on unsupported c++14 compiler\n\t    add_definitions(-std=c++17 -stdlib=libc++)\n\tendif()\nendif()\n\nset(INSTALL_STANDALONE ON)\n\n## Set default build output binaries (used also in sub CMakeLists.txt) :\nset(BIN_BUILT_DIR \"bin\")\nif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\tset(ARCHI_BUILT_DIR \"x64\")\n\tset(LIB_BUILT_DIR \"lib64\")\nelse()\n\tset(ARCHI_BUILT_DIR \"x86\")\n\tset(LIB_BUILT_DIR \"lib\")\nendif()\n\noption(SEPARATE_CONFIGURATIONS \"Clearer separation between configurations\" OFF)\nSET(CMAKE_INSTALL_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/install)\nSET(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_ROOT})\n\nif(DEFINED CMAKE_BUILD_TYPE) ## for mono config type (make/nmake/ninja based)\n\tif(${CMAKE_BUILD_TYPE} MATCHES \"Debug\")\n\t\tset(CMAKE_DEBUG_POSTFIX \"_d\")\n\telseif(${CMAKE_BUILD_TYPE} MATCHES \"RelWithDebInfo\")\n\t\tset(CMAKE_RELWITHDEBINFO_POSTFIX \"_rwdi\")\n\telseif(${CMAKE_BUILD_TYPE} MATCHES \"MinSizeRel\")\n\t\tset(CMAKE_MINSIZEREL_POSTFIX \"_msr\")\n\telseif(${CMAKE_BUILD_TYPE} MATCHES \"Release\")\n\t\tset(CMAKE_RELEASE_POSTFIX \"\")\n\tendif()\n\n\tif(SEPARATE_CONFIGURATIONS)\n\t\tSET(CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE} \t${CMAKE_INSTALL_ROOT}/${CMAKE_BUILD_TYPE})\n\telse()\n\t\tSET(CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE} \t${CMAKE_INSTALL_ROOT})\n\tendif()\n\n\tMESSAGE(STATUS \"Install path set to ${CMAKE_INSTALL_PREFIX}.\")\n\tSET(CMAKE_OUTPUT_LIB_${CMAKE_BUILD_TYPE} \t${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/lib)\n\tSET(CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE} \t${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/bin)\n\n\tset(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE} \t${CMAKE_OUTPUT_LIB_${CMAKE_BUILD_TYPE}})\n\tset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE} \t${CMAKE_OUTPUT_LIB_${CMAKE_BUILD_TYPE}})\n\tset(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE} \t${CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE}})\n\tset(CMAKE_PDB_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE} \t\t${CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE}})\nendif()\nforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) ## for multi config types (MSVC based)\n\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\tif(${CONFIG_TYPES} MATCHES \"Debug\")\n\t\tset(CMAKE_DEBUG_POSTFIX \"_d\")\n\telseif(${CONFIG_TYPES} MATCHES \"RelWithDebInfo\")\n\t\tset(CMAKE_RELWITHDEBINFO_POSTFIX \"_rwdi\")\n\telseif(${CONFIG_TYPES} MATCHES \"MinSizeRel\")\n\t\tset(CMAKE_MINSIZEREL_POSTFIX \"_msr\")\n\telseif(${CMAKE_BUILD_TYPE} MATCHES \"Release\")\n\t\tset(CMAKE_RELEASE_POSTFIX \"\")\n\tendif()\n\n\tif(SEPARATE_CONFIGURATIONS)\n\t\tSET(CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC} ${CMAKE_INSTALL_ROOT}/${CONFIG_TYPES})\n\telse()\n\t\tSET(CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC} ${CMAKE_INSTALL_ROOT})\n\tendif()\n\t\n\tMESSAGE(STATUS \"Install path for ${CONFIG_TYPES} set to ${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}.\")\n\tSET(CMAKE_OUTPUT_LIB_${CONFIG_TYPES_UC} \t${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/lib)\n\tSET(CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC} \t${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/bin)\n\n\tset(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC} \t${CMAKE_OUTPUT_LIB_${CONFIG_TYPES_UC}})\n\tset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC} \t${CMAKE_OUTPUT_LIB_${CONFIG_TYPES_UC}})\n\tset(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC} \t${CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC}})\n\tset(CMAKE_PDB_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC} \t\t${CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC}})\nendforeach()\n\n\n# Settings for RPATH\nif (NOT WIN32)\n\t# Default config of Fedora at INRIA has no LD_LIBRARY_PATH (for security reasons I guess)\n\t# So at least I had \"./\" in RPATH and found link paths\n\t#set(CMAKE_SKIP_RPATH TRUE)\n\t#SET(CMAKE_SKIP_BUILD_RPATH  FALSE)\n\tSET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)\n\n\tSET(CMAKE_INSTALL_RPATH \"$ORIGIN\")\n\t#SET(CMAKE_INSTALL_RPATH \"./\")\n\t#SET(CMAKE_INSTALL_RPATH \"./:/usr/lib64/:/usr/lib/:/usr/local/lib64/:/usr/local/lib/\") # This one causes be a problem -> a \"default\" version of libGL (swrast) is located in /usr/lib64 and was selected instead of nvidia one (in /usr/lib64/nividia)\n\n\tSET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)\nendif()\n\n\nset(SIBR_PROGRAMARGS \"\" CACHE STRING \"Default program arguments used in Visual Studio target properties\")\nif (\"${SIBR_PROGRAMARGS}\" STREQUAL \"\")\n  if (DEFINED ENV{SIBR_PROGRAMARGS})\n    set(SIBR_PROGRAMARGS \"$ENV{SIBR_PROGRAMARGS}\" CACHE STRING \"Default program arguments used in Visual Studio target properties\" FORCE)\n    message( STATUS \"Using program options found in environment variable 'SIBR_PROGRAMARGS' => '${SIBR_PROGRAMARGS}'\")\n  else()\n    message(\n      \"Note you can provide default program options for Visual Studio target properties by either setting\"\n      \" a value for the cmake cached variable 'SIBR_PROGRAMARGS' or by setting a new environment \"\n      \"variable 'SIBR_PROGRAMARGS'\")\n  endif()\nendif()\n\nadd_custom_target(PREBUILD ALL)\n\n## Include all projects\nset(SIBR_PROJECTS_SAMPLES_SUBPAGE_REF \"\")\nset(SIBR_PROJECTS_OURS_SUBPAGE_REF \"\")\nset(SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF \"\")\nset(SIBR_PROJECTS_OTHERS_SUBPAGE_REF \"\")\nset(SIBR_PROJECTS_SAMPLES_REF_REF \"\")\nset(SIBR_PROJECTS_OURS_REF_REF \"\")\nset(SIBR_PROJECTS_TOOLBOX_REF_REF \"\")\nset(SIBR_PROJECTS_OTHERS_REF_REF \"\")\nset(DOXY_APP_SPECIFIC_IMG_PATH \"\")\nset(DOXY_DOC_EXCLUDE_PATTERNS_DIRS \"\")\nADD_SUBDIRECTORY(src)\n\n\n## handle documentation\nif (WIN32)\nADD_SUBDIRECTORY(docs)\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/LICENSE.md",
    "content": "SIBR License  \n============  \n\nThe sibr system is licensed under the Apache 2.0 license, except for some projects in specific branches (in src/projects) that have separate licenses in those directories.\n\nPlease verify the LICENSE.md file for those directories.\n\n--------------\n\n\n\n                                Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   \n   Copyright 2024 Inria / Universite Cote d'Azur\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/README.md",
    "content": "# SIBR Core\n\n**SIBR** is a System for Image-Based Rendering.  \nIt is built around the *sibr-core* in this repo and several *Projects* implementing published research papers.  \nFor more complete documentation, see here: [SIBR Documentation](https://sibr.gitlabpages.inria.fr) \n  \nThis **SIBR core** repository provides :\n- a basic Image-Based Renderer\n- a per-pixel implementation of Unstructured Lumigraph (ULR)\n- several dataset tools & pipelines do process input images\n  \nDetails on how to run in the documentation and in the section below.  \nIf you use this code in a publication, please cite the system as follows:\n\n```\n@misc{sibr2020,\n   author       = \"Bonopera, Sebastien and Esnault, Jerome and Prakash, Siddhant and Rodriguez, Simon and Thonat, Theo and Benadel, Mehdi and Chaurasia, Gaurav and Philip, Julien and Drettakis, George\",\n   title        = \"sibr: A System for Image Based Rendering\",\n   year         = \"2020\",\n   url          = \"https://gitlab.inria.fr/sibr/sibr_core\"\n}\n```\n\n## Setup\n\n**Note**: The current release is for *Windows 10* only. We are planning a Linux release soon.\n\n#### Binary distribution\n\nThe easiest way to use SIBR is to download the binary distribution. All steps described below, including all preprocessing for your datasets will work using this code.\n\nDownload the distribution from the page: https://sibr.gitlabpages.inria.fr/download.html (Core, 57Mb); unzip the file and rename the directory \"install\".\n\n#### Install requirements\n\n- [**Visual Studio 2019**](https://visualstudio.microsoft.com/fr/downloads/)\n- [**Cmake 3.16+**](https://cmake.org/download)\n- [**7zip**](https://www.7-zip.org)\n- [**Python 3.8+**](https://www.python.org/downloads/) for shaders installation scripts and dataset preprocess scripts\n- [**Doxygen 1.8.17+**](https://www.doxygen.nl/download.html#srcbin) for documentation\n- [**CUDA 10.1+**](https://developer.nvidia.com/cuda-downloads) and [**CUDnn**](https://developer.nvidia.com/cudnn) if projects requires it\n\nMake sure Python, CUDA and Doxygen are in the PATH\n\nIf you have Chocolatey, you can grab most of these with this command:\n\n```sh\nchoco install cmake 7zip python3 doxygen.install cuda\n\n## Visual Studio is available on Chocolatey,\n## though we do advise to set it from Visual Studio Installer and to choose your licensing accordingly\nchoco install visualstudio2019community\n```\n\n#### Generation of the solution\n\n- Checkout this repository's master branch:\n  \n  ```sh\n  ## through HTTPS\n  git clone https://gitlab.inria.fr/sibr/sibr_core.git -b master\n  ## through SSH\n  git clone git@gitlab.inria.fr:sibr/sibr_core.git -b master\n  ```\n- Run Cmake-gui once, select the repo root as a source directory, `build/` as the build directory. Configure, select the Visual Studio C++ Win64 compiler\n- Select the projects you want to generate among the BUILD elements in the list (you can group Cmake flags by categories to access those faster)\n- Generate\n\n#### Compilation\n\n- Open the generated Visual Studio solution (`build/sibr_projects.sln`)\n- Build the `ALL_BUILD` target, and then the `INSTALL` target\n- The compiled executables will be put in `install/bin`\n- TODO: are the DLLs properly installed?\n\n#### Compilation of the documentation\n\n- Open the generated Visual Studio solution (`build/sibr_projects.sln`)\n- Build the `DOCUMENTATION` target\n- Run `install/docs/index.html` in a browser\n\n\n## Scripts\n\nSome scripts will require you to install `PIL`, and `convert` from `ImageMagick`.\n\n```sh\n## To install pillow\npython -m pip install pillow\n\n## If you have Chocolatey, you can install imagemagick from this command\nchoco install imagemagick\n```\n\n## Troubleshooting\n\n#### Bugs and Issues\n\nWe will track bugs and issues through the Issues interface on gitlab. Inria gitlab does not allow creation of external accounts, so if you have an issue/bug please email <code>sibr@inria.fr</code> and we will either create a guest account or create the issue on our side.\n\n#### Cmake complaining about the version\n\nif you are the first to use a very recent Cmake version, you will have to update `CHECKED_VERSION` in the root `CmakeLists.txt`.\n\n#### Weird OpenCV error\n\nyou probably selected the 32-bits compiler in Cmake-gui.\n\n#### `Cmd.exe failed with error 009` or similar\n\nmake sure Python is installed and in the path. \n\n#### `BUILD_ALL` or `INSTALL` fail because of a project you don't really need\n\nbuild and install each project separately by selecting the proper targets.\n\n#### Error in CUDA headers under Visual Studio 2019\n\nmake sure CUDA >= 10.1 (first version to support VS2019) is installed.\n\n## To run an example\n\nFor more details, please see the documentation: http://sibr.gitlabpages.inria.fr\n\nDownload a dataset from: https://repo-sam.inria.fr/fungraph/sibr-datasets/\n\ne.g., the *sibr-museum-front* dataset in the *DATASETS_PATH* directory.\n\n```\nwget https://repo-sam.inria.fr/fungraph/sibr-datasets/museum_front27_ulr.zip\n```\n\nOnce you have built the system or downloaded the binaries (see above), go to *install/bin* and you can run:\n```\n\tsibr_ulrv2_app.exe --path DATASETS_PATH/sibr-museum-front\n```\n\nYou will have an interactive viewer and you can navigate freely in the captured scene. \nOur default interactive viewer has a main view running the algorithm and a top view to visualize the position of the calibrated cameras. By default you are in WASD mode, and can toggle to trackball using the \"y\" key. Please see the page [Interface](https://sibr.gitlabpages.inria.fr/docs/nightly/howto_sibr_useful_objects.html) for more details on the interface.\n\nPlease see the documentation on how to create a dataset from your own scene, and the various other IBR algorithms available.\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/MSVCsetUserCommand.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(__MSVCsetUserCommand_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__MSVCsetUserCommand_cmake_INCLUDED__ ON)\nendif()\n\n## Allow to configure the Debugger settings of visual studio\n## Note: Using this command under linux doesn't affect anything\n## On run Debug Windows local : visual will try to load a specific COMMAND with ARGS in the provided WORKING_DIR\n##\n## usage:\n## MSVCsetUserCommand(\t<targetName>\n##    [COMMAND \t\t\t<myCustomAppToLaunch> | [ PATH <myCustomDirWhereIsDefaultTargetFileNameToLaunch> [FILE <myCustomExecFileToLaunch>] ] ]\n##    ARGS \t\t\t\t<associatedArguments>\n##    WORKING_DIR\t\t<whereStartTheProgram>\n## )\n##\n## Warning 1 : All arugments () must be passed under quotes\n## Warning 2 : WORKING_DIR path arg have to finish with remain slah '/'\n## Warning 3 : use COMMAND for external app OR PATH (optionaly with FILE) option(s) to set your built/installed/moved target\n##\n## Example 1:\n## include(MSVCsetUserCommand)\n## MSVCsetUserCommand(\tUnityRenderingPlugin\n## \t  COMMAND \t\t\t\"C:/Program Files (x86)/Unity/Editor/Unity.exe\"\n## \t  ARGS\t\t\t\t\"-force-opengl -projectPath \\\"${CMAKE_HOME_DIRECTORY}/UnityPlugins/RenderingPluginExample/UnityProject\\\"\"\n## \t  WORKING_DIR\t\t\"${CMAKE_HOME_DIRECTORY}/UnityPlugins/RenderingPluginExample/UnityProject\"\n## \t  VERBOSE\n## )\n##\n## Example 2:\n## include(MSVCsetUserCommand)\n## MSVCsetUserCommand(\tibrApp\n## \t  PATH \t\t\t\t\"C:/Program Files (x86)/workspace/IBR/install\"\n##\t  FILE\t\t\t\t\"ibrApp${CMAKE_EXECUTABLE_SUFFIX}\" ## this option line is optional since the target name didn't change between build and install step\n## \t  ARGS\t\t\t\t\"-path \\\"${CMAKE_HOME_DIRECTORY}/dataset\\\"\"\n## \t  WORKING_DIR\t\t\"${CMAKE_HOME_DIRECTORY}\"\n## \t  VERBOSE\n## )\n##\nfunction(MSVCsetUserCommand targetName)\n    cmake_parse_arguments(MSVCsuc \"VERBOSE\" \"PATH;FILE;COMMAND;ARGS;WORKING_DIR\" \"\" ${ARGN} )\n\n\t## If no arguments are given, do not create an unecessary .vcxproj.user file\n\tset(MSVCsuc_DEFAULT OFF)\n\n\tif(MSVCsuc_PATH AND MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n\tif(MSVCsuc_FILE AND MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n\tif(NOT MSVCsuc_COMMAND)\n\t\tif(MSVCsuc_PATH AND MSVCsuc_FILE)\n\t\t\tset(MSVCsuc_COMMAND \"${MSVCsuc_PATH}\\\\${MSVCsuc_FILE}\")\n\t\telseif(MSVCsuc_PATH)\n\t\t\tset(MSVCsuc_COMMAND \"${MSVCsuc_PATH}\\\\$(TargetFileName)\")\n\t\telse()\n\t\t\tset(MSVCsuc_COMMAND \"$(TargetPath)\") ## => $(TargetDir)\\$(TargetName)$(TargetExt)\n\t\tendif()\n\telseif(MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n        # NOTE: there was a typo here. there is an else if written after else statement\n        # changing the order of the else if statement\n\tif(MSVCsuc_WORKING_DIR)\n\t\tfile(TO_NATIVE_PATH ${MSVCsuc_WORKING_DIR} MSVCsuc_WORKING_DIR)\n\telseif(MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\telse()\n\t\tset(MSVCsuc_WORKING_DIR \"$(ProjectDir)\")\n\tendif()\n\n\tif(NOT MSVCsuc_ARGS)\n\t\tset(MSVCsuc_ARGS \"\")\n\telseif(MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n\tif(MSVC10 OR (MSVC AND MSVC_VERSION GREATER 1600)) # 2010 or newer\n\n\t\tif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\t\t\tset(PLATEFORM_BITS x64)\n\t\telse()\n\t\t\tset(PLATEFORM_BITS Win32)\n\t\tendif()\n\n\t\tif(NOT MSVCsuc_DEFAULT AND PLATEFORM_BITS)\n\n\t\t\tfile(WRITE \"${CMAKE_CURRENT_BINARY_DIR}/${targetName}.vcxproj.user\"\n\t\t\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\n<Project ToolsVersion=\\\"4.0\\\" xmlns=\\\"http://schemas.microsoft.com/developer/msbuild/2003\\\">\n  <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='Release|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n\t<LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n  <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='Debug|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n    <LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n    <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='MinSizeRel|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n    <LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n    <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='RelWithDebInfo|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n    <LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n</Project>\"\n\t\t\t)\n\t\t\tif(MSVCsuc_VERBOSE)\n\t\t\t\tmessage(STATUS \"[MSVCsetUserCommand] Write ${CMAKE_CURRENT_BINARY_DIR}/${targetName}.vcxproj.user file\")\n\t\t\t\tmessage(STATUS \"   to execute ${MSVCsuc_COMMAND} ${MSVCsuc_ARGS}\")\n\t\t\t\tmessage(STATUS \"   from derectory ${MSVCsuc_WORKING_DIR}\")\n\t\t\t\tmessage(STATUS \"   on visual studio run debugger button\")\n\t\t\tendif()\n\n\t\telse()\n\t\t\tmessage(WARNING \"PLATEFORM_BITS is undefined...\")\n\t\tendif()\n\n\telse()\n\t\tif(MSVCsuc_VERBOSE)\n\t\t\tmessage(WARNING \"MSVCsetUserCommand is disable because too old MSVC is used (need MSVC10 2010 or newer)\")\n\t\tendif()\n\tendif()\n\nendfunction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/Modules/FindASSIMP.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Try to find the ASSIMP library\n## Once done this will define\n##\n##  \tASSIMP_FOUND \t\t- system has ASSIMP\n##  \tASSIMP_INCLUDE_DIR \t- The ASSIMP include directory\n##  \tASSIMP_LIBRARIES \t- The libraries needed to use ASSIMP\n##  \tASSIMP_CMD \t\t\t- the full path of ASSIMP executable\n##\tASSIMP_DYNAMIC_LIB\t- the Assimp dynamic lib (available only on windows as .dll file for the moment)\n##\n## Edited for using a bugfixed version of Assimp\n\nif(NOT ASSIMP_DIR)\n    set(ASSIMP_DIR \"$ENV{ASSIMP_DIR}\" CACHE PATH \"ASSIMP root directory\")\n\tmessage(\"NO ASSIMP DIR \" ASSIMP_DIR )\n\tfile(TO_CMAKE_PATH \"/data/graphdeco/share/usr/local\" ASSIMP_DIR)\n        set(ASSIMP_DIR \"/data/graphdeco/share/usr/local\" )\n\tmessage(\"SETTING ASSIMP DIR \" ASSIMP_DIR )\nendif()\nif(ASSIMP_DIR)\n\tfile(TO_CMAKE_PATH ${ASSIMP_DIR} ASSIMP_DIR)\n\tfile(TO_CMAKE_PATH \"/data/graphdeco/share/usr/local\" ASSIMP_DIR)\n\tmessage(\"ASSIMP DIR \" ASSIMP_DIR )\nendif()\n\n\n## set the LIB POSTFIX to find in a right directory according to what kind of compiler we use (32/64bits)\nif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\tset(ASSIMP_SEARCH_LIB \"lib64\")\n\tset(ASSIMP_SEARCH_BIN \"bin64\")\n\tset(ASSIMP_SEARCH_LIB_PATHSUFFIXE \"x64\")\nelse()\n\tset(ASSIMP_SEARCH_LIB \"lib32\")\n\tset(ASSIMP_SEARCH_BIN \"bin32\")\n\tset(ASSIMP_SEARCH_LIB_PATHSUFFIXE \"x86\")\nendif()\n\nset(PROGRAMFILESx86 \"PROGRAMFILES(x86)\")\n\n\nFIND_PATH(ASSIMP_INCLUDE_DIR\n\tNAMES assimp/config.h\n\tPATHS\n\t\t${ASSIMP_DIR}\n\t\t## linux\n\t\t/usr\n\t\t/usr/include\n\t\t/usr/local\n\t\t/opt/local\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/Assimp\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/Assimp\"\n\t\t\"$ENV{ProgramW6432}/Assimp\"\n\tPATH_SUFFIXES include\n)\n\n\nFIND_LIBRARY(ASSIMP_LIBRARY\n\tNAMES assimp-vc140-mt assimp\n\tPATHS\n\t\t${ASSIMP_DIR}/${ASSIMP_SEARCH_LIB}\n\t\t${ASSIMP_DIR}/lib\n\t\t${ASSIMP_DIR}/lib64\n\t\t## linux\n\t\t/usr/${ASSIMP_SEARCH_LIB}\n\t\t/usr/local/${ASSIMP_SEARCH_LIB}\n\t\t/opt/local/${ASSIMP_SEARCH_LIB}\n\t\t/usr/lib\n\t\t/usr/lib64\n\t\t/usr/local/lib\n\t\t/opt/local/lib\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/Assimp/${ASSIMP_SEARCH_LIB}\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/Assimp/${ASSIMP_SEARCH_LIB}\"\n\t\t\"$ENV{ProgramW6432}/Assimp/${ASSIMP_SEARCH_LIB}\"\n\t\t\"$ENV{PROGRAMFILES}/Assimp/lib\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/Assimp/lib\"\n\t\t\"$ENV{ProgramW6432}/Assimp/lib\"\n\tPATH_SUFFIXES ${ASSIMP_SEARCH_LIB_PATHSUFFIXE}\n)\nset(ASSIMP_LIBRARIES ${ASSIMP_LIBRARY})\n\n\nif(ASSIMP_LIBRARY)\n\tget_filename_component(ASSIMP_LIBRARY_DIR ${ASSIMP_LIBRARY} PATH)\n\tif(WIN32)\n\t\tfile(GLOB ASSIMP_DYNAMIC_LIB \"${ASSIMP_LIBRARY_DIR}/assimp*.dll\")\n\t\tif(NOT ASSIMP_DYNAMIC_LIB)\n\t\t\tmessage(\"ASSIMP_DYNAMIC_LIB is missing... at ${ASSIMP_LIBRARY_DIR}\")\n\t\tendif()\n\tendif()\n\tset(ASSIMP_DYNAMIC_LIB ${ASSIMP_DYNAMIC_LIB} CACHE PATH \"Windows dll location\")\nendif()\n\nMARK_AS_ADVANCED(ASSIMP_DYNAMIC_LIB ASSIMP_INCLUDE_DIR ASSIMP_LIBRARIES)\n\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(ASSIMP\n\tREQUIRED_VARS ASSIMP_INCLUDE_DIR ASSIMP_LIBRARIES\n\tFAIL_MESSAGE \"ASSIMP wasn't found correctly. Set ASSIMP_DIR to the root SDK installation directory.\"\n)\n\nif(NOT ASSIMP_FOUND)\n\tset(ASSIMP_DIR \"\" CACHE STRING \"Path to ASSIMP install directory\")\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/Modules/FindEGL.cmake",
    "content": "#.rst:\n# FindEGL\n# -------\n#\n# Try to find EGL.\n#\n# This will define the following variables:\n#\n# ``EGL_FOUND``\n#     True if (the requested version of) EGL is available\n# ``EGL_VERSION``\n#     The version of EGL; note that this is the API version defined in the\n#     headers, rather than the version of the implementation (eg: Mesa)\n# ``EGL_LIBRARIES``\n#     This can be passed to target_link_libraries() instead of the ``EGL::EGL``\n#     target\n# ``EGL_INCLUDE_DIRS``\n#     This should be passed to target_include_directories() if the target is not\n#     used for linking\n# ``EGL_DEFINITIONS``\n#     This should be passed to target_compile_options() if the target is not\n#     used for linking\n#\n# If ``EGL_FOUND`` is TRUE, it will also define the following imported target:\n#\n# ``EGL::EGL``\n#     The EGL library\n#\n# In general we recommend using the imported target, as it is easier to use.\n# Bear in mind, however, that if the target is in the link interface of an\n# exported library, it must be made available by the package config file.\n#\n# Since pre-1.0.0.\n\n#=============================================================================\n# Copyright 2014 Alex Merry <alex.merry@kde.org>\n# Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions\n# are met:\n#\n# 1. Redistributions of source code must retain the copyright\n#    notice, this list of conditions and the following disclaimer.\n# 2. Redistributions in binary form must reproduce the copyright\n#    notice, this list of conditions and the following disclaimer in the\n#    documentation and/or other materials provided with the distribution.\n# 3. The name of the author may not be used to endorse or promote products\n#    derived from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#=============================================================================\n\ninclude(CheckCXXSourceCompiles)\ninclude(CMakePushCheckState)\n\n# Use pkg-config to get the directories and then use these values\n# in the FIND_PATH() and FIND_LIBRARY() calls\nfind_package(PkgConfig)\npkg_check_modules(PKG_EGL QUIET egl)\n\nset(EGL_DEFINITIONS ${PKG_EGL_CFLAGS_OTHER})\n\nfind_path(EGL_INCLUDE_DIR\n    NAMES\n        EGL/egl.h\n    HINTS\n        ${PKG_EGL_INCLUDE_DIRS}\n)\nfind_library(EGL_LIBRARY\n    NAMES\n        EGL\n    HINTS\n        ${PKG_EGL_LIBRARY_DIRS}\n)\n\n# NB: We do *not* use the version information from pkg-config, as that\n#     is the implementation version (eg: the Mesa version)\nif(EGL_INCLUDE_DIR)\n    # egl.h has defines of the form EGL_VERSION_x_y for each supported\n    # version; so the header for EGL 1.1 will define EGL_VERSION_1_0 and\n    # EGL_VERSION_1_1.  Finding the highest supported version involves\n    # finding all these defines and selecting the highest numbered.\n    file(READ \"${EGL_INCLUDE_DIR}/EGL/egl.h\" _EGL_header_contents)\n    string(REGEX MATCHALL\n        \"[ \\t]EGL_VERSION_[0-9_]+\"\n        _EGL_version_lines\n        \"${_EGL_header_contents}\"\n    )\n    unset(_EGL_header_contents)\n    foreach(_EGL_version_line ${_EGL_version_lines})\n        string(REGEX REPLACE\n            \"[ \\t]EGL_VERSION_([0-9_]+)\"\n            \"\\\\1\"\n            _version_candidate\n            \"${_EGL_version_line}\"\n        )\n        string(REPLACE \"_\" \".\" _version_candidate \"${_version_candidate}\")\n        if(NOT DEFINED EGL_VERSION OR EGL_VERSION VERSION_LESS _version_candidate)\n            set(EGL_VERSION \"${_version_candidate}\")\n        endif()\n    endforeach()\n    unset(_EGL_version_lines)\nendif()\n\ncmake_push_check_state(RESET)\nlist(APPEND CMAKE_REQUIRED_LIBRARIES \"${EGL_LIBRARY}\")\nlist(APPEND CMAKE_REQUIRED_INCLUDES \"${EGL_INCLUDE_DIR}\")\n\ncheck_cxx_source_compiles(\"\n#include <EGL/egl.h>\n\nint main(int argc, char *argv[]) {\n    EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0;\n    eglDestroyContext(dpy, ctx);\n}\" HAVE_EGL)\n\ncmake_pop_check_state()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(EGL\n    FOUND_VAR\n        EGL_FOUND\n    REQUIRED_VARS\n        EGL_LIBRARY\n        EGL_INCLUDE_DIR\n        HAVE_EGL\n    VERSION_VAR\n        EGL_VERSION\n)\n\nif(EGL_FOUND AND NOT TARGET EGL::EGL)\n    add_library(EGL::EGL UNKNOWN IMPORTED)\n    set_target_properties(EGL::EGL PROPERTIES\n        IMPORTED_LOCATION \"${EGL_LIBRARY}\"\n        INTERFACE_COMPILE_OPTIONS \"${EGL_DEFINITIONS}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${EGL_INCLUDE_DIR}\"\n    )\nendif()\n\nmark_as_advanced(EGL_LIBRARY EGL_INCLUDE_DIR HAVE_EGL)\n\n# compatibility variables\nset(EGL_LIBRARIES ${EGL_LIBRARY})\nset(EGL_INCLUDE_DIRS ${EGL_INCLUDE_DIR})\nset(EGL_VERSION_STRING ${EGL_VERSION})\n\ninclude(FeatureSummary)\nset_package_properties(EGL PROPERTIES\n    URL \"https://www.khronos.org/egl/\"\n    DESCRIPTION \"A platform-agnostic mechanism for creating rendering surfaces for use with other graphics libraries, such as OpenGL|ES and OpenVG.\"\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/Modules/FindEmbree.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n## Important Note:\n## This is not an official Find*cmake. It has been written for searching through\n## a custom path (EMBREE_DIR) before checking elsewhere.\n##\n## FindEMBREE.cmake\n## Find EMBREE's includes and library\n##\n## This module defines :\n## \t[in] \tEMBREE_DIR, The base directory to search for EMBREE (as cmake var or env var)\n## \t[out] \tEMBREE_INCLUDE_DIR where to find EMBREE.h\n## \t[out] \tEMBREE_LIBRARIES, EMBREE_LIBRARY, libraries to link against to use EMBREE\n## \t[out] \tEMBREE_FOUND, If false, do not try to use EMBREE.\n##\n\n\nif(NOT EMBREE_DIR)\n    set(EMBREE_DIR \"$ENV{EMBREE_DIR}\" CACHE PATH \"EMBREE root directory\")\nendif()\nif(EMBREE_DIR)\n\tfile(TO_CMAKE_PATH ${EMBREE_DIR} EMBREE_DIR)\nendif()\n\n\n## set the LIB POSTFIX to find in a right directory according to what kind of compiler we use (32/64bits)\nif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\tset(EMBREE_SEARCH_LIB \"lib64\")\n\tset(EMBREE_SEARCH_BIN \"bin64\")\n\tset(EMBREE_SEARCH_LIB_PATHSUFFIXE \"x64\")\nelse()\n\tset(EMBREE_SEARCH_LIB \"lib32\")\n\tset(EMBREE_SEARCH_BIN \"bin32\")\n\tset(EMBREE_SEARCH_LIB_PATHSUFFIXE \"x86\")\nendif()\n\nset(PROGRAMFILESx86 \"PROGRAMFILES(x86)\")\n\nFIND_PATH(EMBREE_INCLUDE_DIR\n\tNAMES embree3/rtcore_geometry.h\n\tPATHS\n\t\t${EMBREE_DIR}\n\t\t## linux\n\t\t/usr\n\t\t/usr/local\n\t\t/opt/local\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/EMBREE\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/EMBREE\"\n\t\t\"$ENV{ProgramW6432}/EMBREE\"\n\tPATH_SUFFIXES include\n)\n\nFIND_LIBRARY(EMBREE_LIBRARY\n\tNAMES embree3\n\tPATHS\n\t\t${EMBREE_DIR}/${EMBREE_SEARCH_LIB}\n\t\t${EMBREE_DIR}/lib\n\t\t## linux\n\t\t/usr/${EMBREE_SEARCH_LIB}\n\t\t/usr/local/${EMBREE_SEARCH_LIB}\n\t\t/opt/local/${EMBREE_SEARCH_LIB}\n\t\t/usr/lib\n\t\t/usr/local/lib\n\t\t/opt/local/lib\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/EMBREE/${EMBREE_SEARCH_LIB}\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/EMBREE/${EMBREE_SEARCH_LIB}\"\n\t\t\"$ENV{ProgramW6432}/EMBREE/${EMBREE_SEARCH_LIB}\"\n\t\t\"$ENV{PROGRAMFILES}/EMBREE/lib\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/EMBREE/lib\"\n\t\t\"$ENV{ProgramW6432}/EMBREE/lib\"\n\tPATH_SUFFIXES ${EMBREE_SEARCH_LIB_PATHSUFFIXE}\n)\nset(EMBREE_LIBRARIES ${EMBREE_LIBRARY})\n\nMARK_AS_ADVANCED(EMBREE_INCLUDE_DIR EMBREE_LIBRARIES)\n\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(EMBREE\n\tREQUIRED_VARS EMBREE_INCLUDE_DIR EMBREE_LIBRARIES\n\tFAIL_MESSAGE \"EMBREE wasn't found correctly. Set EMBREE_DIR to the root SDK installation directory.\"\n)\n\nif(NOT EMBREE_FOUND)\n\tset(EMBREE_DIR \"\" CACHE STRING \"Path to EMBREE install directory\")\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/Modules/FindFFMPEG.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Try to find the FFMPEG library\n## Once done this will define\n##\n##  \tFFMPEG_FOUND \t\t- system has FFmpeg\n##  \tFFMPEG_INCLUDE_DIR \t- The FFmpeg include directory\n##  \tFFMPEG_LIBRARIES \t- The libraries needed to use FFmpeg\n##\t\tFFMPEG_DYNAMIC_LIBS\t- DLLs for windows\n\n\nif(NOT FFMPEG_DIR)\n    set(FFMPEG_DIR \"$ENV{FFMPEG_DIR}\" CACHE PATH \"FFMPEG_DIR root directory\")\nendif()\n\nif(FFMPEG_DIR)\n\tfile(TO_CMAKE_PATH ${FFMPEG_DIR} FFMPEG_DIR)\nendif()\n\nMACRO(FFMPEG_FIND varname shortname headername)\n\t\n\t# Path to include dirs\n\tFIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS \n\t\tNAMES \"lib${shortname}/${headername}\" \n\t\tPATHS\n\t\t\t\"${FFMPEG_DIR}/include\" # modify this to adapt according to OS/compiler\t\n\t\t\t\"/usr/include\"\n\t\t\t\"/usr/include/ffmpeg\"\t\t\n\t)\n\t\t\n\t#Add libraries\n\tIF(${FFMPEG_${varname}_INCLUDE_DIRS} STREQUAL \"FFMPEG_${varname}_INCLUDE_DIR-NOTFOUND\")\n\t\tMESSAGE(STATUS \"Can't find includes for ${shortname}...\")\n\tELSE()\n\t\tFIND_LIBRARY(FFMPEG_${varname}_LIBRARIES\n\t\t\tNAMES ${shortname}\n\t\t\tPATHS\n\t\t\t\t${FFMPEG_DIR}/lib\n\t\t\t\t\"/usr/lib\"\n\t\t\t\t\"/usr/lib64\"\n\t\t\t\t\"/usr/local/lib\"\n\t\t\t\t\"/usr/local/lib64\"\n\t\t)\n\n\t\t# set libraries and other variables\n\t\tSET(FFMPEG_${varname}_FOUND 1)\n\t\tSET(FFMPEG_${varname}_INCLUDE_DIRS ${FFMPEG_${varname}_INCLUDE_DIR})\n\t\tSET(FFMPEG_${varname}_LIBS ${FFMPEG_${varname}_LIBRARIES})\n\tENDIF()\n ENDMACRO(FFMPEG_FIND)\n\n#Calls to ffmpeg_find to get  librarires ------------------------------\nFFMPEG_FIND(LIBAVFORMAT avformat avformat.h)\nFFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h)\nFFMPEG_FIND(LIBAVCODEC  avcodec  avcodec.h)\nFFMPEG_FIND(LIBAVUTIL   avutil   avutil.h)\nFFMPEG_FIND(LIBSWSCALE  swscale  swscale.h)\n \n# check if libs are found and set FFMPEG related variables\n#SET(FFMPEG_FOUND \"NO\")\nIF(FFMPEG_LIBAVFORMAT_FOUND \n\tAND FFMPEG_LIBAVDEVICE_FOUND \n\tAND FFMPEG_LIBAVCODEC_FOUND \n\tAND FFMPEG_LIBAVUTIL_FOUND \n\tAND FFMPEG_LIBSWSCALE_FOUND)\n \n\t# All ffmpeg libs are here\n    SET(FFMPEG_FOUND \"YES\")\n\tSET(FFMPEG_INCLUDE_DIR ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS})\n\tSET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS})\n\tSET(FFMPEG_LIBRARIES\n        ${FFMPEG_LIBAVFORMAT_LIBS}\n        ${FFMPEG_LIBAVDEVICE_LIBS}\n        ${FFMPEG_LIBAVCODEC_LIBS}\n        ${FFMPEG_LIBAVUTIL_LIBS}\n\t\t${FFMPEG_LIBSWSCALE_LIBS}\t)\n\t\t\n\t# add dynamic libraries\n\tif(WIN32)\n\t\tfile(GLOB FFMPEG_DYNAMIC_LIBS \"${FFMPEG_DIR}/bin/*.dll\")\n\t\tif(NOT FFMPEG_DYNAMIC_LIBS)\n\t\t\tmessage(\"FFMPEG_DYNAMIC_LIBS is missing...\")\n\tendif()\n\tset(FFMPEG_DYNAMIC_LIBS ${FFMPEG_DYNAMIC_LIBS} CACHE PATH \"Windows dll location\")\nendif()\n\t\n\tmark_as_advanced(FFMPEG_INCLUDE_DIR FFMPEG_LIBRARY_DIRS FFMPEG_LIBRARIES FFMPEG_DYNAMIC_LIBS)\nELSE ()\n    MESSAGE(STATUS \"Could not find FFMPEG\")\nENDIF()\n \n \nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG\n\tREQUIRED_VARS FFMPEG_INCLUDE_DIR FFMPEG_LIBRARIES\n\tFAIL_MESSAGE \"FFmpeg wasn't found correctly. Set FFMPEG_DIR to the root SDK installation directory.\"\n)\n\nif(NOT FFMPEG_FOUND)\n\tset(FFMPEG_DIR \"\" CACHE STRING \"Path to FFmpeg install directory\")\nendif()\n  \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/Modules/FindGLFW.cmake",
    "content": "##=============================================================================\n##\n##  Copyright (c) Kitware, Inc.\n##  All rights reserved.\n##  See LICENSE.txt for details.\n##\n##  This software is distributed WITHOUT ANY WARRANTY; without even\n##  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n##  PURPOSE.  See the above copyright notice for more information.\n##\n##  Copyright 2016 Sandia Corporation.\n##  Copyright 2016 UT-Battelle, LLC.\n##  Copyright 2016 Los Alamos National Security.\n##\n##  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,\n##  the U.S. Government retains certain rights in this software.\n##  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National\n##  Laboratory (LANL), the U.S. Government retains certain rights in\n##  this software.\n##\n##=============================================================================\n# Try to find EGL library and include dir.\n# Once done this will define\n#\n# GLFW_FOUND\n# GLFW_INCLUDE_DIR\n# GLFW_LIBRARY\n#\n\ninclude(FindPackageHandleStandardArgs)\n\nif (WIN32)\n    find_path( GLFW_INCLUDE_DIR\n        NAMES\n            GLFW/glfw3.h\n        PATHS\n            ${PROJECT_SOURCE_DIR}/shared_external/glfw/include\n            ${PROJECT_SOURCE_DIR}/../shared_external/glfw/include\n            ${GLFW_LOCATION}/include\n            $ENV{GLFW_LOCATION}/include\n            $ENV{PROGRAMFILES}/GLFW/include\n            ${GLFW_LOCATION}\n            $ENV{GLFW_LOCATION}\n            DOC \"The directory where GLFW/glfw3.h resides\" )\n    if(ARCH STREQUAL \"x86\")\n      find_library( GLFW_LIBRARY\n          NAMES\n              glfw3\n          PATHS\n              ${GLFW_LOCATION}/lib\n              $ENV{GLFW_LOCATION}/lib\n              $ENV{PROGRAMFILES}/GLFW/lib\n              DOC \"The GLFW library\")\n    else()\n      find_library( GLFW_LIBRARY\n          NAMES\n              glfw3\n          PATHS\n              ${GLFW_LOCATION}/lib\n              $ENV{GLFW_LOCATION}/lib\n              $ENV{PROGRAMFILES}/GLFW/lib\n              DOC \"The GLFW library\")\n    endif()\nendif ()\n\nif (${CMAKE_HOST_UNIX})\n    message(\"GFLW LOCATION \" $ENV{GLFW_LOCATION} )\n    find_path( GLFW_INCLUDE_DIR\n        NAMES\n            GLFW/glfw3.h\n        PATHS\n#            ${GLFW_LOCATION}/include\n            $ENV{GLFW_LOCATION}/include\n#            /usr/include\n#            /usr/local/include\n#            /sw/include\n#            /opt/local/include\n#            NO_DEFAULT_PATH\n            DOC \"The directory where GLFW/glfw3.h resides\"\n    )\n    find_library( GLFW_LIBRARY\n        NAMES\n            glfw3 glfw\n        PATHS\n#            ${GLFW_LOCATION}/lib\n            $ENV{GLFW_LOCATION}/lib\n            $ENV{GLFW_LOCATION}/lib64\n#            /usr/lib64\n#            /usr/lib\n#            /usr/local/lib64\n#            /usr/local/lib\n#            /sw/lib\n#            /opt/local/lib\n#            /usr/lib/x86_64-linux-gnu\n#            NO_DEFAULT_PATH\n            DOC \"The GLFW library\")\n\n    set( GLFW_INCLUDE_DIR $ENV{GLFW_LOCATION}/include )\n    set( GLFW_LIBRARY $ENV{GLFW_LOCATION}/lib64/libglfw3.a )\n    message(\"*************==========> FindGLFW .cmake \" ${GLFW_INCLUDE_DIR} \" LIB \" ${GLFW_LIBRARY} )\nendif ()\n\nfind_package_handle_standard_args(GLFW DEFAULT_MSG\n    GLFW_INCLUDE_DIR\n    GLFW_LIBRARY\n)\n\nmark_as_advanced( GLFW_FOUND )\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/Win3rdParty.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## This file should be include and use only on WIN32 OS and once\n## It allow to auto check/download and use a preconfigured 3rdParty binaries for cmake usage\n## It use the downloadAndExtractZipFile cmake module to work.\n##\nif(__Win3rdParty_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__Win3rdParty_cmake_INCLUDED__ ON)\nendif()\n\n\n##\n## To be sure to reset an empty cached variable but keep any other kind of variables\n##\n## Usage:\n## check_cached_var(<var> <resetedCachedValue> <cacheType> <cacheDoc> [FORCE])\n##\n## <var> is the cached cmake variable you need to reset\n## <resetedCachedValue> is the new default value of the reseted cached cmake variable\n## <cacheType> is the kind of GUI cache input can be : FILEPATH; PATH; STRING or BOOL\n## <cacheDoc> is the associated GUI cache input documentation display in the GUI\n## FORCE option could be use to reset a cached variable even if it is not empty.\n##\nmacro(check_cached_var var resetedCachedValue cacheType cacheDoc)\n    # message(STATUS \"inside check_cached_var macro. argn=${ARGN}\")\n    cmake_parse_arguments(ccv \"FORCE\" \"\" \"\" ${ARGN})\n\n    if(ccv_FORCE)\n        set(FORCE FORCE)\n    else()\n        set(FORCE )\n    endif()\n\n    if(NOT ${var} OR ccv_FORCE)\n        unset(${var} CACHE)\n        # message(STATUS \"setting new cache value. var ${var} = ${resetedCachedValue}\")\n        set(${var}\t\"${resetedCachedValue}\" CACHE ${cacheType} \"${cacheDoc}\" ${FORCE})\n    endif()\nendmacro()\n\n\n##\n## Win3rdParty function allow to specify a directory which contain all necessary windows dependenties.\n## By uploading 3rdParty directory (which contain dependencies, *.lib, *.dll... for a specific version of compiler) onto Gforge file tab,\n## you get back an URL of download you can give to this function with a directory name. So you can provide multiple 3rdParty version of same dependencies (MSVC11, MSVC12...).\n## By providing a prefix to this function, you allow to use different kind of 3rdParty which can be handled by CMAKE OPTIONS depending on what your framework need for example.\n##\n## Usage 1:\n##    Win3rdParty(<prefix> MSVC<XX> <DirName> <URL>\n##\t\t\t\t\t[MSVC<XX> <DirName> <URL>] [...]\n##\t\t\t\t\t[VCID] [DEFAULT_USE] [VERBOSE] )\n##\n## * <prefix> allow to identify which 3rdParty you process (prefix name)\n## * MSVC<XX> flag could be MSVC11 or MSVC12 (any element of the MSVC_VERSIONS_LIST) and refer to a 3rdParty compiler with :\n##   * <DirName> which will be the local pathName of the downloaded 3rdParty : relative to CMAKE_BINARY_DIR\n##   * <URL> which is the link location of the 3rdParty zip\n## * VCID flag will make available a cache variable ${prefix}_WIN3RDPARTY_VCID\n## * DEFAULT_USE flag [ON|OFF] may be used to set default value of cmake cached variable : <prefix>_WIN3RDPARTY_USE [default to ON]\n##\n## WARNING:\n## This function define CACHE variables you can use after :\n## * ${prefix}_WIN3RDPARTY_USE : allow to check/downloaded win3rdParty dir (it will force the cached variables for this dependency folder generally <prefix>_DIR>)\n## * ${prefix}_WIN3RDPARTY_DIR : where is your local win3rdParty dir (the PATH)\n## * ${prefix}_WIN3RDPARTY_VCID : [if VCID flag is used] the MSVC id (commonly used to prefix/suffix library name, see boost or CGAL)\n##\n## If you want to add a win3rdParty version, please:\n## 1- build dependencies on your local side with the compiler you want\n## 2- build your own zip with your built dependencies\n## 3- upload it (onto the forge where the project is stored) and copy the link location in order to use it for this function\n## 4- if you just introduced a new MSVC version, add it to the MSVC_VERSIONS_LIST bellow\n##\n## In a second pass, you can also use this function to set necessary cmake cached variables in order to let cmake find packages of these 3rdParty.\n##\n## Usage 2:\n##    win3rdParty(<prefix> [VERBOSE] MULTI_SET|SET\n##          CHECK_CACHED_VAR <cmakeVar> <cmakeCacheType> [LIST] <cmakeValue> [DOC <stringToolTips>]\n##        [ CHECK_CACHED_VAR <cmakeVar> <cmakeCacheType> [LIST] <cmakeValue> [DOC <stringToolTips>] ] [...]\n##\n## * MULTI_SET or SET flags are used to tell cmake that all next arguments will use repeated flags with differents entries (SET mean we will provide only one set of arguments, without repetition)\n## * CHECK_CACHED_VAR are the repeated flag which contain differents entries\n##      * <cmakeVar> is the cmake variable you want to be cached for the project\n##      * <cmakeCacheType> is the kind of cmake variable (couble be: FILEPATH; PATH; STRING or BOOL) => see check_cached_var.\n##      * LIST optional flag could be used with CHECK_CACHED_VAR when <cmakeCacheType> = STRING. It allow to handle multiple STRINGS value list.\n##      * <cmakeValue> is the value of the variable (if FILEPATH, PATH or STRING: use quotes, if BOOL : use ON/OFF)\n##      * DOC optional flag is used to have a tooltips info about this new cmake variable entry into the GUI (use quotes).\n##\n## Full example 1 :\n##    win3rdParty(COMMON MSVC11 \"win3rdParty-MSVC11\" \"https://path.to/an.archive.7z\"\n##                SET CHECK_CACHED_VAR SuiteSparse_DIR PATH \"SuiteSparse-4.2.1\" DOC \"default empty doc\"\n##    )\n##\n## WARNING:\n## For the 2nd usage (with MULTI_SET), if you planned to set some CACHED_VAR using/composed by ${prefix}_WIN3RDPARTY_* just set in this macro (usage 1),\n## then (due to the not yet existing var) you will need to call this function 2 times :\n## One for the 1st usage (downloading of the current compiler 3rdParty).\n## One for the MLUTI_SET flag which will use existsing ${prefix}_WIN3RDPARTY_* cached var.\n##\n## Full example 2 :\n##    win3rdParty(COMMON MSVC11    \"win3rdParty-MSVC11\" \"https://path.to/an.archive.7z\")\n##    win3rdParty(COMMON MULTI_SET\n##       CHECK_CACHED_VAR CGAL_INCLUDE_DIR  PATH \"CGAL-4.3/include\" DOC \"default empty doc\"\n##       CHECK_CACHED_VAR CGAL_LIBRARIES\tSTRING LIST \"debug;CGAL-4.3/lib${LIB_POSTFIX}/CGAL-${WIN3RDPARTY_COMMON_VCID}-mt-gd-4.3.lib;optimized;CGAL-4.3/lib${LIB_POSTFIX}/CGAL-${WIN3RDPARTY_COMMON_VCID}-mt-4.3.lib\"\n##\n##\n## WARNING: This function use internaly :\n## * downloadAndExtractZipFile.cmake\n## * parse_arguments_multi.cmake\n## * check_cached_var macro\n##\nfunction(win3rdParty prefix )\n\n    # ARGV: list of all arguments given to the macro/function\n    # ARGN: list of remaining arguments\n\n    if(NOT WIN32)\n        return()\n    endif()\n\n    ## set the handled version of MSVC\n    ## if you plan to add a win3rdParty dir to download with a new MSVC version: build the win3rdParty dir and add the MSCV entry here.\n    set(MSVC_VERSIONS_LIST \"MSVC17;MSVC11;MSVC12;MSVC14\")\n\n    #include(CMakeParseArguments)   # CMakeParseArguments is obsolete since cmake 3.5\n    # cmake_parse_arguments (<prefix> <options> <one_value_keywords> <multi_value_keywords> args)\n    # <options> : options (flags) pass to the macro\n    # <one_value_keywords> : options that neeed a value\n    # <multi_value_keywords> : options that neeed more than one value\n    cmake_parse_arguments(w3p \"VCID\" \"VERBOSE;TIMEOUT;DEFAULT_USE\" \"${MSVC_VERSIONS_LIST};MULTI_SET;SET\" ${ARGN})\n\n    # message(STATUS \"value of w3p_VCID = ${w3p_VCID}\")\n    # message(STATUS \"value of w3p_VERBOSE = ${w3p_VERBOSE}\")\n    # message(STATUS \"value of w3p_TIMEOUT = ${w3p_TIMEOUT}\")\n    # message(STATUS \"value of w3p_DEFAULT_USE = ${w3p_DEFAULT_USE}\")\n\n    # foreach (loop_var ${MSVC_VERSIONS_LIST})\n            # message(STATUS \"value of w3p_${loop_var} = ${w3p_${loop_var}}\")\n    # endforeach(loop_var)\n\n    # message(STATUS \"value of w3p_MULTI_SET = ${w3p_MULTI_SET}\")\n    # message(STATUS \"value of w3p_SET = ${w3p_SET}\")\n\n    # message(\"values for MSVC = ${w3p_MSVC14}\")\n\n    if(NOT w3p_TIMEOUT)\n        set(w3p_TIMEOUT 300)\n    endif()\n\n    if(NOT DEFINED w3p_DEFAULT_USE)\n        set(w3p_DEFAULT_USE ON)\n    endif()\n\t\n\n    ## 1st use (check/update|download) :\n    set(${prefix}_WIN3RDPARTY_USE ${w3p_DEFAULT_USE} CACHE BOOL \"Use required 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist\")\n\n\n    ## We want to test if each version of MSVC was filled by the function (see associated parameters)\n    ## As CMake is running only for one version of MSVC, if that MSVC version was filled, we get back associated parameters,\n    ## otherwise we can't use the downloadAndExtractZipFile with win3rdParty.\n    set(enableWin3rdParty OFF)\n\t\n\tforeach(MSVC_VER ${MSVC_VERSIONS_LIST})\n\t\tif(${MSVC_VER} AND w3p_${MSVC_VER} OR ${MSVC_TOOLSET_VERSION} EQUAL 143 AND ${MSVC_VER} STREQUAL \"MSVC17\") \n\t\t\tlist(LENGTH w3p_${MSVC_VER} count)\n\t\t\tif(\"${count}\" LESS \"2\")\n\t\t\t\t#message(WARNING \"You are using ${MSVC_VER} with ${prefix}_WIN3RDPARTY_USE=${${prefix}_WIN3RDPARTY_USE}, but win3rdParty function isn't filled for ${MSVC_VER}!\")\n\t\t\telse()\n\t\t\t\tlist(GET w3p_${MSVC_VER} 0 Win3rdPartyName)\n\t\t\t\tlist(GET w3p_${MSVC_VER} 1 Win3rdPartyUrl)\n\t\t\t\tif(w3p_VCID)\n\t\t\t\t\t## try to get the VcId of MSVC. See also MSVC_VERSION cmake var in the doc.\n\t\t\t\t\tstring(REGEX REPLACE \"MS([A-Za-z_0-9-]+)\" \"\\\\1\" vcId ${MSVC_VER})\n\t\t\t\t\tstring(TOLOWER ${vcId} vcId)\n\t\t\t\t\tset(${prefix}_WIN3RDPARTY_VCID \"${vcId}0\" CACHE STRING \"the MSVC id (commonly used to prefix/suffix library name, see boost or CGAL)\")\n\t\t\t\t\tmark_as_advanced(${prefix}_WIN3RDPARTY_VCID)\n\t\t\t\tendif()\n\t\t\t\tset(enableWin3rdParty ON)\n\t\t\t\tset(suffixCompilerID ${MSVC_VER})\n\t\t\t\tbreak()\n\t\t\tendif()\n\t\tendif()\n\tendforeach()\n    ## If previous step succeed to get MSVC dirname and URL of the current MSVC version, use it to auto download/update the win3rdParty dir\n    if(enableWin3rdParty AND ${prefix}_WIN3RDPARTY_USE)\n\n        if(IS_ABSOLUTE \"${Win3rdPartyName}\")\n        else()\n            set(Win3rdPartyName \"${CMAKE_BINARY_DIR}/${Win3rdPartyName}\")\n        endif()\n\n        if(NOT EXISTS \"${Win3rdPartyName}\")\n            file(MAKE_DIRECTORY ${Win3rdPartyName})\n        endif()\n\n        include(downloadAndExtractZipFile)\n        downloadAndExtractZipFile(  \"${Win3rdPartyUrl}\"                             ## URL link location\n                                    \"Win3rdParty-${prefix}-${suffixCompilerID}.7z\"  ## where download it: relative path, so default to CMAKE_BINARY_DIR\n                                    \"${Win3rdPartyName}\"                            ## where extract it : fullPath (default relative to CMAKE_BINARY_DIR)\n            CHECK_DIRTY_URL \"${Win3rdPartyName}/Win3rdPartyUrl\"                     ## last downloaded url file : fullPath (default relative to CMAKE_BINARY_DIR)\n            TIMEOUT ${w3p_TIMEOUT}\n            VERBOSE ${w3p_VERBOSE}\n        )\n        file(GLOB checkDl \"${Win3rdPartyName}/*\")\n        list(LENGTH checkDl checkDlCount)\n        if(\"${checkDlCount}\" GREATER \"1\")\n        else()\n            message(\"The downloadAndExtractZipFile didn't work...?\")\n            set(enableWin3rdParty OFF)\n        endif()\n    endif()\n\n    ## Try to auto set ${prefix}_WIN3RDPARTY_DIR or let user set it manually\n    set(${prefix}_WIN3RDPARTY_DIR \"\" CACHE PATH \"windows ${Win3rdPartyName} dir to ${prefix} dependencies of the project\")\n\n    if(NOT ${prefix}_WIN3RDPARTY_DIR AND ${prefix}_WIN3RDPARTY_USE)\n        if(EXISTS \"${Win3rdPartyName}\")\n            unset(${prefix}_WIN3RDPARTY_DIR CACHE)\n            set(${prefix}_WIN3RDPARTY_DIR \"${Win3rdPartyName}\" CACHE PATH \"dir to ${prefix} dependencies of the project\")\n        endif()\n    endif()\n\n    if(EXISTS ${${prefix}_WIN3RDPARTY_DIR})\n        message(STATUS \"Found a 3rdParty ${prefix} dir : ${${prefix}_WIN3RDPARTY_DIR}.\")\n        set(enableWin3rdParty ON)\n    elseif(${prefix}_WIN3RDPARTY_USE)\n        message(WARNING \"${prefix}_WIN3RDPARTY_USE=${${prefix}_WIN3RDPARTY_USE} but ${prefix}_WIN3RDPARTY_DIR=${${prefix}_WIN3RDPARTY_DIR}.\")\n        set(enableWin3rdParty OFF)\n    endif()\n\n    ## Final check\n    if(NOT enableWin3rdParty)\n        message(\"Disable ${prefix}_WIN3RDPARTY_USE (cmake cached var will be not set), due to a win3rdParty problem.\")\n        message(\"You still can set ${prefix}_WIN3RDPARTY_DIR to an already downloaded Win3rdParty directory location.\")\n        set(${prefix}_WIN3RDPARTY_USE OFF CACHE BOOL \"Use required 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist\" FORCE)\n    endif()\n\n    ## 2nd use : handle multi values args to set cached cmake variables in order to ease the next find_package call\n    if(${prefix}_WIN3RDPARTY_USE AND ${prefix}_WIN3RDPARTY_DIR)\n        if(w3p_VERBOSE)\n            message(STATUS \"Try to set cmake cached variables for ${prefix} required libraries directly from : ${${prefix}_WIN3RDPARTY_DIR}.\")\n        endif()\n\n        include(parse_arguments_multi)\n        # message (STATUS \"before defining an override of parse_arguments_multi_function\")\n        function(parse_arguments_multi_function ) ## overloaded function to handle all CHECK_CACHED_VAR values list (see: parse_arguments_multi)\n            # message(STATUS \"inside overloaded parse_arguments_multi_function defined in Win3rdParty.cmake\")\n            # message(STATUS ${ARGN})\n            ## we know the function take 3 args : var cacheType resetedCachedValue (see check_cached_var)\n            cmake_parse_arguments(pamf \"\" \"DOC\" \"LIST\" ${ARGN})\n\n            ## var and cacheType are mandatory (with the resetedCachedValue)\n            set(var         ${ARGV0})\n            set(cacheType   ${ARGV1})\n            # message(STATUS \"var=${var} and cacheType=${cacheType} list=${pamf_LIST}\")\n            if(pamf_DOC)\n                set(cacheDoc    ${pamf_DOC})\n            else()\n                set(cacheDoc    \"\")\n            endif()\n            if(pamf_LIST)\n                set(value ${pamf_LIST})\n            else()\n                # message(\"USING ARGV2 with value ${ARGV2}\")\n                set(value ${ARGV2})\n            endif()\n            # message(\"inside override function in Win3rdparty.cmake value+ ${value}\")\n            if(\"${cacheType}\" MATCHES \"PATH\" AND EXISTS \"${${prefix}_WIN3RDPARTY_DIR}/${value}\")\n                # message(\"math with path\")\n                set(resetedCachedValue \"${${prefix}_WIN3RDPARTY_DIR}/${value}\") ## path relative to ${prefix}_WIN3RDPARTY_DIR\n            elseif (\"${cacheType}\" MATCHES \"PATH\" AND EXISTS \"${${prefix}_WIN3RDPARTY_DIR}\")\n                set(resetedCachedValue \"${${prefix}_WIN3RDPARTY_DIR}\") ## path relative to ${prefix}_WIN3RDPARTY_DIR\n            elseif(\"${cacheType}\" MATCHES \"STRING\")\n                foreach(var IN LISTS value)\n                    if(EXISTS \"${${prefix}_WIN3RDPARTY_DIR}/${var}\")\n                        list(APPEND resetedCachedValue \"${${prefix}_WIN3RDPARTY_DIR}/${var}\") ## string item of the string list is a path => make relative to ${prefix}_WIN3RDPARTY_DIR\n                    else()\n                        list(APPEND resetedCachedValue ${var}) ## string item of the string list is not an existing path => simply use the item\n                    endif()\n                endforeach()\n            else()\n                set(resetedCachedValue \"${value}\") ## could be a BOOL or a STRING\n            endif()\n\n            ## call our macro to reset cmake cache variable if empty\n            check_cached_var(${var} \"${resetedCachedValue}\" ${cacheType} \"${cacheDoc}\" FORCE)\n\n        endfunction()\n        # message (STATUS \"after defining an override of parse_arguments_multi_function\")\n\n        if(w3p_MULTI_SET)\n            parse_arguments_multi(CHECK_CACHED_VAR w3p_MULTI_SET ${w3p_MULTI_SET}) ## internaly will call our overloaded parse_arguments_multi_function\n        elseif(w3p_SET)\n            # message(\"calling set version of parse_arguments_multi with w3p_set = ${w3p_SET}\")\n            parse_arguments_multi(CHECK_CACHED_VAR w3p_SET ${w3p_SET})\n        endif()\n\n    endif()\n\nendfunction()\n\n## cmake variables introspection to globally activate/deactivate ${prefix}_WIN3RDPARTY_USE\n## This \"one shot\" call (only one for the next cmake configure) will automatically then reset the global variable WIN3RDPARTY_USE to UserDefined (do nothing).\n## use (call it) before and after the call of all your win3rdParty functions\nfunction(Win3rdPartyGlobalCacheAction )\n\tset(WIN3RDPARTY_USE \"UserDefined\" CACHE STRING \"Choose how to handle all cmake cached *_WIN3RDPARTY_USE for the next configure.\\nCould be:\\nUserDefined [default]\\nActivateAll\\nDesactivateAll\" )\n\tset_property(CACHE WIN3RDPARTY_USE PROPERTY STRINGS \"UserDefined;ActivateAll;DesactivateAll\" )\n\tif(${WIN3RDPARTY_USE} MATCHES \"UserDefined\")\n\telse()\n\t\tif(${WIN3RDPARTY_USE} MATCHES \"ActivateAll\")\n\t\t\tset(win3rdPvalue ON)\n\t\telseif(${WIN3RDPARTY_USE} MATCHES \"DesactivateAll\")\n\t\t\tset(win3rdPvalue OFF)\n\t\tendif()\n\t\tget_cmake_property(_variableNames CACHE_VARIABLES)\n\t\tforeach (_variableName ${_variableNames})\n\t\t\tstring(REGEX MATCH \t\"[A-Za-z_0-9-]+_WIN3RDPARTY_USE\" win3rdpartyUseCacheVar ${_variableName})\n\t\t\tif(win3rdpartyUseCacheVar)\n\t\t\t\tstring(REGEX REPLACE \"([A-Za-z_0-9-]+_WIN3RDPARTY_USE)\" \"\\\\1\" win3rdpartyUseCacheVar ${_variableName})\n\t\t\t\tset(${win3rdpartyUseCacheVar} ${win3rdPvalue} CACHE BOOL \"Use required 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist\" FORCE)\n\t\t\t\tmessage(STATUS \"${win3rdpartyUseCacheVar} cached variable set to ${win3rdPvalue}.\")\n\t\t\tendif()\n\t\tendforeach()\n\t\tset(WIN3RDPARTY_USE \"UserDefined\" CACHE STRING \"Choose how to handle all cmake cached *_WIN3RDPARTY_USE for the next configure.\\nCould be:\\nUserDefined [default]\\nActivateAll\\nDesactivateAll\" FORCE)\n\t\tmessage(STATUS \"reset WIN3RDPARTY_USE to UserDefined.\")\n\tendif()\n\tmark_as_advanced(WIN3RDPARTY_USE)\nendfunction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/cmake_policies.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(__set_policies_INCLUDED__)\n\treturn()\nelse()\n\tset(__set_policies_INCLUDED__ ON)\nendif()\n\nmacro(setPolicies)\n\t# No more policies to enforce\nendmacro()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/dependencies.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Included once for all sub project.\n## It contain the whole cmake instructions to find necessary common dependencies.\n## 3rdParty (provided by sibr_addlibrary win3rdParty or from external packages) are then available in cmake sub projects.\n##\n## Do not include this file more than once but you can modify it to fit to your own project.\n## So please, read it carefully because you can use on of these dependencies for your project or appen new one.\n##\n## As it is included after camke options, you can use conditional if(<CMAKE_PROJ_OPT>)/endif() to encapsulate your 3rdParty.\n##\n\n## win3rdParty function allowing to auto check/download/update binaries dependencies for current windows compiler\n## Please open this file in order to get more documentation and usage examples.\ninclude(Win3rdParty)\n\ninclude(sibr_library)\n\nWin3rdPartyGlobalCacheAction()\n\nfind_package(OpenGL REQUIRED)\n\nset(OpenGL_GL_PREFERENCE \"GLVND\")\n\n############\n## Find GLEW\n############\n##for headless rendering\nfind_package(EGL QUIET)\n\nif(EGL_FOUND)\n    add_definitions(-DGLEW_EGL)\n    message(\"Activating EGL support for headless GLFW/GLEW\")\nelse()\n    message(\"EGL not found : EGL support for headless GLFW/GLEW is disabled\")\nendif()\n\nif (MSVC11 OR MSVC12)\n    set(glew_multiset_arguments \n            CHECK_CACHED_VAR GLEW_INCLUDE_DIR\t    PATH \"glew-1.10.0/include\" DOC \"default empty doc\"\n            CHECK_CACHED_VAR GLEW_LIBRARIES         STRING LIST \"debug;glew-1.10.0/${LIB_BUILT_DIR}/glew32d.lib;optimized;glew-1.10.0/${LIB_BUILT_DIR}/glew32.lib\" DOC \"default empty doc\"\n        )\nelseif (MSVC14)\n    set(glew_multiset_arguments \n            CHECK_CACHED_VAR GLEW_INCLUDE_DIR\t    PATH \"glew-2.0.0/include\" DOC \"default empty doc\"\n            CHECK_CACHED_VAR GLEW_SHARED_LIBRARY_RELEASE       PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32.lib\"\n            CHECK_CACHED_VAR GLEW_STATIC_LIBRARY_RELEASE       PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32s.lib\"\n            CHECK_CACHED_VAR GLEW_SHARED_LIBRARY_DEBUG         PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32d.lib\"\n            CHECK_CACHED_VAR GLEW_STATIC_LIBRARY_DEBUG         PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32sd.lib\"\n        )\nelse ()\n    message(\"There is no provided GLEW library for your compiler, relying on find_package to find it\")\nendif()\nsibr_addlibrary(NAME GLEW #VERBOSE ON\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/glew-1.10.0.7z\"\n    MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/glew-1.10.0.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/glew-2.0.0.7z\"        # using recompiled version of glew\n    MULTI_SET ${glew_multiset_arguments}\n)\nset(GLEW_VERBOSE ON)\nFIND_PACKAGE(GLEW REQUIRED)\nIF(GLEW_FOUND)\n    INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIR})\nELSE(GLEW_FOUND)\n    MESSAGE(\"GLEW not found. Set GLEW_DIR to base directory of GLEW.\")\nENDIF(GLEW_FOUND)\n\n\n##############\n## Find ASSIMP\n##############\nif (MSVC11 OR MSVC12)\n    set(assimp_set_arguments \n        CHECK_CACHED_VAR ASSIMP_DIR PATH \"Assimp_3.1_fix\"\n    )\nelseif (MSVC14)\n    set(assimp_set_arguments \n        CHECK_CACHED_VAR ASSIMP_DIR PATH \"Assimp-4.1.0\"\n    )\nelse ()\n    message(\"There is no provided ASSIMP library for your compiler, relying on find_package to find it\")\nendif()\n\nsibr_addlibrary(NAME ASSIMP #VERBOSE ON\n        MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/Assimp_3.1_fix.7z\"\n        MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/Assimp_3.1_fix.7z\"\n        MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/Assimp-4.1.0.7z\"\n        MULTI_SET\n            ${assimp_set_arguments}\n)\n\nfind_package(ASSIMP REQUIRED)\ninclude_directories(${ASSIMP_INCLUDE_DIR})\n\n################\n## Find FFMPEG\n################\nsibr_addlibrary(NAME FFMPEG\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/ffmpeg.zip\"\n    MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/ffmpeg.zip\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/ffmpeg-4.0.2-win64-win3rdParty.7z\"\n    SET CHECK_CACHED_VAR FFMPEG_DIR PATH ${FFMPEG_WIN3RDPARTY_DIR}\n)\nfind_package(FFMPEG)\ninclude_directories(${FFMPEG_INCLUDE_DIR})\n## COMMENT OUT ALL FFMPEG FOR CLUSTER\n\n###################\n## Find embree3\n###################\nsibr_addlibrary(\n    NAME embree3\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/embree2.7.0.x64.windows.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/embree-3.6.1.x64.vc14.windows.7z\"     # TODO SV: provide a valid version if required\n)\n\n# CLUSTER\n#find_package(embree 3.0 REQUIRED PATHS \"/data/graphdeco/share/embree/usr/local/lib64/cmake/\" )\nfind_package(embree 3.0 )\n\n###################\n## Find eigen3\n###################\nsibr_addlibrary(\n\tNAME eigen3\n\t#MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/eigen-eigen-dc6cfdf9bcec.7z\"\n\t#MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/eigen-eigen-dc6cfdf9bcec.7z\"    # TODO SV: provide a valid version if required\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/eigen3.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/eigen3.7z\"\n    SET CHECK_CACHED_VAR eigen3_DIR PATH \"eigen/share/eigen3/cmake\"\n)\ninclude_directories(/usr/include/eigen3)\nadd_definitions(-DEIGEN_INITIALIZE_MATRICES_BY_ZERO)\n\n#############\n## Find Boost\n#############\nset(Boost_REQUIRED_COMPONENTS \"system;chrono;filesystem;date_time\" CACHE INTERNAL \"Boost Required Components\")\n\nif (WIN32)\n    # boost multiset arguments\n    if (MSVC11 OR MSVC12)\n        set(boost_multiset_arguments \n                CHECK_CACHED_VAR BOOST_ROOT                 PATH \"boost_1_55_0\"\n                CHECK_CACHED_VAR BOOST_INCLUDEDIR \t\t    PATH \"boost_1_55_0\"\n                CHECK_CACHED_VAR BOOST_LIBRARYDIR \t\t    PATH \"boost_1_55_0/${LIB_BUILT_DIR}\"\n                #CHECK_CACHED_VAR Boost_COMPILER             STRING \"-${Boost_WIN3RDPARTY_VCID}\" DOC \"vcid (eg: -vc110 for MSVC11)\"\n                CHECK_CACHED_VAR Boost_COMPILER             STRING \"-vc110\" DOC \"vcid (eg: -vc110 for MSVC11)\" # NOTE: if it doesnt work, uncomment this option and set the right value for VisualC id\n            )\n    elseif (MSVC14)\n        set(boost_multiset_arguments \n                CHECK_CACHED_VAR BOOST_ROOT                 PATH \"boost-1.71\"\n                CHECK_CACHED_VAR BOOST_INCLUDEDIR \t\t    PATH \"boost-1.71\"\n                CHECK_CACHED_VAR BOOST_LIBRARYDIR \t\t    PATH \"boost-1.71/${LIB_BUILT_DIR}\"\n                CHECK_CACHED_VAR Boost_COMPILER             STRING \"-vc141\" DOC \"vcid (eg: -vc110 for MSVC11)\" # NOTE: if it doesnt work, uncomment this option and set the right value for VisualC id\n            )\n        \n        option(BOOST_MINIMAL_VERSION \"Only get minimal Boost dependencies\" ON)\n\n        if(${BOOST_MINIMAL_VERSION})\n            set(BOOST_MSVC14_ZIP \"boost-1.71-ibr-minimal.7z\")\n        else()\n            set(BOOST_MSVC14_ZIP \"boost-1.71.7z\")\n        endif()\n    else ()\n        message(\"There is no provided Boost library for your compiler, relying on find_package to find it\")\n    endif()\n\n    sibr_addlibrary(NAME Boost VCID TIMEOUT 600 #VERBOSE ON\n        MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/boost_1_55_0.7z\"\n        MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/boost_1_55_0.7z\"\n        MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/${BOOST_MSVC14_ZIP}\"    # boost compatible with msvc14\n        MULTI_SET ${boost_multiset_arguments}\n            CHECK_CACHED_VAR Boost_NO_SYSTEM_PATHS      BOOL ON DOC \"Set to ON to disable searching in locations not specified by these boost cached hint variables\"\n            CHECK_CACHED_VAR Boost_NO_BOOST_CMAKE       BOOL ON DOC \"Set to ON to disable the search for boost-cmake (package cmake config file if boost was built with cmake)\"\n    )\n    if(NOT Boost_COMPILER AND Boost_WIN3RDPARTY_USE)\n        message(WARNING \"Boost_COMPILER is not set and it's needed.\")\n    endif()\nendif()\n\nfind_package(Boost 1.65.0 REQUIRED COMPONENTS ${Boost_REQUIRED_COMPONENTS})\n# for CLUSTER\n##find_package(Boost 1.58.0 REQUIRED COMPONENTS ${Boost_REQUIRED_COMPONENTS})\n\n\nif(WIN32)\n\tadd_compile_options(\"$<$<COMPILE_LANGUAGE:CXX>:/EHsc>\")\n    #add_definitions(/EHsc)\nendif()\n\nif(Boost_LIB_DIAGNOSTIC_DEFINITIONS)\n    add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS})\nendif()\n\n#if(WIN32)\n    add_definitions(-DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB)\n#endif()\n\ninclude_directories(${BOOST_INCLUDEDIR} ${Boost_INCLUDE_DIRS})\nlink_directories(${BOOST_LIBRARYDIR} ${Boost_LIBRARY_DIRS})\n\n\n##############\n## Find OpenMP\n##############\nfind_package(OpenMP)\n\n##############\n## Find OpenCV\n##############\nif (WIN32)\n\tif (${MSVC_TOOLSET_VERSION} EQUAL 143)\n\t\tMESSAGE(\"SPECIAL OPENCV HANDLING\")\n\t\tset(opencv_set_arguments \n\t\tCHECK_CACHED_VAR OpenCV_DIR PATH \"install\" ## see OpenCVConfig.cmake\n\t    )\n\telseif (MSVC11 OR MSVC12)\n\t    set(opencv_set_arguments \n\t\tCHECK_CACHED_VAR OpenCV_DIR PATH \"opencv/build\" ## see OpenCVConfig.cmake\n\t    )\n\telseif (MSVC14)\n\t    set(opencv_set_arguments \n\t\tCHECK_CACHED_VAR OpenCV_DIR PATH \"opencv-4.5.0/build\" ## see OpenCVConfig.cmake\n\t    )\n\telse ()\n\t    message(\"There is no provided OpenCV library for your compiler, relying on find_package to find it\")\n\tendif()\nelse()\n\t    message(\"There is no provided OpenCV library for your compiler, relying on find_package to find it\")\nendif()\n\nsibr_addlibrary(NAME OpenCV #VERBOSE ON\n        MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv.7z\"\n        MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv.7z\"\n        MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv-4.5.0.7z\"    # opencv compatible with msvc14 and with contribs\n        MSVC17 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv4-8.7z\" \n\t\tSET ${opencv_set_arguments}\n    )\nfind_package(OpenCV 4.5 REQUIRED) ## Use directly the OpenCVConfig.cmake provided\n## FOR CLUSTER\n###find_package(OpenCV 4.5 REQUIRED PATHS \"/data/graphdeco/share/opencv/usr/local/lib64/cmake/opencv4/\" ) ## Use directly the OpenCVConfig.cmake provided\n\n    ##https://stackoverflow.com/questions/24262081/cmake-relwithdebinfo-links-to-debug-libs\nset_target_properties(${OpenCV_LIBS} PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE)\n\nadd_definitions(-DOPENCV_TRAITS_ENABLE_DEPRECATED) \n\nif(OpenCV_INCLUDE_DIRS)\n    foreach(inc ${OpenCV_INCLUDE_DIRS})\n        if(NOT EXISTS ${inc})\n            set(OpenCV_INCLUDE_DIR \"\" CACHE PATH \"additional custom include DIR (in case of trouble to find it (fedora 17 opencv package))\")\n        endif()\n    endforeach()\n    if(OpenCV_INCLUDE_DIR)\n        list(APPEND OpenCV_INCLUDE_DIRS ${OpenCV_INCLUDE_DIR})\n        include_directories(${OpenCV_INCLUDE_DIRS})\n    endif()\nendif()\n\n###################\n## Find GLFW\n###################\nsibr_addlibrary(\n    NAME glfw3\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/glfw-3.2.1.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/glfw-3.2.1.7z\"     # TODO SV: provide a valid version if required\n    SET CHECK_CACHED_VAR glfw3_DIR PATH \"glfw-3.2.1\"\n)\n\n### FOR CLUSTER COMMENT OUT lines above, uncomment lines below\n##find_package(GLFW REQUIRED 3.3 )\n##message(\"***********=============> GLFW IS \" ${GLFW_LIBRARY})\n##message(\"***********=============> GLFW IS \" ${GLFW_LIBRARIES})\n\nfind_package(glfw3 REQUIRED)\n\nsibr_gitlibrary(TARGET imgui\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/imgui.git\"\n    GIT_TAG\t\t\t\"741fb3ab6c7e1f7cef23ad0501a06b7c2b354944\"\n)\n\n## FOR CLUSTER COMMENT OUT nativefiledialog\nsibr_gitlibrary(TARGET nativefiledialog\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/nativefiledialog.git\"\n    GIT_TAG\t\t\t\"ae2fab73cf44bebdc08d997e307c8df30bb9acec\"\n)\n\n\nsibr_gitlibrary(TARGET mrf\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/mrf.git\"\n    GIT_TAG\t\t\t\"30c3c9494a00b6346d72a9e37761824c6f2b7207\"\n)\n\nsibr_gitlibrary(TARGET nanoflann\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/nanoflann.git\"\n    GIT_TAG\t\t\t\"7a20a9ac0a1d34850fc3a9e398fc4a7618e8a69a\"\n)\n\nsibr_gitlibrary(TARGET picojson\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/picojson.git\"\n    GIT_TAG\t\t\t\"7cf8feee93c8383dddbcb6b64cf40b04e007c49f\"\n)\n\nsibr_gitlibrary(TARGET rapidxml\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/rapidxml.git\"\n    GIT_TAG\t\t\t\"069e87f5ec5ce1745253bd64d89644d6b894e516\"\n)\n\nsibr_gitlibrary(TARGET xatlas\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/xatlas.git\"\n    GIT_TAG\t\t\t\"0fbe06a5368da13fcdc3ee48d4bdb2919ed2a249\"\n    INCLUDE_DIRS \t\"source/xatlas\"\n)\n\nWin3rdPartyGlobalCacheAction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/downloadAndExtractZipFile.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## downloadAndExtractZipFile cmake function\n## Provide a way to download zip file from public internet ZIP_URL host\n## and to extract it in a specific EXCTRATED_ZIP_PATH destination.\n## This function use 7-Zip external tool to maximize the compatibles formats.\n## This will be not download again if the EXCTRATED_ZIP_PATH already exist and DL_FORCE is set to OFF.\n## This will try to unzip file if already exist in the ZIP_DL_PATH.\n##\n## If EXCTRATED_ZIP_PATH and/or ZIP_DL_PATH are not full path,\n## it will be interpreted relative to CMAKE_BINARY_DIR\n##\n## Usage example :\n## include(downloadAndExtractZipFile)\n## downloadAndExtractZipFile(\n## \thttp://www.cs.cornell.edu/~snavely/bundler/distr/bundler-v0.4-source.zip\n## \t${CMAKE_BINARY_DIR}/Bundler/bundler-v0.4-source.zip\n## \t${CMAKE_BINARY_DIR}/Bundler\n##  [DL_FORCE ON|OFF]\n##  [TIMEOUT]\n##  [CHECK_DIRTY_URL]\n## )\n##\n## option DL_FORCE will redownload the zip file [deafult to OFF]\n## option TIMEOUT will end the unzip process after this period of time [default to 600s]\n## option CHECK_DIRTY_URL will write into the given file the downloaded URL and then,\n## next time, if the URL was updated, it detect it with this file\n## and will download the last version. This prevent to alway set manually DL_FORCE to ON...\n##\nif(__downloadAndExtractZipFile_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__downloadAndExtractZipFile_cmake_INCLUDED__ ON)\nendif()\n\nfunction(downloadAndExtractZipFile ZIP_URL ZIP_DL_PATH EXCTRATED_ZIP_PATH)\n\n    # message(STATUS \"zipUrl=${ZIP_URL} zipDlPath=${ZIP_DL_PATH} extractedZipPath=${EXCTRATED_ZIP_PATH}\")\n    cmake_parse_arguments(dwnlezf \"\" \"VERBOSE;DL_FORCE;TIMEOUT;CHECK_DIRTY_URL\" \"\" ${ARGN})\n\n\tset(PROGRAMFILESx86 \"PROGRAMFILES(x86)\")\n\n    ## Check entries mandatory args\n    if(IS_ABSOLUTE \"${ZIP_DL_PATH}\")\n    else()\n        set(ZIP_DL_PATH \"${CMAKE_BINARY_DIR}/${ZIP_DL_PATH}\")\n    endif()\n    if(IS_ABSOLUTE \"${EXCTRATED_ZIP_PATH}\")\n    else()\n        set(EXCTRATED_ZIP_PATH \"${CMAKE_BINARY_DIR}/${EXCTRATED_ZIP_PATH}\")\n    endif()\n    if(NOT EXISTS \"${EXCTRATED_ZIP_PATH}\")\n        file(MAKE_DIRECTORY ${EXCTRATED_ZIP_PATH})\n    endif()\n\n\t# SB: Once, one of downloaded zip was corrupted by an error message coming from the server.\n\tif(EXISTS \"${ZIP_DL_PATH}\")\n\t\t# So I check for removing such corrupted files\n\t\tmessage(\"Removing previous ${ZIP_DL_PATH} (might be corrupted)\")\n\t\tfile(REMOVE \"${ZIP_DL_PATH}\")\n\t\tif(EXISTS \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\t# and remove the previous (corrupted) made 'Win3rdPartyUrl' file\n\t\t\tfile(REMOVE \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\tendif()\n\tendif()\n\n    ## Check entries optional args\n\tmacro(readDirtyUrl )\n\t\tif(dwnlezf_CHECK_DIRTY_URL)\n\t\t\tif(IS_ABSOLUTE \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\telse()\n\t\t\t\tset(dwnlezf_CHECK_DIRTY_URL \"${CMAKE_BINARY_DIR}/${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\tendif()\n\t\t\tget_filename_component(unzipDir \t${EXCTRATED_ZIP_PATH} NAME)\n\t\t\tget_filename_component(unzipPath \t${EXCTRATED_ZIP_PATH} PATH)\n\t\t\tmessage(STATUS \"Checking ${unzipDir} [from ${unzipPath}]...\")\n\t\t\tif(EXISTS \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\t\tget_filename_component(CHECK_DIRTY_URL_FILENAME ${dwnlezf_CHECK_DIRTY_URL} NAME)\n\t\t\t\tfile(STRINGS \"${dwnlezf_CHECK_DIRTY_URL}\" contents)\n\t\t\t\tlist(GET contents 0 downloadURL)\n\t\t\t\tlist(REMOVE_AT contents 0)\n\t\t\t\tif(\"${downloadURL}\" MATCHES \"${ZIP_URL}\")\n\t\t\t\t\tif(dwnlezf_VERBOSE)\n\t\t\t\t\t\tmessage(STATUS \"Your downloaded version (URL) seems to be up to date. Let me check if nothing is missing... (see ${dwnlezf_CHECK_DIRTY_URL}).\")\n\t\t\t\t\tendif()\n\t\t\t\t\tfile(GLOB PATHNAME_PATTERN_LIST \"${EXCTRATED_ZIP_PATH}/*\") ## is there something inside the downloaded destination ?\n\t\t\t\t\tunset(NAME_PATTERN_LIST)\n\t\t\t\t\tforeach(realPathPattern ${PATHNAME_PATTERN_LIST})\n\t\t\t\t\t\tget_filename_component(itemName ${realPathPattern} NAME)\n\t\t\t\t\t\tlist(APPEND NAME_PATTERN_LIST ${itemName})\n\t\t\t\t\tendforeach()\n\t\t\t\t\tif(NAME_PATTERN_LIST)\n\t\t\t\t\t\tforeach(item ${contents})\n\t\t\t\t\t\t\tlist(FIND NAME_PATTERN_LIST ${item} id)\n\t\t\t\t\t\t\tif(${id} MATCHES \"-1\")\n\t\t\t\t\t\t\t\tmessage(STATUS \"${item} is missing, your downloaded version content changed, need to redownload it.\")\n\t\t\t\t\t\t\t\tset(ZIP_DL_FORCE ON)\n\t\t\t\t\t\t\t\tbreak()\n\t\t\t\t\t\t\telse()\n\t\t\t\t\t\t\t\tlist(REMOVE_AT NAME_PATTERN_LIST ${id})\n\t\t\t\t\t\t\t\tset(ZIP_DL_FORCE OFF)\n\t\t\t\t\t\t\tendif()\n\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\tif(NOT ZIP_DL_FORCE AND NAME_PATTERN_LIST)\n\t\t\t\t\t\t\tmessage(\"Yours seems to be up to date (regarding to ${CHECK_DIRTY_URL_FILENAME})!\\nBut there are additional files/folders into your downloaded destination (feel free to clean it if you want).\")\n\t\t\t\t\t\t\tforeach(item ${NAME_PATTERN_LIST})\n\t\t\t\t\t\t\t\tif(item)\n\t\t\t\t\t\t\t\t\tmessage(\"${item}\")\n\t\t\t\t\t\t\t\tendif()\n\t\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\tendif()\n\t\t\t\t\tendif()\n\t\t\t\telse()\n\t\t\t\t\tset(ZIP_DL_FORCE ON)\n\t\t\t\t\tmessage(STATUS \"Your downloaded version is dirty (too old).\")\n\t\t\t\tendif()\n\t\t\telse()\n\t\t\t\tfile(GLOB PATHNAME_PATTERN_LIST \"${EXCTRATED_ZIP_PATH}/*\") ## is there something inside the downloaded destination ?\n\t\t\t\tif(NOT PATHNAME_PATTERN_LIST)\n\t\t\t\t\tmessage(\"We found nothing into ${EXCTRATED_ZIP_PATH}, we will try to download it for you now.\")\n\t\t\t\tendif()\n\t\t\t\tset(ZIP_DL_FORCE ON)\n\t\t\tendif()\n\t\tendif()\n\tendmacro()\n\treadDirtyUrl()\n\tif(NOT ZIP_DL_FORCE)\n\t\treturn() ## do not need to further (as we are up to date, just exit the function\n\tendif()\n\n\tmacro(writeDirtyUrl )\n\t\tif(dwnlezf_CHECK_DIRTY_URL)\n\t\t\tfile(WRITE \"${dwnlezf_CHECK_DIRTY_URL}\" \"${ZIP_URL}\\n\")\n\t\t\tfile(GLOB PATHNAME_PATTERN_LIST \"${EXCTRATED_ZIP_PATH}/*\") ## is there something inside the downloaded destination ?\n\t\t\tunset(NAME_PATTERN_LIST)\n\t\t\tforeach(realPathPattern ${PATHNAME_PATTERN_LIST})\n\t\t\t\tget_filename_component(itemName ${realPathPattern} NAME)\n\t\t\t\tlist(APPEND NAME_PATTERN_LIST ${itemName})\n\t\t\tendforeach()\n\t\t\tif(NAME_PATTERN_LIST)\n\t\t\t\tforeach(item ${NAME_PATTERN_LIST})\n\t\t\t\t\tfile(APPEND \"${dwnlezf_CHECK_DIRTY_URL}\" \"${item}\\n\")\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\tendif()\n\tendmacro()\n\n\tif(dwnlezf_DL_FORCE)\n        set(ZIP_DL_FORCE ON)\n    endif()\n\n\tif(NOT dwnlezf_TIMEOUT)\n\t\tset(dwnlezf_TIMEOUT 600)\n\tendif()\n\tmath(EXPR dwnlezf_TIMEOUT_MIN \"${dwnlezf_TIMEOUT}/60\")\n\n\tmacro(unzip whichZipFile)\n\t\tif(NOT SEVEN_ZIP_CMD)\n\t\t\tfind_program(SEVEN_ZIP_CMD NAMES 7z 7za p7zip DOC \"7-zip executable\" PATHS \"$ENV{PROGRAMFILES}/7-Zip\" \"$ENV{${PROGRAMFILESx86}}/7-Zip\" \"$ENV{ProgramW6432}/7-Zip\")\n\t\tendif()\n\t\tif(SEVEN_ZIP_CMD)\n            if(dwnlezf_VERBOSE)\n                message(STATUS \"UNZIP: please, WAIT UNTIL ${SEVEN_ZIP_CMD} finished...\\n(no more than ${dwnlezf_TIMEOUT_MIN} min)\")\n            else()\n                message(STATUS \"UNZIP...wait...\")\n            endif()\n\t\t\texecute_process( \tCOMMAND ${SEVEN_ZIP_CMD} x ${whichZipFile} -y\n\t\t\t\t\t\t\t\tWORKING_DIRECTORY ${EXCTRATED_ZIP_PATH}\tTIMEOUT ${dwnlezf_TIMEOUT}\n\t\t\t\t\t\t\t\tRESULT_VARIABLE resVar OUTPUT_VARIABLE outVar ERROR_VARIABLE errVar\n\t\t\t\t\t\t\t)\n\t\t\tif(${resVar} MATCHES \"0\")\n                if(dwnlezf_VERBOSE)\n                    message(STATUS \"SUCESS to unzip in ${EXCTRATED_ZIP_PATH}. Now we can remove the downloaded zip file.\")\n                endif()\n\t\t\t\texecute_process(COMMAND ${CMAKE_COMMAND} -E remove ${whichZipFile})\n\t\t\t\tmark_as_advanced(SEVEN_ZIP_CMD)\n\t\t\telse()\n\t\t\t\tmessage(WARNING \"something wrong in ${EXCTRATED_ZIP_PATH}\\n with \\\"${SEVEN_ZIP_CMD} x ${whichZipFile} -y\\\", redo or try to unzip by yourself...\")\n\t\t\t\tmessage(\"unzip: resVar=${resVar}\")\n\t\t\t\tmessage(\"unzip: outVar=${outVar}\")\n\t\t\t\tmessage(\"unzip: errVar=${errVar}\")\n\t\t\t\tmessage(\"unzip: failed or canceled or timeout\")\n\t\t\tendif()\n\t\telse()\n\t\t\tmessage(WARNING \"You need 7zip (http://www.7-zip.org/download.html) to unzip the downloaded dir.\")\n\t\t\tset(SEVEN_ZIP_CMD \"\" CACHE FILEPATH \"7-zip executable\")\n\t\t\tmark_as_advanced(CLEAR SEVEN_ZIP_CMD)\n\t\tendif()\n\tendmacro()\n\n    if(dwnlezf_VERBOSE)\n        message(STATUS \"Trying to look ${ZIP_DL_PATH} if a zip file exist...\")\n    endif()\n\tif(EXISTS \"${ZIP_DL_PATH}\")\n\n\t\t## already downloaded, so just unzip it\n\t\tunzip(${ZIP_DL_PATH})\n\t\twriteDirtyUrl()\n\n\telseif(ZIP_DL_FORCE)\n\n\t\t## the download part (+ unzip)\n\t\tmessage(STATUS \"Let me try to download package for you : ${ZIP_URL}\")\n        if(dwnlezf_VERBOSE)\n            message(STATUS \"Downloading...\\n   SRC=${ZIP_URL}\\n   DEST=${ZIP_DL_PATH}.tmp\\n   INACTIVITY_TIMEOUT=180s\")\n        endif()\n\t\tfile(DOWNLOAD ${ZIP_URL} ${ZIP_DL_PATH}.tmp INACTIVITY_TIMEOUT 360 STATUS status SHOW_PROGRESS)\n\n\t\tlist(GET status 0 numResult)\n\t\tif(${numResult} MATCHES \"0\")\n\n            if(dwnlezf_VERBOSE)\n                message(STATUS \"Download succeed, so let me rename the tmp file to unzip it\")\n            endif()\n\t\t\texecute_process(COMMAND ${CMAKE_COMMAND} -E rename ${ZIP_DL_PATH}.tmp ${ZIP_DL_PATH})\n\t\t\tunzip(${ZIP_DL_PATH})\n\t\t\twriteDirtyUrl()\n\n\t\telse()\n\n\t\t\tlist(GET status 1 errMsg)\n\t\t\tmessage(WARNING \"DOWNLOAD ${ZIP_URL} to ${ZIP_DL_PATH} failed\\n:${errMsg}\")\n\t\t\tmessage(WARNING \"OK, you need to download the ${ZIP_URL} manually and put it into ${ZIP_DL_PATH}\")\n\t\t\tmessage(\"Take a look at the project website page to check available URL.\")\n\n\t\tendif()\n\n\tendif()\n\n\t## clean up the tmp downloaded file\n\tif(EXISTS \"${ZIP_DL_PATH}.tmp\")\n\t\texecute_process(COMMAND ${CMAKE_COMMAND} -E remove ${ZIP_DL_PATH}.tmp)\n\tendif()\n\nendfunction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/git_describe.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(__git_describe_INCLUDED__)\n\treturn()\nelse()\n\tset(__git_describe_INCLUDED__ ON)\nendif()\n\nfind_package(Git)\nif(Git_FOUND)\n  message(STATUS \"Git found: ${GIT_EXECUTABLE}\")\nelse()\n  message(FATAL_ERROR \"Git not found. Aborting\")\nendif()\n\nmacro(git_describe)\n    cmake_parse_arguments(GIT_DESCRIBE \"\" \"GIT_URL;GIT_BRANCH;GIT_COMMIT_HASH;GIT_TAG;GIT_VERSION;PATH\" \"\" ${ARGN})\n\n    if(NOT GIT_DESCRIBE_PATH)\n        set(GIT_DESCRIBE_PATH ${CMAKE_SOURCE_DIR})\n    endif()\n\n    if(GIT_DESCRIBE_GIT_URL)\n        # Get the current remote\n        execute_process(\n            COMMAND git remote\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     GIT_DESCRIBE_GIT_REMOTE\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n\n        # Get the current remote\n        execute_process(\n            COMMAND git remote get-url ${GIT_DESCRIBE_GIT_REMOTE}\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_URL}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_BRANCH)\n        # Get the current working branch\n        execute_process(\n            COMMAND git rev-parse --abbrev-ref HEAD\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_BRANCH}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_COMMIT_HASH)\n        # Get the latest abbreviated commit hash of the working branch\n        execute_process(\n            COMMAND git rev-parse HEAD\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_COMMIT_HASH}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_TAG)\n        # Get the tag\n        execute_process(\n            COMMAND git describe --tags --exact-match\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_TAG}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_VERSION)\n        # Get the version from git describe\n        execute_process(\n            COMMAND git describe\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_VERSION}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n\n        if(${GIT_DESCRIBE_GIT_VERSION} STREQUAL \"\")\n            execute_process(\n                COMMAND git rev-parse --abbrev-ref HEAD\n                WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n                OUTPUT_VARIABLE     GIT_DESCRIBE_GIT_VERSION_BRANCH\n                OUTPUT_STRIP_TRAILING_WHITESPACE\n                ERROR_QUIET\n            )\n            execute_process(\n                COMMAND git log -1 --format=%h\n                WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n                OUTPUT_VARIABLE     GIT_DESCRIBE_GIT_VERSION_COMMIT\n                OUTPUT_STRIP_TRAILING_WHITESPACE\n                ERROR_QUIET\n            )\n\n            set(${GIT_DESCRIBE_GIT_VERSION} \"${GIT_DESCRIBE_GIT_VERSION_BRANCH}-${GIT_DESCRIBE_GIT_VERSION_COMMIT}\")\n        endif()\n    endif()\n\nendmacro()"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/include_once.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nmacro(include_once file)\n    get_filename_component(INCLUDE_ONCE_FILEPATH ${file} REALPATH)\n    string(REGEX REPLACE \"(\\\\.|\\\\/+|\\\\:|\\\\\\\\+)\" \"_\" INCLUDE_ONCE_FILEPATH ${INCLUDE_ONCE_FILEPATH})\n    get_property(INCLUDED_${INCLUDE_ONCE_FILEPATH}_LOCAL GLOBAL PROPERTY INCLUDED_${INCLUDE_ONCE_FILEPATH})\n    if (INCLUDED_${INCLUDE_ONCE_FILEPATH}_LOCAL)\n        return()\n    else()\n        set_property(GLOBAL PROPERTY INCLUDED_${INCLUDE_ONCE_FILEPATH} true)\n\n        include(${file})\n    endif()\nendmacro()"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/install_runtime.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## This file is mainly used to allow runtime installation\n## There are some utilities cmake functions to ease the generic deployement (abstract common usage of cmake)...\n##\n## You cannot run your programm automaticaly from your CNAKE_BINARY_DIR when you build\n## as it will miss all dependencies and ressources files...\n## You have to run install target in order to test your programm.\n##\n## The only one function/macros you may use inside your sub-CMakeLists.txt (sub-project) is :\n## ******************\n## ibr_install_target macro => see documentation at the end of this file\n## ******************\n## It use these utilities cmake functions to abstract the installation in an uniform way for all sub-projects.\n##\nif(__install_runtime_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__install_runtime_cmake_INCLUDED__ ON)\nendif()\n\n\n##\n## Allow to write a resource config file which contain additional ressource paths\n## (used by IBR_Common Resource system to load shaders and potentialy images, plugins and so on)\n##\n## ADD option list all the paths to add in the file (relative paths are interpreted relative to working dir of the executable)\n## INSTALL option to specify where we want to install this file\n##\n## Example usage:\n## resourceFile(ADD \"shaders\" \"${PROJECT_NAME}_rsc\" INSTALL bin)\n##\nmacro(resourceFile)\n\tcmake_parse_arguments(rsc \"\" \"INSTALL;FILE_PATH;CONFIG_TYPE\" \"ADD\" ${ARGN}) ## both args are directory path\n\n\tif(rsc_ADD)\n\t\tunset(IBR_RSC_FILE_CONTENT_LIST)\n\t\tif(EXISTS \"${rsc_FILE_PATH}\")\n\t\t\tfile(READ \"${rsc_FILE_PATH}\" IBR_RSC_FILE_CONTENT)\n\t\t\tstring(REGEX REPLACE \"\\n\" \";\" IBR_RSC_FILE_CONTENT_LIST \"${IBR_RSC_FILE_CONTENT}\")\n\t\tendif()\n\t\tlist(APPEND IBR_RSC_FILE_CONTENT_LIST \"${rsc_ADD}\")\n\t\tlist(REMOVE_DUPLICATES IBR_RSC_FILE_CONTENT_LIST)\n\t\tfile(WRITE \"${rsc_FILE_PATH}\" \"\")\n\t\tforeach(rscDir ${IBR_RSC_FILE_CONTENT_LIST})\n\t\t\tfile(APPEND \"${rsc_FILE_PATH}\" \"${rscDir}\\n\")\n\t\tendforeach()\n\t\tunset(rsc_ADD)\n\tendif()\n\n\tif(rsc_INSTALL)\n\t\tinstall(FILES ${rsc_FILE_PATH} CONFIGURATIONS ${rsc_CONFIG_TYPE} DESTINATION ${rsc_INSTALL})\n\t\tunset(rsc_INSTALL)\n\tendif()\nendmacro()\n\n\n##\n## Install *.pdb generated file for the current cmake project\n## assuming the output target name is the cmake project name.\n## This macro is useful for crossplateform multi config mode.\n##\n## Usage Example:\n##\n## \tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n##\t\tinstallPDB(${PROJECT_NAME} ${CMAKE_BUILD_TYPE} RUNTIME_DEST bin ARCHIVE_DEST lib LIBRARY_DEST lib)\n## \tendif()\n##\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n##\t\tinstallPDB(${PROJECT_NAME} ${CONFIG_TYPES} RUNTIME_DEST bin ARCHIVE_DEST lib LIBRARY_DEST lib)\n##\tendforeach()\n##\nmacro(installPDB targetName configType)\n\tcmake_parse_arguments(instpdb \"\" \"COMPONENT\" \"ARCHIVE_DEST;LIBRARY_DEST;RUNTIME_DEST\" ${ARGN}) ## both args are directory path\n\n\tif(NOT MSVC)\n\t\treturn()\n\tendif()\n\n    ## Check if DESTINATION are provided according to the TYPE of the given target (see install command doc to see correspodances)\n    get_target_property(type ${targetName} TYPE)\n    if(${type} MATCHES \"EXECUTABLE\" AND instpdb_RUNTIME_DEST)\n        set(pdb_DESTINATION ${instpdb_RUNTIME_DEST})\n    elseif(${type} MATCHES \"STATIC_LIBRARY\" AND instpdb_ARCHIVE_DEST)\n        set(pdb_DESTINATION ${instpdb_ARCHIVE_DEST})\n    elseif(${type} MATCHES \"MODULE_LIBRARY\" AND instpdb_LIBRARY_DEST)\n        set(pdb_DESTINATION ${instpdb_LIBRARY_DEST})\n    elseif(${type} MATCHES \"SHARED_LIBRARY\")\n        if(WIN32 AND instpdb_RUNTIME_DEST)\n            set(pdb_DESTINATION ${instpdb_RUNTIME_DEST})\n        else()\n            set(pdb_DESTINATION ${instpdb_LIBRARY_DEST})\n        endif()\n    endif()\n\n    if(NOT pdb_DESTINATION)\n\t\tset(pdb_DESTINATION bin) ## default destination of the pdb file\n\tendif()\n\n\tif(NOT instpdb_COMPONENT)\n\t\tset(instpdb_COMPONENT )\n\telse()\n\t\tset(instpdb_COMPONENT COMPONENT ${instpdb_COMPONENT})\n\tendif()\n\n\tstring(TOUPPER ${configType} CONFIG_TYPES_UC)\n\tget_target_property(PDB_PATH ${targetName} PDB_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC})\n\n\tget_target_property(confModePostfix ${targetName} ${CONFIG_TYPES_UC}_POSTFIX)\n\tif(NOT confModePostfix)\n\t\tset(confModePostfix \"\")\n\tendif()\n\tset_target_properties(${targetName} PROPERTIES  PDB_NAME_${CONFIG_TYPES_UC} ${targetName}${confModePostfix})\n\tget_target_property(PDB_NAME ${targetName} PDB_NAME_${CONFIG_TYPES_UC})# if not set, this is empty\n\n\tif(EXISTS \"${PDB_PATH}/${PDB_NAME}.pdb\")\n\t\tinstall(FILES \"${PDB_PATH}/${PDB_NAME}.pdb\" CONFIGURATIONS ${configType} DESTINATION ${pdb_DESTINATION} ${instpdb_COMPONENT} OPTIONAL)\n\tendif()\nendmacro()\n\n\n##\n## Add additional target to install a project independently and based on its component\n## configMode is used to prevent default Release installation (we want also to install in other build/config type)\n##\nmacro(installTargetProject targetOfProject targetOfInstallProject)\n \tif(DEFINED CMAKE_BUILD_TYPE) ## for make/nmake based\n\t\tset(configMode ${CMAKE_BUILD_TYPE})\n\telseif(MSVC)\n\t\t## $(Configuration) will be one of the following : Debug, Release, MinSizeRel, RelWithDebInfo\n\t\tset(configMode $(Configuration))\n \tendif()\n\tif(configMode)\n        get_target_property(srcFiles ${targetOfProject} SOURCES)\n\t\tadd_custom_target(\t${targetOfInstallProject} #ALL\n\t\t\t\t\t\t\t${CMAKE_COMMAND} -DBUILD_TYPE=${configMode} -DCOMPONENT=${targetOfInstallProject} -P ${CMAKE_BINARY_DIR}/cmake_install.cmake\n\t\t\t\t\t\t\tDEPENDS ${srcFiles}\n\t\t\t\t\t\t\tCOMMENT \"run the installation only for ${targetOfProject}\" VERBATIM\n\t\t\t\t\t\t\t)\n\t\tadd_dependencies(${targetOfInstallProject} ${targetOfProject})\n\n\t\tget_target_property(INSTALL_BUILD_FOLDER ${targetOfProject} FOLDER)\n\t\tset_target_properties(${targetOfInstallProject} PROPERTIES FOLDER ${INSTALL_BUILD_FOLDER})\n\tendif()\nendmacro()\n\n# Collect all currently added targets in all subdirectories\n#\n# Parameters:\n# - _result the list containing all found targets\n# - _dir root directory to start looking from\nfunction(get_all_targets _result _dir)\n    get_property(_subdirs DIRECTORY \"${_dir}\" PROPERTY SUBDIRECTORIES)\n    foreach(_subdir IN LISTS _subdirs)\n        get_all_targets(${_result} \"${_subdir}\")\n    endforeach()\n\n    get_directory_property(_sub_targets DIRECTORY \"${_dir}\" BUILDSYSTEM_TARGETS)\n    set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE)\nendfunction()\n\n##\n## Add targets for building and installing subdirectories\nmacro(subdirectory_target target directory build_folder)\n\tadd_custom_target(${target}\n\t\tCOMMENT \"run build for all projects in this directory\" VERBATIM\n\t)\n\tget_all_targets(ALL_TARGETS ${directory})\n\tadd_dependencies(${target} ${ALL_TARGETS})\n\tadd_custom_target(${target}_install\n\t\t${CMAKE_COMMAND} -DBUILD_TYPE=$<CONFIG> -DCOMPONENT=${target}_install -P ${CMAKE_BINARY_DIR}/cmake_install.cmake\n\t\tCOMMENT \"run install for all projects in this directory\" VERBATIM\n\t)\n\tadd_dependencies(${target}_install ${target})\n\n\tset_target_properties(${target}\t\t\tPROPERTIES FOLDER ${build_folder})\n\tset_target_properties(${target}_install PROPERTIES FOLDER ${build_folder})\nendmacro()\n\n\n##  CMAKE install all required dependencies for an application (included system OS files like msvc*.dll for example)\n##\n## install_runtime(<installedFilePathTargetAppToResolve>\n##      [TARGET                 name]\n##      [PLUGINS \t\t\t\tname \t\t[nameN ...] [PLUGIN_PATH_NAME currentPathName [FROM_REL_PATH matchDirFromCurrentPathName] [PLUGIN_PATH_DEST installDir] ]\n##      [PLUGINS \t\t\t\t...]\n##      [DIRS \t\t\t\t\tpath \t\t[pathN ...] ]\n##\t\t[TARGET_LIBRARIES  \t\tfilePath\t[filePathN ...] ]\n##\t\t[TARGET_PACKAGES   \t\tpackageName [packageNameN ...] ]\n##\t\t[COMPONENT\t\t\t\tinstallComponentName]\n##\t\t[PLAUSIBLES_POSTFIX\t\tDebug_postfix [MinSizeRel_postfix relWithDebInfo_postfix ...] ]\n##      [VERBOSE]\n## )\n##\n## installedFilePathTargetAppToResolve : the final installed targetApp absolute full file path name you want to resolve\n##\n## TARGET           :   The target app we want to install. If given, it's used to look for link libraries paths (best choice to use, strongly advised to use it)\n##\n## PLUGINS \t\t\t: \tSome application built use/load some plugins which can't be detect inside its binary,\n##\t\t\t\t\t\tso, here you can specify which plugins the application use/load in order to install them\n##\t\t\t\t\t\tand resolve also there dependencies.\n## \t\tWith PLUGINS multi FLAGS \t:\n## \t \t\tPLUGIN_PATH_NAME \t: The current plugin full file path we want to install\n##\t\t\tFROM_REL_PATH\t\t: [optional: default only the file is kept] From which matching dir of the plugin path we want to install (keep the directories structure)\n##\t\t\tPLUGIN_PATH_DEST\t: [optional: default relative to executable directory] Where (full path to the install directory) we will install the plugin file (or file path)\n##\n## DIRS \t\t\t:\tA list of directories to looking for dependencies\n## TARGET_LIBRARIES :\tDEPRECATED (use TARGET flag instead) : The cmake content variables used for the target_link_libraries(<targetApp> ...)\n## TARGET_PACKAGES \t: \tDEPRECATED (use TARGET flag instead) : The cmake package names used for the findPackage(...) for your targetApp\n##\t\t\t\t\t\tADVICE: This flag add entries in cache (like: <packageName>_DIR), it could be useful to fill these variable!\n## COMPONENT\t\t:\t(default to runtime) Is the component name associated to the installation\n##\t\t\t\t\t\tIt is used when you want to install separatly some part of your projets (see install cmake doc)\n## VERBOSE\t\t\t: \tFor debug or to get more informations in the output console\n##\n## Usage:\n##\t install_runtime(${CMAKE_INSTALL_PREFIX}/${EXECUTABLE_NAME}${CMAKE_EXECUTABLE_SUFFIX}\n##\t\tVERBOSE\n##      TARGET  ${PROJECT_NAME}\n##      PLAUSIBLES_POSTFIX  _d\n##      PLUGINS\n##\t\t    PLUGIN_PATH_NAME    ${PLUGIN_PATH_NAME}${CMAKE_SHARED_MODULE_SUFFIX} ## will be installed (default exec path if no PLUGINS_DEST) and then will be resolved\n##\t\t\tFROM_REL_PATH\t\tplugins ## optional, used especially for keeping qt plugins tree structure\n##          PLUGIN_PATH_DEST    ${CMAKE_INSTALL_PREFIX}/plugins ## (or relative path 'plugins' will be interpreted relative to installed executable)\n##\t\tDIRS\t\t\t\t${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}\n##\t\tTARGET_LIBRARIES\t${OPENGL_LIBRARIES}         ## DEPRECATED (use TARGET flag instead)\n##\t\t\t\t\t\t\t${GLEW_LIBRARIES}\n##\t\t\t\t\t\t\t${GLUT_LIBRARIES}\n##\t\t\t\t\t\t\t${Boost_LIBRARIES}\n##\t\t\t\t\t\t\t${SuiteSparse_LIBRARIES}\n##\t\t\t\t\t\t\t${CGAL_LIBRARIES}\n##\t\tTARGET_PACKAGES\t\tOPENGL                      ## DEPRECATED (use TARGET flag instead)\n##\t\t\t\t\t\t\tGLEW\n##\t\t\t\t\t\t\tGLUT\n##\t\t\t\t\t\t\tCGAL\n##\t\t\t\t\t\t\tBoost\n##\t\t\t\t\t\t\tSuiteSparse\n##\t)\n##\n## For plugins part, it use our internal parse_arguments_multi.cmake\n##\nfunction(install_runtime installedFilePathTargetAppToResolve)\n    set(optionsArgs \"VERBOSE\")\n    set(oneValueArgs \"COMPONENT;INSTALL_FOLDER;CONFIG_TYPE\")\n    set(multiValueArgs \"DIRS;PLUGINS;TARGET_LIBRARIES;TARGET_PACKAGES;TARGET;PLAUSIBLES_POSTFIX\")\n    cmake_parse_arguments(inst_run \"${optionsArgs}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN} )\n\n    if(IS_ABSOLUTE ${installedFilePathTargetAppToResolve})\n    else()\n        set(installedFilePathTargetAppToResolve ${inst_run_INSTALL_FOLDER}/${installedFilePathTargetAppToResolve})\n    endif()\n\n\tget_filename_component(EXEC_NAME ${installedFilePathTargetAppToResolve} NAME_WE)\n\tget_filename_component(EXEC_PATH ${installedFilePathTargetAppToResolve} PATH)\n\n\tif(NOT inst_run_COMPONENT)\n\t\tset(inst_run_COMPONENT runtime)\n\tendif()\n\n\n    ## Try to append as more possible as possible paths to find dependencies (deprecated since we can use target_properties to get back paths)\n    set(libPaths )\n\tforeach(libraryFileName ${inst_run_TARGET_LIBRARIES})\n\t\tif(IS_DIRECTORY \"${libraryFileName}\")\n\t\t\tlist(APPEND libPaths \"${libraryFileName}\")\n\t\telse()\n\t\t\tget_filename_component(libpath \"${libraryFileName}\" PATH)\n\t\t\tif(EXISTS \"${libpath}\")\n\t\t\t\tlist(APPEND libPaths \"${libpath}\")\n\t\t\tendif()\n\t\tendif()\n\tendforeach()\n\n    ## This macro is used internaly here to recursilvely get path of LINK_LIBRARIES of each non imported target\n    ## Typically if you have 2 internal dependencies between cmake targets, we want cmake to be able to get back path where are these dependencies\n    macro(recurseDepList target)\n        get_target_property(linkLibs ${target} LINK_LIBRARIES)\n        foreach(lib ${linkLibs})\n            string(FIND ${lib} \">\" strId) ## cmake is using generator-expression?\n\t\t\tif(TARGET ${lib})\n\t\t\t\t## Skipping interface libraries as they're system ones\n                get_target_property(type ${lib} TYPE)\n\t\t\t\tget_target_property(imported ${lib} IMPORTED)\n\t\t\t\tif(type STREQUAL \"INTERFACE_LIBRARY\")\n\t\t\t\t\tget_target_property(imp_loc ${lib} INTERFACE_IMPORTED_LOCATION)\n\t\t\t\t\tif(imp_loc)\n\t\t\t\t\t\tget_filename_component(imp_loc ${imp_loc} PATH)\n\t\t\t\t\t\tlist(APPEND targetLibPath ${imp_loc})\n\t\t\t\t\tendif()\n\t\t\t\t\tget_target_property(loc ${lib} INTERFACE_LOCATION)\n\t\t\t\t\tif(loc)\n\t\t\t\t\t\tget_filename_component(loc ${loc} PATH)\n\t\t\t\t\t\tlist(APPEND targetLibPath ${loc})\n\t\t\t\t\tendif()\n                ## it's not a path but a single target name\n                ## for build-target which are part of the current cmake configuration : nothing to do as cmake already know the output path\n                ## for imported target, we need to look for theire imported location\n                elseif(imported)\n                    get_target_property(imp_loc ${lib} IMPORTED_LOCATION)\n                    if(imp_loc)\n                        get_filename_component(imp_loc ${imp_loc} PATH)\n                        list(APPEND targetLibPath ${imp_loc})\n                    endif()\n                    get_target_property(loc ${lib} LOCATION)\n                    if(loc)\n                        get_filename_component(loc ${loc} PATH)\n                        list(APPEND targetLibPath ${loc})\n                    endif()\n                else()\n                    recurseDepList(${lib})\n                endif()\n            elseif(NOT ${strId} MATCHES -1) ## mean cmake use generator-expression (CMAKE VERSION > 3.0)\n                string(REGEX MATCH      \">:[@A-Za-z_:/.0-9-]+\"           targetLibPath ${lib})\n                string(REGEX REPLACE    \">:([@A-Za-z_:/.0-9-]+)\" \"\\\\1\"   targetLibPath ${targetLibPath})\n                get_filename_component(targetLibPath ${targetLibPath} PATH)\n            elseif(EXISTS ${lib})\n                set(targetLibPath ${lib})\n                get_filename_component(targetLibPath ${targetLibPath} PATH)\n            else()\n                #message(STATUS \"[install_runtime] skip link library : ${lib} , of target ${target}\")\n            endif()\n            if(targetLibPath)\n                list(APPEND targetLinkLibsPathList ${targetLibPath})\n            endif()\n        endforeach()\n        if(targetLinkLibsPathList)\n            list(REMOVE_DUPLICATES targetLinkLibsPathList)\n        endif()\n    endmacro()\n    if(inst_run_TARGET)\n        recurseDepList(${inst_run_TARGET})\n        if(targetLinkLibsPathList)\n            list(APPEND libPaths ${targetLinkLibsPathList})\n        endif()\n    endif()\n\n\tif(libPaths)\n\t\tlist(REMOVE_DUPLICATES libPaths)\n        foreach(libPath ${libPaths})\n            get_filename_component(path ${libPath} PATH)\n            list(APPEND libPaths ${path})\n        endforeach()\n\tendif()\n\n\n\t## possible speciale dir(s) according to the build system and OS\n\tif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\t\tset(BUILD_TYPES_FOR_DLL \"x64\")\n\t\tif(WIN32)\n\t\t\tlist(APPEND BUILD_TYPES_FOR_DLL \"Win64\")\n\t\tendif()\n\telse()\n\t\tset(BUILD_TYPES_FOR_DLL \"x86\")\n\t\tif(WIN32)\n\t\t\tlist(APPEND BUILD_TYPES_FOR_DLL \"Win32\")\n\t\tendif()\n\tendif()\n\n\n\t## Try to append as more as possible paths to find dependencies (here, mainly for *.dll)\n\tforeach(dir ${inst_run_DIRS} ${libPaths})\n\t\tif(EXISTS \"${dir}/bin\")\n\t\t\tlist(APPEND inst_run_DIRS \"${dir}/bin\")\n        elseif(EXISTS \"${dir}\")\n            list(APPEND inst_run_DIRS \"${dir}\")\n\t\tendif()\n\tendforeach()\n    list(REMOVE_DUPLICATES inst_run_DIRS)\n\tforeach(dir ${inst_run_DIRS})\n\t\tif(EXISTS \"${dir}\")\n\t\t\tlist(APPEND argDirs ${dir})\n\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\tif(EXISTS \"${dir}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tlist(APPEND argDirs \"${dir}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\t\tif(EXISTS \"${dir}/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\t\tif(EXISTS \"${dir}/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tendif()\n\t\t\t\tendif()\n\t\t\tendforeach()\n\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\tif(EXISTS \"${dir}/${OUTPUTCONFIG}\")\n\t\t\t\t\tlist(APPEND argDirs \"${dir}/${OUTPUTCONFIG}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${dir}/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendforeach()\n\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\tif(EXISTS \"${dir}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tlist(APPEND argDirs \"${dir}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${dir}/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\tendif()\n\tendforeach()\n\tif(argDirs)\n\t\tlist(REMOVE_DUPLICATES argDirs)\n\tendif()\n\n\n\t## Try to append as more possible paths to find dependencies (here, mainly for *.dll)\n\tforeach(packageName ${inst_run_TARGET_PACKAGES})\n\t\tif(EXISTS \"${${packageName}_DIR}\")\n\t\t\tlist(APPEND packageDirs ${${packageName}_DIR})\n\t\t\tlist(APPEND packageDirs ${${packageName}_DIR}/bin)\n\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tendif()\n\t\t\t\tendif()\n\t\t\tendforeach()\n\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}\")\n\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendforeach()\n\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\telse()\n\t\t\tset(${packageName}_DIR \"$ENV{${packageName}_DIR}\" CACHE PATH \"${packageName}_DIR root directory for looking for dirs containning *.dll\")\n\t\tendif()\n\tendforeach()\n\tif(packageDirs)\n\t\tlist(REMOVE_DUPLICATES packageDirs)\n\tendif()\n\n\n\tset(dirsToLookFor \"${EXEC_PATH}\")\n\tif(packageDirs)\n\t\tlist(APPEND dirsToLookFor ${packageDirs})\n\tendif()\n\tif(argDirs)\n\t\tlist(APPEND dirsToLookFor ${argDirs})\n\tendif()\n\tget_property(used_LINK_DIRECTORIES DIRECTORY PROPERTY LINK_DIRECTORIES)\n\tif (used_LINK_DIRECTORIES)\n\t\tlist(APPEND dirsToLookFor ${used_LINK_DIRECTORIES})\n\t\tlist(REMOVE_DUPLICATES dirsToLookFor)\n\tendif()\n\n\n    ## handle plugins\n\tset(pluginsList \"\")\n    include(parse_arguments_multi) ## this function will process recursively items of the sub-list [default print messages]\n    function(parse_arguments_multi_function results)\n        cmake_parse_arguments(pamf \"VERBOSE\" \"PLUGIN_PATH_DEST;FROM_REL_PATH;EXEC_PATH;COMPONENT\" \"\" ${ARGN}) ## EXEC_PATH and COMPONENT are for exclusive internal use\n\t\tlist(REMOVE_DUPLICATES pamf_UNPARSED_ARGUMENTS)\n        foreach(PLUGIN_PATH_NAME ${pamf_UNPARSED_ARGUMENTS})\n            if(EXISTS ${PLUGIN_PATH_NAME})\n                if(IS_DIRECTORY ${PLUGIN_PATH_NAME})\n                    if(pamf_VERBOSE)\n                        message(WARNING \"${PLUGIN_PATH_NAME} IS_DIRECTORY, cannot installed a directory, please give a path filename\")\n                    endif()\n                else()\n                    if(NOT pamf_PLUGIN_PATH_DEST)\n                        set(PLUGIN_PATH_DEST ${pamf_EXEC_PATH}) ## the default dest value\n\t\t\t\t\telse()\n\t\t\t\t\t\tset(PLUGIN_PATH_DEST ${pamf_PLUGIN_PATH_DEST})\n                    endif()\n\n\t\t\t\t\tif(pamf_FROM_REL_PATH)\n\t\t\t\t\t\tfile(TO_CMAKE_PATH ${PLUGIN_PATH_NAME} PLUGIN_PATH_NAME)\n\t\t\t\t\t\tget_filename_component(PLUGIN_PATH ${PLUGIN_PATH_NAME} PATH)\n\t\t\t\t\t\tunset(PLUGIN_PATH_LIST)\n\t\t\t\t\t\tunset(PLUGIN_PATH_LIST_COUNT)\n\t\t\t\t\t\tunset(PLUGIN_REL_PATH_LIST)\n\t\t\t\t\t\tunset(PLUGIN_REL_PATH)\n\t\t\t\t\t\tstring(REPLACE \"/\" \";\" PLUGIN_PATH_LIST ${PLUGIN_PATH}) ## create a list of dir\n\t\t\t\t\t\tlist(FIND \tPLUGIN_PATH_LIST ${pamf_FROM_REL_PATH} id)\n\t\t\t\t\t\tlist(LENGTH PLUGIN_PATH_LIST PLUGIN_PATH_LIST_COUNT)\n\t\t\t\t\t\tif(${id} GREATER 0)\n\t\t\t\t\t\t\tmath(EXPR id \"${id}+1\") ## matches relative path not include\n\t\t\t\t\t\t\tmath(EXPR PLUGIN_PATH_LIST_COUNT \"${PLUGIN_PATH_LIST_COUNT}-1\") ## the end of the list\n\t\t\t\t\t\t\tforeach(i RANGE ${id} ${PLUGIN_PATH_LIST_COUNT})\n\t\t\t\t\t\t\t\tlist(GET \tPLUGIN_PATH_LIST \t${i} out)\n\t\t\t\t\t\t\t\tlist(APPEND PLUGIN_REL_PATH_LIST \t${out})\n\t\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\t\tforeach(dir ${PLUGIN_REL_PATH_LIST})\n\t\t\t\t\t\t\t\tset(PLUGIN_REL_PATH \"${PLUGIN_REL_PATH}/${dir}\")\n\t\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\tendif()\n\t\t\t\t\t\tset(PLUGIN_PATH_DEST ${PLUGIN_PATH_DEST}${PLUGIN_REL_PATH})\n\t\t\t\t\tendif()\n\n                    install(FILES ${PLUGIN_PATH_NAME} CONFIGURATIONS ${inst_run_CONFIG_TYPE} DESTINATION ${PLUGIN_PATH_DEST} COMPONENT ${pamf_COMPONENT})\n                    get_filename_component(pluginName ${PLUGIN_PATH_NAME} NAME)\n                    if(IS_ABSOLUTE ${PLUGIN_PATH_DEST})\n                    else()\n                        set(PLUGIN_PATH_DEST ${inst_run_INSTALL_FOLDER}/${PLUGIN_PATH_DEST})\n                    endif()\n                    list(APPEND pluginsList ${PLUGIN_PATH_DEST}/${pluginName})\n                endif()\n            else()\n                message(WARNING \"You need to provide a valid PLUGIN_PATH_NAME\")\n                set(pluginsList )\n            endif()\n        endforeach()\n        set(${results} ${pluginsList} PARENT_SCOPE)\n    endfunction()\n\n    if(inst_run_VERBOSE)\n        list(APPEND extra_flags_to_add VERBOSE)\n    endif()\n    list(APPEND extra_flags_to_add EXEC_PATH ${EXEC_PATH} COMPONENT ${inst_run_COMPONENT}) ## for internal use inside overloaded function\n    list(LENGTH inst_run_PLUGINS inst_run_PLUGINS_count)\n    if(${inst_run_PLUGINS_count} GREATER 0)\n        parse_arguments_multi(PLUGIN_PATH_NAME inst_run_PLUGINS ${inst_run_PLUGINS} ## see internal overload parse_arguments_multi_function for processing each sub-list\n                                NEED_RESULTS ${inst_run_PLUGINS_count}  ## this is used to check when we are in the first loop (in order to reset parse_arguments_multi_results)\n                                EXTRAS_FLAGS ${extra_flags_to_add}      ## this is used to allow catching additional internal flags of our overloaded function\n        )\n    endif()\n\n    #message(parse_arguments_multi_results = ${parse_arguments_multi_results})\n    list(APPEND pluginsList ${parse_arguments_multi_results})\n\n\n\n\t## Install rules for required system runtimes such as MSVCRxx.dll\n\tset(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON)\n\tinclude(InstallRequiredSystemLibraries)\n\tif(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)\n\t\tinstall(FILES \t\t\t${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}\n\t\t\t\tCONFIGURATIONS \t${inst_run_CONFIG_TYPE}\n\t\t\t\tDESTINATION \t${EXEC_PATH}\n\t\t\t\tCOMPONENT   \t${inst_run_COMPONENT}\n\t\t)\n\tendif()\n\n\t## print what we are doing to do\n\tif(inst_run_VERBOSE)\n\t\tmessage(STATUS \"[install_runtime] On install target call, cmake will try to resolve dependencies for given app:\\n ${installedFilePathTargetAppToResolve} (with plausible postfix: ${inst_run_PLAUSIBLES_POSTFIX})\")\n\t\tif(pluginsList)\n\t\t\tmessage(STATUS \"   and also for plugins :\")\n\t\t\tforeach(plugin ${pluginsList})\n\t\t\t\tmessage(STATUS \"      ${plugin}\")\n\t\t\tendforeach()\n\t\tendif()\n\t\tmessage(STATUS \"   Looking for dependencies into:\")\n\t\tforeach(dir ${dirsToLookFor})\n\t\t\tmessage(STATUS \"      ${dir}\")\n\t\tendforeach()\n\tendif()\n\n\t## Install rules for required dependencies libs/plugins for the target app\n\t## will resolve all installed target files with config modes postfixes\n\tstring(TOUPPER ${inst_run_CONFIG_TYPE} inst_run_CONFIG_TYPE_UC)\n\tget_target_property(postfix ${inst_run_TARGET} \"${inst_run_CONFIG_TYPE_UC}_POSTFIX\")\n\tinstall(CODE \"set(target \t\t\t\t\t\t\\\"${inst_run_TARGET}\\\")\" \t\t\t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(inst_run_CONFIG_TYPE \t\t\t\\\"${inst_run_CONFIG_TYPE}\\\")\" \t\t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(inst_run_INSTALL_FOLDER \t\t\\\"${inst_run_INSTALL_FOLDER}\\\")\" \t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(app\t \t\t\t\t\t\t\\\"${EXEC_PATH}/${EXEC_NAME}${postfix}${CMAKE_EXECUTABLE_SUFFIX}\\\")\" \tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(dirsToLookFor \t\t\t\t\\\"${dirsToLookFor}\\\")\" \t\t\t\t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE\n\t\t[[\n\t\t\tif(\"${CMAKE_INSTALL_CONFIG_NAME}\" STREQUAL \"${inst_run_CONFIG_TYPE}\")\n\t\t\t\tmessage(STATUS \"Installing ${target} dependencies...\")\n\n\t\t\t\tfile(GET_RUNTIME_DEPENDENCIES\n\t\t\t\t\tEXECUTABLES ${app}\n\t\t\t\t\tRESOLVED_DEPENDENCIES_VAR _r_deps\n\t\t\t\t\tUNRESOLVED_DEPENDENCIES_VAR _u_deps\n\t\t\t\t\tCONFLICTING_DEPENDENCIES_PREFIX _c_deps\n\t\t\t\t\tDIRECTORIES ${dirsToLookFor}\n\t\t\t\t\tPRE_EXCLUDE_REGEXES \"api-ms-*\"\n\t\t\t\t\tPOST_EXCLUDE_REGEXES \".*system32/.*\\\\.dll\" \".*SysWOW64/.*\\\\.dll\"\n\t\t\t\t)\n\t\t\t\n\t\t\t\tif(_u_deps)\n\t\t\t\t\tmessage(WARNING \"There were unresolved dependencies for executable ${EXEC_FILE}: \\\"${_u_deps}\\\"!\")\n\t\t\t\tendif()\n\t\t\t\tif(_c_deps_FILENAMES)\n\t\t\t\t\tmessage(WARNING \"There were conflicting dependencies for executable ${EXEC_FILE}: \\\"${_c_deps_FILENAMES}\\\"!\")\n\t\t\t\tendif()\n\t\t\t\n\t\t\t\tforeach(_file ${_r_deps})\n\t\t\t\t\tfile(INSTALL\n\t\t\t\t\tDESTINATION \"${inst_run_INSTALL_FOLDER}/bin\"\n\t\t\t\t\tTYPE SHARED_LIBRARY\n\t\t\t\t\tFOLLOW_SYMLINK_CHAIN\n\t\t\t\t\tFILES \"${_file}\"\n\t\t\t\t)\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\t]]\n\t   COMPONENT ${inst_run_COMPONENT} CONFIGURATIONS ${CONFIG_TYPE}\n\t)\n\nendfunction()\n\n## High level macro to install resources in the correct folder\n##\n## EXECUTABLE: [opt] option to copy files as programs\n## RELATIVE  : [opt] copy files relatively to current folder\n## TYPE      : [opt] type and folder where to store the files\n## FOLDER    : [opt] subfolder to use\n## FILES     : [opt] contains a list of resources files to copy to install folder\nmacro(ibr_install_rsc target)\n\tcmake_parse_arguments(install_rsc_${target} \"EXECUTABLE;RELATIVE\" \"TYPE;FOLDER\" \"FILES\" ${ARGN})\n\tset(rsc_target \"${target}_${install_rsc_${target}_TYPE}\")\n\n\tif(install_rsc_${target}_FOLDER)\n\t\tset(rsc_folder \"${install_rsc_${target}_TYPE}/${install_rsc_${target}_FOLDER}\")\n\telse()\n\t\tset(rsc_folder \"${install_rsc_${target}_TYPE}\")\n\tendif()\n\n\tadd_custom_target(${rsc_target}\n\t\t\t\t\tCOMMENT \"run the ${install_rsc_${target}_TYPE} installation only for ${target} (component ${rsc_target})\"\n\t\t\t\t\tVERBATIM)\n\tforeach(scriptFile ${install_rsc_${target}_FILES})\n\t\tif(install_rsc_${target}_RELATIVE)\n\t\t\tfile(RELATIVE_PATH relativeFilename ${CMAKE_CURRENT_SOURCE_DIR} ${scriptFile})\n\t\telse()\n\t\t\tget_filename_component(relativeFilename ${scriptFile} NAME)\n\t\tendif()\n\n\t\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\tadd_custom_command(TARGET ${rsc_target} POST_BUILD\n\t\t\t\t\t\t\tCOMMAND ${CMAKE_COMMAND} -E\n\t\t\t\t\t\t\tcopy_if_different ${scriptFile} ${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/${rsc_folder}/${relativeFilename})\n\t\tendif()\n\t\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\tadd_custom_command(TARGET ${rsc_target} POST_BUILD\n\t\t\t\t\t\t\tCOMMAND ${CMAKE_COMMAND} -E\n\t\t\t\t\t\t\tcopy_if_different ${scriptFile} ${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/${rsc_folder}/${relativeFilename})\n\t\tendforeach()\n\tendforeach()\n\n\tget_target_property(INSTALL_RSC_BUILD_FOLDER ${target} FOLDER)\n\tset_target_properties(${rsc_target} PROPERTIES FOLDER ${INSTALL_RSC_BUILD_FOLDER})\n\n\tadd_dependencies(${target} ${rsc_target})\n\tadd_dependencies(PREBUILD ${rsc_target})\n\n\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\tresourceFile(ADD ${rsc_folder} CONFIG_TYPE ${CMAKE_BUILD_TYPE} FILE_PATH \"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/ibr_resources.ini\")\n\t\t\n\t\tif(install_rsc_${target}_EXECUTABLE)\n\t\t\tinstall(\n\t\t\t\tPROGRAMS ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/${rsc_folder}\"\n\t\t\t)\n\t\telse()\n\t\t\tinstall(\n\t\t\t\tFILES ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/${rsc_folder}\"\n\t\t\t)\n\t\tendif()\n\tendif()\n\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\tresourceFile(ADD ${rsc_folder} CONFIG_TYPE ${CONFIG_TYPES} FILE_PATH \"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/ibr_resources.ini\")\n\t\t\n\t\tif(install_rsc_${target}_EXECUTABLE)\n\t\t\tinstall(\n\t\t\t\tPROGRAMS ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/${rsc_folder}\"\n\t\t\t)\n\t\telse()\n\t\t\tinstall(\n\t\t\t\tFILES ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/${rsc_folder}\"\n\t\t\t)\n\t\tendif()\n\tendforeach()\nendmacro()\n\n\n## High level macro to install in an homogen way all our ibr targets (it use some functions inside this file)\n##\n## RSC_FILE_ADD : [opt] is used to auto write/append relative paths of target resources into a common file\n## INSTALL_PDB  : [opt] is used to auto install PDB file (when using MSVC according to the target type)\n## STANDALONE   : [opt] bool ON/OFF var to call install_runtime or not (for bundle resolution)\n##       DIRS   : [opt] used if STANDALONE set to ON, see install_runtime doc\n##       PLUGINS: [opt] used if STANDALONE set to ON, see install_runtime doc\n## MSVC_CMD     : [opt] used to specify an absolute filePathName application to launch with the MSVC IDE Debugger associated to this target (project file)\n## MSVC_ARGS    : [opt] load the MSVC debugger with correct settings (app path, args, working dir)\n##\nmacro(ibr_install_target target)\n\tcmake_parse_arguments(ibrInst${target} \"VERBOSE;INSTALL_PDB\" \"COMPONENT;MSVC_ARGS;STANDALONE;RSC_FOLDER\" \"SHADERS;RESOURCES;SCRIPTS;DIRS;PLUGINS\" ${ARGN})\n\t\n\tif(ibrInst${target}_RSC_FOLDER)\n\t\tset(rsc_folder \"${ibrInst${target}_RSC_FOLDER}\")\n\telse()\n\t\tset(rsc_folder \"${target}\")\n\tendif()\n\n\tif(ibrInst${target}_SHADERS)\n\t\tibr_install_rsc(${target} EXECUTABLE TYPE \"shaders\" FOLDER ${rsc_folder} FILES \"${ibrInst${target}_SHADERS}\")\n    endif()\n\t\n\tif(ibrInst${target}_RESOURCES)\n\t\tibr_install_rsc(${target} TYPE \"resources\" FOLDER ${rsc_folder} FILES \"${ibrInst${target}_RESOURCES}\")\n    endif()\n\t\n\tif(ibrInst${target}_SCRIPTS)\n\t\tibr_install_rsc(${target} EXECUTABLE TYPE \"scripts\" FOLDER ${rsc_folder} FILES \"${ibrInst${target}_SCRIPTS}\")\n    endif()\n\n    if(ibrInst${target}_COMPONENT)\n        set(installCompArg COMPONENT ${ibrInst${target}_COMPONENT})\n        ## Create a custom install target based on COMPONENT\n        installTargetProject(${target} ${ibrInst${target}_COMPONENT})\n\tendif()\n\t\n\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\tset_target_properties(${target} PROPERTIES ${CMAKE_BUILD_TYPE}_POSTFIX \t\"${CMAKE_${CMAKE_BUILD_TYPE}_POSTFIX}\")\n\t\tget_target_property(CURRENT_TARGET_BUILD_TYPE_POSTFIX ${target} ${CMAKE_BUILD_TYPE}_POSTFIX)\n\tendif()\n\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\tset_target_properties(${target} PROPERTIES ${CONFIG_TYPES_UC}_POSTFIX \t\"${CMAKE_${CONFIG_TYPES_UC}_POSTFIX}\")\n\t\tget_target_property(CURRENT_TARGET_BUILD_TYPE_POSTFIX ${target} ${CONFIG_TYPES_UC}_POSTFIX)\n\tendforeach()\n\n\t## Specify default installation rules\n\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\tinstall(TARGETS\t${target}\n\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\tLIBRARY\t\tDESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t\tARCHIVE\t\tDESTINATION ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t\tRUNTIME \tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t)\n\t\tinstall(TARGETS\t${target}\n\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\tLIBRARY\t\tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t\tARCHIVE\t\tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t)\n\tendif()\n\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\tinstall(TARGETS\t${target}\n\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\tLIBRARY\t\tDESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t\tARCHIVE\t\tDESTINATION ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t\tRUNTIME \tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t)\n\t\tinstall(TARGETS\t${target}\n\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\tLIBRARY\t\tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t\tARCHIVE\t\tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t)\n\tendforeach()\n\n    if(ibrInst${target}_INSTALL_PDB)\n        if(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\tinstallPDB(${target} ${CMAKE_BUILD_TYPE}\n\t\t\t\tLIBRARY_DEST ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t\tARCHIVE_DEST ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t\tRUNTIME_DEST ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t)\n        endif()\n        foreach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\tinstallPDB(${target} ${CONFIG_TYPES}\n\t\t\t\tLIBRARY_DEST ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\tARCHIVE_DEST ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\tRUNTIME_DEST ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t)\n        endforeach()\n    endif()\n\n    ## install dynamic necessary dependencies\n    if(ibrInst${target}_STANDALONE)\n        get_target_property(type ${target} TYPE)\n        if(${type} MATCHES \"EXECUTABLE\")\n\n            if(ibrInst${target}_VERBOSE)\n                set(VERBOSE VERBOSE)\n            else()\n                set(VERBOSE )\n\t\t\tendif()\n\t\t\t\n\t\t\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\t\tinstall_runtime(bin/${target}${CMAKE_EXECUTABLE_SUFFIX} ## default relative to CMAKE_INSTALL_PREFIX\n\t\t\t\t\tINSTALL_FOLDER\t\t\"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}\"\n\t\t\t\t\tCONFIG_TYPE\t\t\t${CMAKE_BUILD_TYPE}\n\t\t\t\t\t${VERBOSE}\n\t\t\t\t\tTARGET              ${target}\n\t\t\t\t\t${installCompArg}\n\t\t\t\t\tPLUGINS\t## will be installed\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_PLUGINS}\n\t\t\t\t\tDIRS\t\t\t\t${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_DIRS}\n\t\t\t\t)\n\t\t\tendif()\n\t\t\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\t\tinstall_runtime(bin/${target}${CMAKE_EXECUTABLE_SUFFIX} ## default relative to CMAKE_INSTALL_PREFIX\n\t\t\t\t\tINSTALL_FOLDER\t\t\"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}\"\n\t\t\t\t\tCONFIG_TYPE\t\t\t${CONFIG_TYPES}\n\t\t\t\t\t${VERBOSE}\n\t\t\t\t\tTARGET              ${target}\n\t\t\t\t\t${installCompArg}\n\t\t\t\t\tPLUGINS\t## will be installed\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_PLUGINS}\n\t\t\t\t\tDIRS\t\t\t\t${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_DIRS}\n\t\t\t\t)\n\t\t\tendforeach()\n        else()\n            message(WARNING \"STANDALONE option is only compatible with EXECUTABLES target type. Skip the STANDALONE installation process.\")\n        endif()\n    endif()\n\n    ## Provide a way to directly load the MSVC debugger with correct settings\n    if(MSVC)\n        if(ibrInst${target}_MSVC_CMD)  ## command absolute filePathName is optional as the default is to use the installed target file application\n            set(msvcCmdArg  COMMAND ${ibrInst${target}_MSVC_CMD}) ## flag following by the value (both to pass to the MSVCsetUserCommand function)\n        endif()\n        if(ibrInst${target}_MSVC_ARGS) ## args (between quotes) are optional\n            set(msvcArgsArg ARGS ${ibrInst${target}_MSVC_ARGS})   ## flag following by the value (both to pass to the MSVCsetUserCommand function)\n        endif()\n        get_target_property(type ${target} TYPE)\n        if( (ibrInst${target}_MSVC_CMD OR ibrInst${target}_MSVC_ARGS) OR (${type} MATCHES \"EXECUTABLE\") )\n\t\t\tinclude(MSVCsetUserCommand)\n\t\t\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\t\tMSVCsetUserCommand(\t${target}\n\t\t\t\t\tPATH \t\t\t${CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE}} ##FILE option not necessary since it deduced from targetName\n\t\t\t\t\t\t\t\t\tARGS\t\t\t\t\"${SIBR_PROGRAMARGS}\"\n\t\t\t\t\t${msvcCmdArg}\n\t\t\t\t\t#${msvcArgsArg}\n\t\t\t\t\tWORKING_DIR\t\t${CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE}}\n\t\t\t\t)\n\t\t\tendif()\n\t\t\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\t\tMSVCsetUserCommand(\t${target}\n\t\t\t\t\tPATH \t\t\t${CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC}} ##FILE option not necessary since it deduced from targetName\n\t\t\t\t\t\t\t\t\tARGS\t\t\t\t\"${SIBR_PROGRAMARGS}\"\n\t\t\t\t\t${msvcCmdArg}\n\t\t\t\t\t#${msvcArgsArg}\n\t\t\t\t\tWORKING_DIR\t\t${CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC}}\n\t\t\t\t)\n\t\t\tendforeach()\n        elseif(NOT ${type} MATCHES \"EXECUTABLE\")\n            #message(\"Cannot set MSVCsetUserCommand with target ${target} without COMMAND parameter as it is not an executable (skip it)\")\n        endif()\n    endif()\n\nendmacro()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/parse_arguments_multi.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(NOT WIN32 OR __parse_arguments_multi_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__parse_arguments_multi_cmake_INCLUDED__ ON)\nendif()\n\n## This macro allow to process repeating multi value args from a given function which use cmake_parse_arguments module.\n##\n## cmake_parse_arguments multi args standard behavior:\n##    function(foo)\n##        cmake_parse_arguments(arg \"\" \"\" \"MULTI\" ${ARGN})\n##        foreach(item IN LISTS arg_MULTI)\n##            message(STATUS \"${item}\")\n##        endforeach()\n##    endfunction()\n##    foo(MULTI x y MULTI z w)\n##  The above code outputs 'z' and 'w'. It originally expected it to output all of 'x' 'y' 'z' 'w'.\n##\n## Using this macro inside a function which want to handle repeating multi args values\n## will recursively iterate onto the multi tags list to process each sub list.\n## It take as 1st argument the subTag flag to separate sub list from the main multi list.\n## It take as 2nd argument the nameList of the main multi list (the multiValuesArgs from cmake_parse_arguments: here it is MULTI in the example)\n## and that's why it is important that it should be a macro and not a function (to get access to external variable).\n## Then you give the content of this list allowing to be processed by the macro.\n##\n## parse_arguments_multi macro call a parse_arguments_multi_function which do actually the process from the given sub-list.\n## By default this function only print infos about what variables you are trying to pass/process (only verbose messages),\n## but, by overloading this cmake function, you will be able to externalize the process of your multi argument list.\n##\n## Usage (into a function) : \n## parse_arguments_multi(<multiArgsSubTag> <multiArgsList> <multiArgsListContent> \n##      [NEED_RESULTS <multiArgsListSize>] [EXTRAS_FLAGS <...> <...> ...]\n## )\n##\n## Simple usage example [user point of view]:\n## foo(MULTI\n##    SUB_MULTI x y\n##    SUB_MULTI z w\n## )\n##\n## Simple usage example [inside a function]:\n##    function(foo)\n##        cmake_parse_arguments(arg \"\" \"\" \"MULTI\" ${ARGN})\n##        include(parse_arguments_multi)\n##        function(parse_arguments_multi_function )\n##          #message(\"I'm an overloaded cmake function used by parse_arguments_multi\")\n##          #message(\"I'm processing first part of my sub list: ${ARGN}\")\n##          message(\"ARGV0=${ARGV0}\")\n##          message(\"ARGV1=${ARGV1}\")\n##        endfunction()\n##        parse_arguments_multi(SUB_MULTI arg_MULTI ${arg_MULTI}) ## this function will process recusively items of the sub-list [default print messages]\n##    endfunction()\n##\n##  Will print:\n##      ARGV0=z\n##      ARGV1=w\n##      ARGV0=x\n##      ARGV1=y\n##\n## WARNING : DO NEVER ADD EXTRA THINGS TO parse_arguments_multi MACRO :\n##          parse_arguments_multi(SUB_MULTI arg_MULTI ${arg_MULTI} EXTRAS foo bar SOMTHING) => will failed !!\n## use EXTRAS_FLAGS instead !!\n##\n## Advanced usage example [user point of view]:\n## bar(C:/prout/test.exe VERBOSE \n##      PLUGINS\n##          PLUGIN_PATH_NAME x      PLUGIN_PATH_DEST w\n##          PLUGIN_PATH_NAME a b    PLUGIN_PATH_DEST y\n##          PLUGIN_PATH_NAME c\n## )\n##\n## Advanced usage example [inside a function]:\n##    function(bar execFilePathName)\n##        cmake_parse_arguments(arg \"VERBOSE\" \"\" \"PLUGINS\" ${ARGN})\n##\n##        include(parse_arguments_multi)\n##        function(parse_arguments_multi_function results)\n##            cmake_parse_arguments(pamf \"VERBOSE\" \"PLUGIN_PATH_DEST;EXEC_PATH\" \"\" ${ARGN}) ## EXEC_PATH is for internal use\n##            message(\"\")\n##            message(\"I'm an overloaded cmake function used by parse_arguments_multi from install_runtime function\")\n##            message(\"I'm processing first part of my sub list: ${ARGN}\")\n##            message(\"PLUGIN_PATH_NAME = ${pamf_UNPARSED_ARGUMENTS}\")\n##            message(pamf_VERBOSE = ${pamf_VERBOSE})\n##            message(\"pamf_PLUGIN_PATH_DEST = ${pamf_PLUGIN_PATH_DEST}\")\n##            message(pamf_EXEC_PATH = ${pamf_EXEC_PATH})\n##            if(NOT ${pamf_PLUGIN_PATH_DEST})\n##              set(pamf_PLUGIN_PATH_DEST ${pamf_EXEC_PATH})\n##            endif()\n##            foreach(plugin ${pamf_UNPARSED_ARGUMENTS})\n##              get_filename_component(pluginName ${plugin} NAME)\n##              list(APPEND pluginsList ${pamf_PLUGIN_PATH_DEST}/${pluginName})\n##            endforeach()\n##            set(${results} ${pluginsList} PARENT_SCOPE)\n##        endfunction()\n##\n##        if(arg_VERBOSE)\n##            list(APPEND extra_flags_to_add VERBOSE) ## here we transmit the VERNOSE flag\n##        endif()\n##        get_filename_component(EXEC_PATH ${execFilePathName} PATH) ## will be the default value if PLUGIN_PATH_DEST option is not provided\n##        list(APPEND extra_flags_to_add EXEC_PATH ${EXEC_PATH})  \n##        list(LENGTH arg_PLUGINS arg_PLUGINS_count)\n##        parse_arguments_multi(PLUGIN_PATH_NAME arg_PLUGINS ${arg_PLUGINS}\n##                            NEED_RESULTS ${arg_PLUGINS_count}  ## this is used to check when we are in the first loop (in order to reset parse_arguments_multi_results)\n##                            EXTRAS_FLAGS ${extra_flags_to_add} ## this is used to allow catching VERBOSE and PLUGIN_PATH_DEST flags of our overloaded function\n##        )\n##    endfunction()\n##    message(parse_arguments_multi_results = ${parse_arguments_multi_results}) ## list of the whole pluginsList\n##    #Will print w/x;a/y;b/y;C:/prout/c\n##\n##  NOTE that here, since our overloaded function need to provide a result list, we use the other parse_arguments_multi_function signature (the which one with a results arg)\n##\n\nfunction(parse_arguments_multi_function_default) ## used in case of you want to reset the default behavior of this function process\n    message(\"[default function] parse_arguments_multi_function(ARGC=${ARGC} ARGV=${ARGV} ARGN=${ARGN})\")\n    message(\"This function is used by parse_arguments_multi and have to be overloaded to process sub list of multi values args\")\nendfunction()\n\nfunction(parse_arguments_multi_function )   ## => the function to overload\n    parse_arguments_multi_function_default(${ARGN})\nendfunction()\n\n## first default signature above\n##------------------------------\n## second results signature behind\n\nfunction(parse_arguments_multi_function_default result) ## used in case of you want to reset the default behavior of this function process\n    message(\"[default function] parse_arguments_multi_function(ARGC=${ARGC} ARGV=${ARGV} ARGN=${ARGN})\")\n    message(\"This function is used by parse_arguments_multi and have to be overloaded to process sub list of muluti values args\")\nendfunction()\n\nfunction(parse_arguments_multi_function result)   ## => the function to overload\n    parse_arguments_multi_function_default(result ${ARGN})\nendfunction()\n\n## => the macro to use inside your function which use cmake_parse_arguments\n# NOTE: entry point of parse_arguments_multi, which is called from win3rdPart)\nmacro(parse_arguments_multi multiArgsSubTag multiArgsList #<${multiArgsList}> the content of the list\n)\n    # message (STATUS \"\")\n    # message(STATUS \"calling parse_arguemnts_multi defined in parse_arguments_multi.cmake:141\")\n    # message(STATUS \"multiArgsSubTag = ${multiArgsSubTag}\")\t# CHECK_CACHED_VAR\n    # message(STATUS \"multiArgsList = ${multiArgsList}\")\t# it contains the name of the variable which is holding the list i.e w3p_MULTI_SET\n    # message(STATUS \"value of ${multiArgsList} = ${${multiArgsList}}\") # a semicolon separated list of values passed to SET or MULTISET keyword in win3rdParty\n    # message(STATUS \"actual values ARGN = ${ARGN}\")  # the same as ${${multiArgsList}}\n\n    ## INFO\n    ## starting from CMake 3.5 cmake_parse_arguments is not a module anymore and now is a native CMake command.\n    ## the behaviour is different though\n    ## In CMake 3.4, if you pass multiple times a multi_value_keyword, CMake returns the values of the LAST match\n    ## In CMake 3.5 and above, CMake returns the whole list of values that were following that multi_value_keyword\n    ## example:\n    ## cmake_parse_arguments(\n    ##\t\t\t<prefix>\n    ##\t\t\t\"\"\t\t# options\n    ##\t\t\t\"\"\t\t# one value keywords\n    ##\t\t\t\"MY_MULTI_VALUE_TAG\"\n    ##\t\t\t\tMY_MULTI_VALUE_TAG value1 value2\n    ##\t\t\t\tMY_MULTI_VALUE_TAG value3 value4\n    ##\t\t\t\tMY_MULTI_VALUE_TAG value5 value6\n    ##\t\t\t)\n    ## result in CMake 3.4\n    ## <prefix>_MY_MULTI_VALUE_TAG = \"value5;value6\"\n    ##\n    ## result in CMake 3.8\n    ## <prefix>_MY_MULTI_VALUE_TAG = \"value5;value6\"\n\n    #include(CMakeParseArguments) #module CMakeParseArguments is obsolete since cmake 3.5\n    # cmake_parse_arguments (<prefix> <options> <one_value_keywords> <multi_value_keywords> args)\n    # <options> : options (flags) pass to the macro\n    # <one_value_keywords> : options that neeed a value\n    # <multi_value_keywords> : options that neeed more than one value\n    cmake_parse_arguments(_pam \"\" \"NEED_RESULTS\" \"${multiArgsSubTag};EXTRAS_FLAGS\" ${ARGN})\n    \n    ## multiArgsList is the name of the list used by the multiValuesOption flag from the cmake_parse_arguments of the user function\n    ## that's why we absolutly need to use MACRO here (and also for passing parse_arguments_multi_results when NEED_RESULTS flag is set)\n    \n    ## for debugging\n    #message(\"\")\n    #message(\"[parse_arguments_multi] => ARGN = ${ARGN}\")\n    #message(\"_pam_NEED_RESULTS=${_pam_NEED_RESULTS}\")\n    #message(\"_pam_EXTRAS_FLAGS=${_pam_EXTRAS_FLAGS}\")\n    # foreach(var ${_pam_${multiArgsSubTag}})\n    #     message(\"arg=${var}\")\n    # endforeach()\n\n    if (${CMAKE_VERSION} VERSION_GREATER \"3.5\")\n        # lets make ${_pam_${multiArgsSubTag}} behave as it is in version 3.4\n        # that means, cmake_parse_arguments should have only the last values of a multi set for a given keyword\n\n        # message(\"\")\n        # message(\"values in multiArgsList\")\n        # foreach(val ${${multiArgsList}})\n        #     message(STATUS ${val})\n        # endforeach()\n        # message(\"end values in multiArgsList\")\n\n\n        set(lastIndexFound OFF)\n        list(LENGTH ${multiArgsList} argnLength)\n        # message(${argnLength})\n        math(EXPR argnLength \"${argnLength}-1\")             # make last index a valid one\n        set(recordIndex 0)\n        set(records \"\")                                     # clear records list\n        set(record0 \"\")                                    # clear first record list\n        foreach(iter RANGE ${argnLength})\n            list(GET ${multiArgsList} ${iter} value)\n            # message(STATUS \"index=${iter} value=${value}\")\n            if (${value} STREQUAL ${multiArgsSubTag})\n                if (lastIndexFound)\n                    list(APPEND records ${recordIndex})    # records store the list NAMES\n                    math(EXPR recordIndex \"${recordIndex}+1\")\n                    set(record${recordIndex} \"\")            # clear record list\n                else ()\n                    set(lastIndexFound ON)\n                endif()\n\n                set(lastIndex ${iter})\n            else ()\n                if (lastIndexFound)\n                    # message(${value})\n                    list(APPEND record${recordIndex} ${value})\n                endif()\n            endif()\n        endforeach()\n\n        # save the last list of values\n        if (lastIndexFound)\n            list(APPEND records ${recordIndex})    # records store the list NAMES\n        endif()\n\n        # set multiArgsList to make it behave like CMake 3.4\n        # message(\"\")\n        # message(\"using my records\")\n        foreach(recordName ${records})\n            # message(${recordName})\n            # foreach(value ${record${recordName}})\n            #     message(${value})\n            # endforeach()\n            # message(\"\")\n            set(_pam_${multiArgsSubTag} ${record${recordName}})\n        endforeach()\n        # message(${_pam_${multiArgsSubTag}})\n\n        # message(\"\")\n        # message(\"using argn\")\n        # foreach(value ${ARGN})\n        #     message(${value})\n        # endforeach()\n    endif() # end if cmake > 3.5\n\n    # message(\"values with pam ${_pam_${multiArgsSubTag}}\")\n\n    ## check and init\n    list(LENGTH ${multiArgsList} globalListCount)\t# GLUT_TRACE: globalListCound=16 in CMake3.4 and CMake3.8\n    # message(STATUS \"nr items in multiArgsList: ${globalListCount}\")\n    math(EXPR globalListCount \"${globalListCount}-1\") ## because it will contain [multiArgsSubTag + ${multiArgsList}]\n    if(_pam_NEED_RESULTS)\n        if(${globalListCount} EQUAL ${_pam_NEED_RESULTS})\n            ## first time we enter into this macro (because we call it recursively)\n            unset(parse_arguments_multi_results)\n        endif()\n    endif()\n    \n    ## process the part of the multi agrs list\n    ## ${ARGN} shouldn't be passed to the function in order to avoid missmatch size list ${multiArgsList} and _pam_${multiArgsSubTag}\n    ## if you want to pass extra internal flags from your function to this callback, use EXTRAS_FLAGS\n    if(_pam_NEED_RESULTS)\n        parse_arguments_multi_function(parse_arguments_multi_function_result ${_pam_${multiArgsSubTag}} ${_pam_EXTRAS_FLAGS})\n        list(APPEND parse_arguments_multi_results ${parse_arguments_multi_function_result})\n    else()\n        # message(STATUS \"about to call parse_arguments_multi_function in parse_arguments_multi.cmake:177 ${_pam_${multiArgsSubTag}} and extra flags ${_pam_EXTRAS_FLAGS}\")\n        parse_arguments_multi_function(${_pam_${multiArgsSubTag}} ${_pam_EXTRAS_FLAGS})\n    endif()\n\n    ## remove just processed items from the main list to process (multiArgsList)\n    list(REVERSE ${multiArgsList})\n    list(LENGTH _pam_${multiArgsSubTag} subTagListCount)\n    unset(ids)\n    foreach(id  RANGE ${subTagListCount})\n         list(APPEND ids ${id})\n    endforeach()\n    list(REMOVE_AT  ${multiArgsList} ${ids})\n    list(REVERSE    ${multiArgsList})\n    \n    ## test if remain sub multi list to process (recursive call) or finish the process\n    list(LENGTH ${multiArgsList} mainTagListCount)\n    if(${mainTagListCount} GREATER 1)\n        ## do not pass ${ARGN} just because it will re pass the initial 2 inputs args and we wont as they was consumed (in order to avoir conflicts)\n        # message(STATUS \"about to call a parse_arguments_multi but without knowing where the definition is going to be taken from\")\n        parse_arguments_multi(${multiArgsSubTag} ${multiArgsList} ${${multiArgsList}} \n                                NEED_RESULTS ${_pam_NEED_RESULTS} EXTRAS_FLAGS ${_pam_EXTRAS_FLAGS}\n            )\n    endif()\nendmacro()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/linux/sibr_library.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n# NOTE\n# This feature is used to easily download, store and link external dependencies. This\n# requires to prepare pre-compiled libraries (to download). For now, packages have\n# only be prepare for Windows 64-bit with Visual Studio 2012. (You should re-build\n# everything if you want to use another version of Visual Studio/ another compiler).\n\n# NOTE ABOUT UNIX SYSTEMS\n# There is no need for \"searching mechanism\". This function is discard and your\n# libraries should be installed is the standard folders that are:\n#\n# /usr/include/\n# /usr/lib/\n# /usr/lib64/\n# for packages downloaded using apt-get/yum\n# \n# /usr/local/include/\n# /usr/local/lib/\n# /usr/local/lib64/\n# for packages manually installed (\"make install\")\n#\n# if you encounter problems when linking (e.g. lib not found even if it is installed),\n# please check these folders are in your search PATH environment variables.\n\nset(EXTLIBS_PACKAGE_FOLDER \"${CMAKE_SOURCE_DIR}/extlibs\")\n\nfunction(sibr_addlibrary)\n    if(NOT WIN32)\n        return()\n    endif()\n\n    file(MAKE_DIRECTORY ${EXTLIBS_PACKAGE_FOLDER})\n    cmake_parse_arguments(args \"VCID\" \"VERBOSE;TIMEOUT;DEFAULT_USE;NAME;VERSION;MSVC11;MSVC12;MSVC14;MSVC17\" \"MULTI_SET;SET\" ${ARGN})\n\n\n    if (NOT \"${args_VERSION}\" MATCHES \"\")\n        message(WARNING \"VERSION is not implemented yet\")\n    endif()\n\n    set(lcname \"\")\n    set(ucname \"\")\n    string(TOLOWER \"${args_NAME}\" lcname)\n    string(TOUPPER \"${args_NAME}\" ucname)\n\n    set(LIB_PACKAGE_FOLDER \"${EXTLIBS_PACKAGE_FOLDER}/${lcname}\")\n    win3rdParty(${ucname}\n                    $<args_VCID:VCID>\n                    VERBOSE     ${args_VERBOSE}\n                    TIMEOUT     ${args_TIMEOUT}\n                    DEFAULT_USE ${args_DEFAULT_USE}\n                    MSVC11 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC11}\"\n                    MSVC12 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC12}\"\n                    MSVC14 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC14}\" # TODO SV: make sure to build this library if required\n\t\t\t\t\tMSVC17 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC17}\"\n                    SET         ${args_SET}\n                    MULTI_SET   ${args_MULTI_SET}\n                )\n\t\t\t\n    # Add include/ directory\n    # and lib/ directories\n\n    # TODO SV: paths not matching with current hierarchy. example: libraw/libraw-0.17.1/include\n    # SR:\tThe link directories will also be used to lookup for dependency DLLs to copy in the install directory.\n    #\t\tSome libraries put the DLLs in the bin/ directory, so we include those.\n    file(GLOB subdirs RELATIVE ${LIB_PACKAGE_FOLDER} ${LIB_PACKAGE_FOLDER}/*)\n    set(dirlist \"\")\n    foreach(dir ${subdirs})\n        if(IS_DIRECTORY ${LIB_PACKAGE_FOLDER}/${dir})\n            # message(\"adding ${LIB_PACKAGE_FOLDER}/${dir}/include/ to the include directories\")\n            include_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/include/\")\n            # message(\"adding ${LIB_PACKAGE_FOLDER}/${dir}/lib[64] to the link directories\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/lib/\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/lib64/\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/bin/\")\n        endif()\n    endforeach()\n\nendfunction()\n\ninclude(FetchContent)\ninclude(git_describe)\ninclude(install_runtime)\n\nfunction(sibr_gitlibrary)\n    cmake_parse_arguments(args \"\" \"TARGET;GIT_REPOSITORY;GIT_TAG;ROOT_DIR;SOURCE_DIR\" \"INCLUDE_DIRS\" ${ARGN})\n    if(NOT args_TARGET)\n        message(FATAL \"Error on sibr_gitlibrary : please define your target name.\")\n        return()\n    endif()\n\n    if(NOT args_ROOT_DIR)\n        set(args_ROOT_DIR ${args_TARGET})\n    endif()\n\n    if(NOT args_SOURCE_DIR)\n        set(args_SOURCE_DIR ${args_TARGET})\n    endif()\n\n    if(args_GIT_REPOSITORY AND args_GIT_TAG)\n        if(EXISTS ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}/.git)\n            git_describe(\n                PATH ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}\n                GIT_URL SIBR_GITLIBRARY_URL\n                GIT_BRANCH SIBR_GITLIBRARY_BRANCH\n                GIT_COMMIT_HASH SIBR_GITLIBRARY_COMMIT_HASH\n                GIT_TAG SIBR_GITLIBRARY_TAG\n            )\n\n            if((SIBR_GITLIBRARY_URL STREQUAL args_GIT_REPOSITORY) AND\n                ((SIBR_GITLIBRARY_BRANCH STREQUAL args_GIT_TAG) OR\n                 (SIBR_GITLIBRARY_TAG STREQUAL args_GIT_TAG) OR\n                 (SIBR_GITLIBRARY_COMMIT_HASH STREQUAL args_GIT_TAG)))\n                message(STATUS \"Library ${args_TARGET} already available, skipping.\")\n                set(SIBR_GITLIBRARY_DECLARED ON)\n            else()\n                message(STATUS \"Adding library ${args_TARGET} from git...\")\n            endif()\n        endif()\n\n        FetchContent_Declare(${args_TARGET}\n            GIT_REPOSITORY \t${args_GIT_REPOSITORY}\n            GIT_TAG\t\t\t${args_GIT_TAG}\n            GIT_SHALLOW\t\tON\n            SOURCE_DIR \t\t${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}\n            SUBBUILD_DIR    ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/subbuild\n            BINARY_DIR      ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/build\n        )\n        FetchContent_GetProperties(${args_TARGET})\n        string(TOLOWER \"<name>\" lcTargetName)\n\n        if((NOT SIBR_GITLIBRARY_DECLARED) AND (NOT ${lcTargetName}_POPULATED))\n            message(STATUS \"Populating library ${args_TARGET}...\")\n            FetchContent_Populate(${args_TARGET} QUIET\n                GIT_REPOSITORY \t${args_GIT_REPOSITORY}\n                GIT_TAG\t\t\t${args_GIT_TAG}\n                SOURCE_DIR \t\t${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}\n                SUBBUILD_DIR    ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/subbuild\n                BINARY_DIR      ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/build\n            )\n        endif()\n\n        add_subdirectory(${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/build)\n\n        get_target_property(type ${args_TARGET} TYPE)\n        if(NOT (type STREQUAL \"INTERFACE_LIBRARY\"))\n            set_target_properties(${args_TARGET} PROPERTIES FOLDER \"extlibs\")\n\n            ibr_install_target(${args_TARGET}\n                COMPONENT   ${args_TARGET}_install  ## will create custom target to install only this project\n            )\n        endif()\n\n        list(APPEND ${args_TARGET}_INCLUDE_DIRS ${EXTLIBS_PACKAGE_FOLDER}/${args_ROOT_DIR})\n        list(APPEND ${args_TARGET}_INCLUDE_DIRS ${EXTLIBS_PACKAGE_FOLDER}/${args_ROOT_DIR}/${args_SOURCE_DIR})\n\n        foreach(args_INCLUDE_DIR ${args_INCLUDE_DIRS})\n            list(APPEND ${args_TARGET}_INCLUDE_DIRS ${EXTLIBS_PACKAGE_FOLDER}/${args_ROOT_DIR}/${args_SOURCE_DIR}/${args_INCLUDE_DIR})\n        endforeach()\n\n        include_directories(${${args_TARGET}_INCLUDE_DIRS})\n    else()\n        message(FATAL \"Error on sibr_gitlibrary for target ${args_TARGET}: missing git tag or git url.\")\n    endif()\nendfunction()"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/MSVCsetUserCommand.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(__MSVCsetUserCommand_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__MSVCsetUserCommand_cmake_INCLUDED__ ON)\nendif()\n\n## Allow to configure the Debugger settings of visual studio\n## Note: Using this command under linux doesn't affect anything\n## On run Debug Windows local : visual will try to load a specific COMMAND with ARGS in the provided WORKING_DIR\n##\n## usage:\n## MSVCsetUserCommand(\t<targetName>\n##    [COMMAND \t\t\t<myCustomAppToLaunch> | [ PATH <myCustomDirWhereIsDefaultTargetFileNameToLaunch> [FILE <myCustomExecFileToLaunch>] ] ]\n##    ARGS \t\t\t\t<associatedArguments>\n##    WORKING_DIR\t\t<whereStartTheProgram>\n## )\n##\n## Warning 1 : All arugments () must be passed under quotes\n## Warning 2 : WORKING_DIR path arg have to finish with remain slah '/'\n## Warning 3 : use COMMAND for external app OR PATH (optionaly with FILE) option(s) to set your built/installed/moved target\n##\n## Example 1:\n## include(MSVCsetUserCommand)\n## MSVCsetUserCommand(\tUnityRenderingPlugin\n## \t  COMMAND \t\t\t\"C:/Program Files (x86)/Unity/Editor/Unity.exe\"\n## \t  ARGS\t\t\t\t\"-force-opengl -projectPath \\\"${CMAKE_HOME_DIRECTORY}/UnityPlugins/RenderingPluginExample/UnityProject\\\"\"\n## \t  WORKING_DIR\t\t\"${CMAKE_HOME_DIRECTORY}/UnityPlugins/RenderingPluginExample/UnityProject\"\n## \t  VERBOSE\n## )\n##\n## Example 2:\n## include(MSVCsetUserCommand)\n## MSVCsetUserCommand(\tibrApp\n## \t  PATH \t\t\t\t\"C:/Program Files (x86)/workspace/IBR/install\"\n##\t  FILE\t\t\t\t\"ibrApp${CMAKE_EXECUTABLE_SUFFIX}\" ## this option line is optional since the target name didn't change between build and install step\n## \t  ARGS\t\t\t\t\"-path \\\"${CMAKE_HOME_DIRECTORY}/dataset\\\"\"\n## \t  WORKING_DIR\t\t\"${CMAKE_HOME_DIRECTORY}\"\n## \t  VERBOSE\n## )\n##\nfunction(MSVCsetUserCommand targetName)\n    cmake_parse_arguments(MSVCsuc \"VERBOSE\" \"PATH;FILE;COMMAND;ARGS;WORKING_DIR\" \"\" ${ARGN} )\n\n\t## If no arguments are given, do not create an unecessary .vcxproj.user file\n\tset(MSVCsuc_DEFAULT OFF)\n\n\tif(MSVCsuc_PATH AND MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n\tif(MSVCsuc_FILE AND MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n\tif(NOT MSVCsuc_COMMAND)\n\t\tif(MSVCsuc_PATH AND MSVCsuc_FILE)\n\t\t\tset(MSVCsuc_COMMAND \"${MSVCsuc_PATH}\\\\${MSVCsuc_FILE}\")\n\t\telseif(MSVCsuc_PATH)\n\t\t\tset(MSVCsuc_COMMAND \"${MSVCsuc_PATH}\\\\$(TargetFileName)\")\n\t\telse()\n\t\t\tset(MSVCsuc_COMMAND \"$(TargetPath)\") ## => $(TargetDir)\\$(TargetName)$(TargetExt)\n\t\tendif()\n\telseif(MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n        # NOTE: there was a typo here. there is an else if written after else statement\n        # changing the order of the else if statement\n\tif(MSVCsuc_WORKING_DIR)\n\t\tfile(TO_NATIVE_PATH ${MSVCsuc_WORKING_DIR} MSVCsuc_WORKING_DIR)\n\telseif(MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\telse()\n\t\tset(MSVCsuc_WORKING_DIR \"$(ProjectDir)\")\n\tendif()\n\n\tif(NOT MSVCsuc_ARGS)\n\t\tset(MSVCsuc_ARGS \"\")\n\telseif(MSVCsuc_DEFAULT)\n\t\tset(MSVCsuc_DEFAULT OFF)\n\tendif()\n\n\tif(MSVC10 OR (MSVC AND MSVC_VERSION GREATER 1600)) # 2010 or newer\n\n\t\tif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\t\t\tset(PLATEFORM_BITS x64)\n\t\telse()\n\t\t\tset(PLATEFORM_BITS Win32)\n\t\tendif()\n\n\t\tif(NOT MSVCsuc_DEFAULT AND PLATEFORM_BITS)\n\n\t\t\tfile(WRITE \"${CMAKE_CURRENT_BINARY_DIR}/${targetName}.vcxproj.user\"\n\t\t\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\n<Project ToolsVersion=\\\"4.0\\\" xmlns=\\\"http://schemas.microsoft.com/developer/msbuild/2003\\\">\n  <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='Release|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n\t<LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n  <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='Debug|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n    <LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n    <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='MinSizeRel|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n    <LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n    <PropertyGroup Condition=\\\"'$(Configuration)|$(Platform)'=='RelWithDebInfo|${PLATEFORM_BITS}'\\\">\n    <LocalDebuggerCommand>${MSVCsuc_COMMAND}</LocalDebuggerCommand>\n    <LocalDebuggerCommandArguments>${MSVCsuc_ARGS}</LocalDebuggerCommandArguments>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n    <LocalDebuggerWorkingDirectory>${MSVCsuc_WORKING_DIR}</LocalDebuggerWorkingDirectory>\n  </PropertyGroup>\n</Project>\"\n\t\t\t)\n\t\t\tif(MSVCsuc_VERBOSE)\n\t\t\t\tmessage(STATUS \"[MSVCsetUserCommand] Write ${CMAKE_CURRENT_BINARY_DIR}/${targetName}.vcxproj.user file\")\n\t\t\t\tmessage(STATUS \"   to execute ${MSVCsuc_COMMAND} ${MSVCsuc_ARGS}\")\n\t\t\t\tmessage(STATUS \"   from derectory ${MSVCsuc_WORKING_DIR}\")\n\t\t\t\tmessage(STATUS \"   on visual studio run debugger button\")\n\t\t\tendif()\n\n\t\telse()\n\t\t\tmessage(WARNING \"PLATEFORM_BITS is undefined...\")\n\t\tendif()\n\n\telse()\n\t\tif(MSVCsuc_VERBOSE)\n\t\t\tmessage(WARNING \"MSVCsetUserCommand is disable because too old MSVC is used (need MSVC10 2010 or newer)\")\n\t\tendif()\n\tendif()\n\nendfunction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/Modules/FindASSIMP.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Try to find the ASSIMP library\n## Once done this will define\n##\n##  \tASSIMP_FOUND \t\t- system has ASSIMP\n##  \tASSIMP_INCLUDE_DIR \t- The ASSIMP include directory\n##  \tASSIMP_LIBRARIES \t- The libraries needed to use ASSIMP\n##  \tASSIMP_CMD \t\t\t- the full path of ASSIMP executable\n##\tASSIMP_DYNAMIC_LIB\t- the Assimp dynamic lib (available only on windows as .dll file for the moment)\n##\n## Edited for using a bugfixed version of Assimp\n\nif(NOT ASSIMP_DIR)\n    set(ASSIMP_DIR \"$ENV{ASSIMP_DIR}\" CACHE PATH \"ASSIMP root directory\")\nendif()\nif(ASSIMP_DIR)\n\tfile(TO_CMAKE_PATH ${ASSIMP_DIR} ASSIMP_DIR)\nendif()\n\n\n## set the LIB POSTFIX to find in a right directory according to what kind of compiler we use (32/64bits)\nif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\tset(ASSIMP_SEARCH_LIB \"lib64\")\n\tset(ASSIMP_SEARCH_BIN \"bin64\")\n\tset(ASSIMP_SEARCH_LIB_PATHSUFFIXE \"x64\")\nelse()\n\tset(ASSIMP_SEARCH_LIB \"lib32\")\n\tset(ASSIMP_SEARCH_BIN \"bin32\")\n\tset(ASSIMP_SEARCH_LIB_PATHSUFFIXE \"x86\")\nendif()\n\nset(PROGRAMFILESx86 \"PROGRAMFILES(x86)\")\n\n\nFIND_PATH(ASSIMP_INCLUDE_DIR\n\tNAMES assimp/config.h\n\tPATHS\n\t\t${ASSIMP_DIR}\n\t\t## linux\n\t\t/usr\n\t\t/usr/local\n\t\t/opt/local\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/Assimp\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/Assimp\"\n\t\t\"$ENV{ProgramW6432}/Assimp\"\n\tPATH_SUFFIXES include\n)\n\n\nFIND_LIBRARY(ASSIMP_LIBRARY\n\tNAMES assimp-vc140-mt\n\tPATHS\n\t\t${ASSIMP_DIR}/${ASSIMP_SEARCH_LIB}\n\t\t${ASSIMP_DIR}/lib\n\t\t${ASSIMP_DIR}/lib64\n\t\t## linux\n\t\t/usr/${ASSIMP_SEARCH_LIB}\n\t\t/usr/local/${ASSIMP_SEARCH_LIB}\n\t\t/opt/local/${ASSIMP_SEARCH_LIB}\n\t\t/usr/lib\n\t\t/usr/local/lib\n\t\t/opt/local/lib\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/Assimp/${ASSIMP_SEARCH_LIB}\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/Assimp/${ASSIMP_SEARCH_LIB}\"\n\t\t\"$ENV{ProgramW6432}/Assimp/${ASSIMP_SEARCH_LIB}\"\n\t\t\"$ENV{PROGRAMFILES}/Assimp/lib\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/Assimp/lib\"\n\t\t\"$ENV{ProgramW6432}/Assimp/lib\"\n\tPATH_SUFFIXES ${ASSIMP_SEARCH_LIB_PATHSUFFIXE}\n)\nset(ASSIMP_LIBRARIES ${ASSIMP_LIBRARY})\n\n\nif(ASSIMP_LIBRARY)\n\tget_filename_component(ASSIMP_LIBRARY_DIR ${ASSIMP_LIBRARY} PATH)\n\tfile(GLOB ASSIMP_DYNAMIC_LIB \"${ASSIMP_LIBRARY_DIR}/assimp*.dll\")\n\tif(NOT ASSIMP_DYNAMIC_LIB)\n\t\tmessage(\"ASSIMP_DYNAMIC_LIB is missing... at ${ASSIMP_LIBRARY_DIR}\")\n\tendif()\n\tset(ASSIMP_DYNAMIC_LIB ${ASSIMP_DYNAMIC_LIB} CACHE PATH \"Windows dll location\")\nendif()\n\nMARK_AS_ADVANCED(ASSIMP_DYNAMIC_LIB ASSIMP_INCLUDE_DIR ASSIMP_LIBRARIES)\n\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(ASSIMP\n\tREQUIRED_VARS ASSIMP_INCLUDE_DIR ASSIMP_LIBRARIES\n\tFAIL_MESSAGE \"ASSIMP wasn't found correctly. Set ASSIMP_DIR to the root SDK installation directory.\"\n)\n\nif(NOT ASSIMP_FOUND)\n\tset(ASSIMP_DIR \"\" CACHE STRING \"Path to ASSIMP install directory\")\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/Modules/FindEmbree.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Important Note:\n## This is not an official Find*cmake. It has been written for searching through\n## a custom path (EMBREE_DIR) before checking elsewhere.\n##\n## FindEMBREE.cmake\n## Find EMBREE's includes and library\n##\n## This module defines :\n## \t[in] \tEMBREE_DIR, The base directory to search for EMBREE (as cmake var or env var)\n## \t[out] \tEMBREE_INCLUDE_DIR where to find EMBREE.h\n## \t[out] \tEMBREE_LIBRARIES, EMBREE_LIBRARY, libraries to link against to use EMBREE\n## \t[out] \tEMBREE_FOUND, If false, do not try to use EMBREE.\n##\n\n\nif(NOT EMBREE_DIR)\n    set(EMBREE_DIR \"$ENV{EMBREE_DIR}\" CACHE PATH \"EMBREE root directory\")\nendif()\nif(EMBREE_DIR)\n\tfile(TO_CMAKE_PATH ${EMBREE_DIR} EMBREE_DIR)\nendif()\n\n\n## set the LIB POSTFIX to find in a right directory according to what kind of compiler we use (32/64bits)\nif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\tset(EMBREE_SEARCH_LIB \"lib64\")\n\tset(EMBREE_SEARCH_BIN \"bin64\")\n\tset(EMBREE_SEARCH_LIB_PATHSUFFIXE \"x64\")\nelse()\n\tset(EMBREE_SEARCH_LIB \"lib32\")\n\tset(EMBREE_SEARCH_BIN \"bin32\")\n\tset(EMBREE_SEARCH_LIB_PATHSUFFIXE \"x86\")\nendif()\n\nset(PROGRAMFILESx86 \"PROGRAMFILES(x86)\")\n\nFIND_PATH(EMBREE_INCLUDE_DIR\n\tNAMES embree3/rtcore_geometry.h\n\tPATHS\n\t\t${EMBREE_DIR}\n\t\t## linux\n\t\t/usr\n\t\t/usr/local\n\t\t/opt/local\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/EMBREE\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/EMBREE\"\n\t\t\"$ENV{ProgramW6432}/EMBREE\"\n\tPATH_SUFFIXES include\n)\n\nFIND_LIBRARY(EMBREE_LIBRARY\n\tNAMES embree3\n\tPATHS\n\t\t${EMBREE_DIR}/${EMBREE_SEARCH_LIB}\n\t\t${EMBREE_DIR}/lib\n\t\t## linux\n\t\t/usr/${EMBREE_SEARCH_LIB}\n\t\t/usr/local/${EMBREE_SEARCH_LIB}\n\t\t/opt/local/${EMBREE_SEARCH_LIB}\n\t\t/usr/lib\n\t\t/usr/local/lib\n\t\t/opt/local/lib\n\t\t## windows\n\t\t\"$ENV{PROGRAMFILES}/EMBREE/${EMBREE_SEARCH_LIB}\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/EMBREE/${EMBREE_SEARCH_LIB}\"\n\t\t\"$ENV{ProgramW6432}/EMBREE/${EMBREE_SEARCH_LIB}\"\n\t\t\"$ENV{PROGRAMFILES}/EMBREE/lib\"\n\t\t\"$ENV{${PROGRAMFILESx86}}/EMBREE/lib\"\n\t\t\"$ENV{ProgramW6432}/EMBREE/lib\"\n\tPATH_SUFFIXES ${EMBREE_SEARCH_LIB_PATHSUFFIXE}\n)\nset(EMBREE_LIBRARIES ${EMBREE_LIBRARY})\n\nMARK_AS_ADVANCED(EMBREE_INCLUDE_DIR EMBREE_LIBRARIES)\n\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(EMBREE\n\tREQUIRED_VARS EMBREE_INCLUDE_DIR EMBREE_LIBRARIES\n\tFAIL_MESSAGE \"EMBREE wasn't found correctly. Set EMBREE_DIR to the root SDK installation directory.\"\n)\n\nif(NOT EMBREE_FOUND)\n\tset(EMBREE_DIR \"\" CACHE STRING \"Path to EMBREE install directory\")\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/Modules/FindFFmpeg.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Try to find the FFMPEG library\n## Once done this will define\n##\n##  \tFFMPEG_FOUND \t\t- system has FFmpeg\n##  \tFFMPEG_INCLUDE_DIR \t- The FFmpeg include directory\n##  \tFFMPEG_LIBRARIES \t- The libraries needed to use FFmpeg\n##\t\tFFMPEG_DYNAMIC_LIBS\t- DLLs for windows\n\n\nif(NOT FFMPEG_DIR)\n    set(FFMPEG_DIR \"$ENV{FFMPEG_DIR}\" CACHE PATH \"FFMPEG_DIR root directory\")\nendif()\n\nif(FFMPEG_DIR)\n\tfile(TO_CMAKE_PATH ${FFMPEG_DIR} FFMPEG_DIR)\nendif()\n\nMACRO(FFMPEG_FIND varname shortname headername)\n\t\n\t# Path to include dirs\n\tFIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS \n\t\tNAMES \"lib${shortname}/${headername}\" \n\t\tPATHS\n\t\t\t\"${FFMPEG_DIR}/include\" # modify this to adapt according to OS/compiler\t\t\t\n\t)\n\t\t\n\t#Add libraries\n\tIF(${FFMPEG_${varname}_INCLUDE_DIRS} STREQUAL \"FFMPEG_${varname}_INCLUDE_DIR-NOTFOUND\")\n\t\tMESSAGE(STATUS \"Can't find includes for ${shortname}...\")\n\tELSE()\n\t\tFIND_LIBRARY(FFMPEG_${varname}_LIBRARIES\n\t\t\tNAMES ${shortname}\n\t\t\tPATHS\n\t\t\t\t${FFMPEG_DIR}/lib\n\t\t)\n\n\t\t# set libraries and other variables\n\t\tSET(FFMPEG_${varname}_FOUND 1)\n\t\tSET(FFMPEG_${varname}_INCLUDE_DIRS ${FFMPEG_${varname}_INCLUDE_DIR})\n\t\tSET(FFMPEG_${varname}_LIBS ${FFMPEG_${varname}_LIBRARIES}) \n\tENDIF()\n ENDMACRO(FFMPEG_FIND)\n\n#Calls to ffmpeg_find to get  librarires ------------------------------\nFFMPEG_FIND(LIBAVFORMAT avformat avformat.h)\nFFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h)\nFFMPEG_FIND(LIBAVCODEC  avcodec  avcodec.h)\nFFMPEG_FIND(LIBAVUTIL   avutil   avutil.h)\nFFMPEG_FIND(LIBSWSCALE  swscale  swscale.h)\n \n# check if libs are found and set FFMPEG related variables\n#SET(FFMPEG_FOUND \"NO\")\nIF(FFMPEG_LIBAVFORMAT_FOUND \n\tAND FFMPEG_LIBAVDEVICE_FOUND \n\tAND FFMPEG_LIBAVCODEC_FOUND \n\tAND FFMPEG_LIBAVUTIL_FOUND \n\tAND FFMPEG_LIBSWSCALE_FOUND)\n \n\t# All ffmpeg libs are here\n    SET(FFMPEG_FOUND \"YES\")\n\tSET(FFMPEG_INCLUDE_DIR ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS})\n\tSET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS})\n\tSET(FFMPEG_LIBRARIES\n        ${FFMPEG_LIBAVFORMAT_LIBS}\n        ${FFMPEG_LIBAVDEVICE_LIBS}\n        ${FFMPEG_LIBAVCODEC_LIBS}\n        ${FFMPEG_LIBAVUTIL_LIBS}\n\t\t${FFMPEG_LIBSWSCALE_LIBS}\t)\n\t\t\n\t# add dynamic libraries\n\tif(WIN32)\n\t\tfile(GLOB FFMPEG_DYNAMIC_LIBS \"${FFMPEG_DIR}/bin/*.dll\")\n\t\tif(NOT FFMPEG_DYNAMIC_LIBS)\n\t\t\tmessage(\"FFMPEG_DYNAMIC_LIBS is missing...\")\n\tendif()\n\tset(FFMPEG_DYNAMIC_LIBS ${FFMPEG_DYNAMIC_LIBS} CACHE PATH \"Windows dll location\")\nendif()\n\t\n\tmark_as_advanced(FFMPEG_INCLUDE_DIR FFMPEG_LIBRARY_DIRS FFMPEG_LIBRARIES FFMPEG_DYNAMIC_LIBS)\nELSE ()\n    MESSAGE(STATUS \"Could not find FFMPEG\")\nENDIF()\n \n \nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG\n\tREQUIRED_VARS FFMPEG_INCLUDE_DIR FFMPEG_LIBRARIES\n\tFAIL_MESSAGE \"FFmpeg wasn't found correctly. Set FFMPEG_DIR to the root SDK installation directory.\"\n)\n\nif(NOT FFMPEG_FOUND)\n\tset(FFMPEG_DIR \"\" CACHE STRING \"Path to FFmpeg install directory\")\nendif()\n  \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/Win3rdParty.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## This file should be include and use only on WIN32 OS and once\n## It allow to auto check/download and use a preconfigured 3rdParty binaries for cmake usage\n## It use the downloadAndExtractZipFile cmake module to work.\n##\nif(__Win3rdParty_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__Win3rdParty_cmake_INCLUDED__ ON)\nendif()\n\n\n##\n## To be sure to reset an empty cached variable but keep any other kind of variables\n##\n## Usage:\n## check_cached_var(<var> <resetedCachedValue> <cacheType> <cacheDoc> [FORCE])\n##\n## <var> is the cached cmake variable you need to reset\n## <resetedCachedValue> is the new default value of the reseted cached cmake variable\n## <cacheType> is the kind of GUI cache input can be : FILEPATH; PATH; STRING or BOOL\n## <cacheDoc> is the associated GUI cache input documentation display in the GUI\n## FORCE option could be use to reset a cached variable even if it is not empty.\n##\nmacro(check_cached_var var resetedCachedValue cacheType cacheDoc)\n    # message(STATUS \"inside check_cached_var macro. argn=${ARGN}\")\n    cmake_parse_arguments(ccv \"FORCE\" \"\" \"\" ${ARGN})\n\n    if(ccv_FORCE)\n        set(FORCE FORCE)\n    else()\n        set(FORCE )\n    endif()\n\n    if(NOT ${var} OR ccv_FORCE)\n        unset(${var} CACHE)\n        # message(STATUS \"setting new cache value. var ${var} = ${resetedCachedValue}\")\n        set(${var}\t\"${resetedCachedValue}\" CACHE ${cacheType} \"${cacheDoc}\" ${FORCE})\n    endif()\nendmacro()\n\n\n##\n## Win3rdParty function allow to specify a directory which contain all necessary windows dependenties.\n## By uploading 3rdParty directory (which contain dependencies, *.lib, *.dll... for a specific version of compiler) onto Gforge file tab,\n## you get back an URL of download you can give to this function with a directory name. So you can provide multiple 3rdParty version of same dependencies (MSVC11, MSVC12...).\n## By providing a prefix to this function, you allow to use different kind of 3rdParty which can be handled by CMAKE OPTIONS depending on what your framework need for example.\n##\n## Usage 1:\n##    Win3rdParty(<prefix> MSVC<XX> <DirName> <URL>\n##\t\t\t\t\t[MSVC<XX> <DirName> <URL>] [...]\n##\t\t\t\t\t[VCID] [DEFAULT_USE] [VERBOSE] )\n##\n## * <prefix> allow to identify which 3rdParty you process (prefix name)\n## * MSVC<XX> flag could be MSVC11 or MSVC12 (any element of the MSVC_VERSIONS_LIST) and refer to a 3rdParty compiler with :\n##   * <DirName> which will be the local pathName of the downloaded 3rdParty : relative to CMAKE_BINARY_DIR\n##   * <URL> which is the link location of the 3rdParty zip\n## * VCID flag will make available a cache variable ${prefix}_WIN3RDPARTY_VCID\n## * DEFAULT_USE flag [ON|OFF] may be used to set default value of cmake cached variable : <prefix>_WIN3RDPARTY_USE [default to ON]\n##\n## WARNING:\n## This function define CACHE variables you can use after :\n## * ${prefix}_WIN3RDPARTY_USE : allow to check/downloaded win3rdParty dir (it will force the cached variables for this dependency folder generally <prefix>_DIR>)\n## * ${prefix}_WIN3RDPARTY_DIR : where is your local win3rdParty dir (the PATH)\n## * ${prefix}_WIN3RDPARTY_VCID : [if VCID flag is used] the MSVC id (commonly used to prefix/suffix library name, see boost or CGAL)\n##\n## If you want to add a win3rdParty version, please:\n## 1- build dependencies on your local side with the compiler you want\n## 2- build your own zip with your built dependencies\n## 3- upload it (onto the forge where the project is stored) and copy the link location in order to use it for this function\n## 4- if you just introduced a new MSVC version, add it to the MSVC_VERSIONS_LIST bellow\n##\n## In a second pass, you can also use this function to set necessary cmake cached variables in order to let cmake find packages of these 3rdParty.\n##\n## Usage 2:\n##    win3rdParty(<prefix> [VERBOSE] MULTI_SET|SET\n##          CHECK_CACHED_VAR <cmakeVar> <cmakeCacheType> [LIST] <cmakeValue> [DOC <stringToolTips>]\n##        [ CHECK_CACHED_VAR <cmakeVar> <cmakeCacheType> [LIST] <cmakeValue> [DOC <stringToolTips>] ] [...]\n##\n## * MULTI_SET or SET flags are used to tell cmake that all next arguments will use repeated flags with differents entries (SET mean we will provide only one set of arguments, without repetition)\n## * CHECK_CACHED_VAR are the repeated flag which contain differents entries\n##      * <cmakeVar> is the cmake variable you want to be cached for the project\n##      * <cmakeCacheType> is the kind of cmake variable (couble be: FILEPATH; PATH; STRING or BOOL) => see check_cached_var.\n##      * LIST optional flag could be used with CHECK_CACHED_VAR when <cmakeCacheType> = STRING. It allow to handle multiple STRINGS value list.\n##      * <cmakeValue> is the value of the variable (if FILEPATH, PATH or STRING: use quotes, if BOOL : use ON/OFF)\n##      * DOC optional flag is used to have a tooltips info about this new cmake variable entry into the GUI (use quotes).\n##\n## Full example 1 :\n##    win3rdParty(COMMON MSVC11 \"win3rdParty-MSVC11\" \"https://path.to/an.archive.7z\"\n##                SET CHECK_CACHED_VAR SuiteSparse_DIR PATH \"SuiteSparse-4.2.1\" DOC \"default empty doc\"\n##    )\n##\n## WARNING:\n## For the 2nd usage (with MULTI_SET), if you planned to set some CACHED_VAR using/composed by ${prefix}_WIN3RDPARTY_* just set in this macro (usage 1),\n## then (due to the not yet existing var) you will need to call this function 2 times :\n## One for the 1st usage (downloading of the current compiler 3rdParty).\n## One for the MLUTI_SET flag which will use existsing ${prefix}_WIN3RDPARTY_* cached var.\n##\n## Full example 2 :\n##    win3rdParty(COMMON MSVC11    \"win3rdParty-MSVC11\" \"https://path.to/an.archive.7z\")\n##    win3rdParty(COMMON MULTI_SET\n##       CHECK_CACHED_VAR CGAL_INCLUDE_DIR  PATH \"CGAL-4.3/include\" DOC \"default empty doc\"\n##       CHECK_CACHED_VAR CGAL_LIBRARIES\tSTRING LIST \"debug;CGAL-4.3/lib${LIB_POSTFIX}/CGAL-${WIN3RDPARTY_COMMON_VCID}-mt-gd-4.3.lib;optimized;CGAL-4.3/lib${LIB_POSTFIX}/CGAL-${WIN3RDPARTY_COMMON_VCID}-mt-4.3.lib\"\n##\n##\n## WARNING: This function use internaly :\n## * downloadAndExtractZipFile.cmake\n## * parse_arguments_multi.cmake\n## * check_cached_var macro\n##\nfunction(win3rdParty prefix )\n\n    # ARGV: list of all arguments given to the macro/function\n    # ARGN: list of remaining arguments\n\n    if(NOT WIN32)\n        return()\n    endif()\n\n    ## set the handled version of MSVC\n    ## if you plan to add a win3rdParty dir to download with a new MSVC version: build the win3rdParty dir and add the MSCV entry here.\n    set(MSVC_VERSIONS_LIST \"MSVC17;MSVC11;MSVC12;MSVC14\")\n\n    #include(CMakeParseArguments)   # CMakeParseArguments is obsolete since cmake 3.5\n    # cmake_parse_arguments (<prefix> <options> <one_value_keywords> <multi_value_keywords> args)\n    # <options> : options (flags) pass to the macro\n    # <one_value_keywords> : options that neeed a value\n    # <multi_value_keywords> : options that neeed more than one value\n    cmake_parse_arguments(w3p \"VCID\" \"VERBOSE;TIMEOUT;DEFAULT_USE\" \"${MSVC_VERSIONS_LIST};MULTI_SET;SET\" ${ARGN})\n\n    # message(STATUS \"value of w3p_VCID = ${w3p_VCID}\")\n    # message(STATUS \"value of w3p_VERBOSE = ${w3p_VERBOSE}\")\n    # message(STATUS \"value of w3p_TIMEOUT = ${w3p_TIMEOUT}\")\n    # message(STATUS \"value of w3p_DEFAULT_USE = ${w3p_DEFAULT_USE}\")\n\n    # foreach (loop_var ${MSVC_VERSIONS_LIST})\n            # message(STATUS \"value of w3p_${loop_var} = ${w3p_${loop_var}}\")\n    # endforeach(loop_var)\n\n    # message(STATUS \"value of w3p_MULTI_SET = ${w3p_MULTI_SET}\")\n    # message(STATUS \"value of w3p_SET = ${w3p_SET}\")\n\n    # message(\"values for MSVC = ${w3p_MSVC14}\")\n\n    if(NOT w3p_TIMEOUT)\n        set(w3p_TIMEOUT 300)\n    endif()\n\n    if(NOT DEFINED w3p_DEFAULT_USE)\n        set(w3p_DEFAULT_USE ON)\n    endif()\n\t\n\n    ## 1st use (check/update|download) :\n    set(${prefix}_WIN3RDPARTY_USE ${w3p_DEFAULT_USE} CACHE BOOL \"Use required 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist\")\n\n\n    ## We want to test if each version of MSVC was filled by the function (see associated parameters)\n    ## As CMake is running only for one version of MSVC, if that MSVC version was filled, we get back associated parameters,\n    ## otherwise we can't use the downloadAndExtractZipFile with win3rdParty.\n    set(enableWin3rdParty OFF)\n\t\n\tforeach(MSVC_VER ${MSVC_VERSIONS_LIST})\n\t\tif(${MSVC_VER} AND w3p_${MSVC_VER} OR ${MSVC_TOOLSET_VERSION} EQUAL 143 AND ${MSVC_VER} STREQUAL \"MSVC17\") \n\t\t\tlist(LENGTH w3p_${MSVC_VER} count)\n\t\t\tif(\"${count}\" LESS \"2\")\n\t\t\t\t#message(WARNING \"You are using ${MSVC_VER} with ${prefix}_WIN3RDPARTY_USE=${${prefix}_WIN3RDPARTY_USE}, but win3rdParty function isn't filled for ${MSVC_VER}!\")\n\t\t\telse()\n\t\t\t\tlist(GET w3p_${MSVC_VER} 0 Win3rdPartyName)\n\t\t\t\tlist(GET w3p_${MSVC_VER} 1 Win3rdPartyUrl)\n\t\t\t\tif(w3p_VCID)\n\t\t\t\t\t## try to get the VcId of MSVC. See also MSVC_VERSION cmake var in the doc.\n\t\t\t\t\tstring(REGEX REPLACE \"MS([A-Za-z_0-9-]+)\" \"\\\\1\" vcId ${MSVC_VER})\n\t\t\t\t\tstring(TOLOWER ${vcId} vcId)\n\t\t\t\t\tset(${prefix}_WIN3RDPARTY_VCID \"${vcId}0\" CACHE STRING \"the MSVC id (commonly used to prefix/suffix library name, see boost or CGAL)\")\n\t\t\t\t\tmark_as_advanced(${prefix}_WIN3RDPARTY_VCID)\n\t\t\t\tendif()\n\t\t\t\tset(enableWin3rdParty ON)\n\t\t\t\tset(suffixCompilerID ${MSVC_VER})\n\t\t\t\tbreak()\n\t\t\tendif()\n\t\tendif()\n\tendforeach()\n    ## If previous step succeed to get MSVC dirname and URL of the current MSVC version, use it to auto download/update the win3rdParty dir\n    if(enableWin3rdParty AND ${prefix}_WIN3RDPARTY_USE)\n\n        if(IS_ABSOLUTE \"${Win3rdPartyName}\")\n        else()\n            set(Win3rdPartyName \"${CMAKE_BINARY_DIR}/${Win3rdPartyName}\")\n        endif()\n\n        if(NOT EXISTS \"${Win3rdPartyName}\")\n            file(MAKE_DIRECTORY ${Win3rdPartyName})\n        endif()\n\n        include(downloadAndExtractZipFile)\n        downloadAndExtractZipFile(  \"${Win3rdPartyUrl}\"                             ## URL link location\n                                    \"Win3rdParty-${prefix}-${suffixCompilerID}.7z\"  ## where download it: relative path, so default to CMAKE_BINARY_DIR\n                                    \"${Win3rdPartyName}\"                            ## where extract it : fullPath (default relative to CMAKE_BINARY_DIR)\n            CHECK_DIRTY_URL \"${Win3rdPartyName}/Win3rdPartyUrl\"                     ## last downloaded url file : fullPath (default relative to CMAKE_BINARY_DIR)\n            TIMEOUT ${w3p_TIMEOUT}\n            VERBOSE ${w3p_VERBOSE}\n        )\n        file(GLOB checkDl \"${Win3rdPartyName}/*\")\n        list(LENGTH checkDl checkDlCount)\n        if(\"${checkDlCount}\" GREATER \"1\")\n        else()\n            message(\"The downloadAndExtractZipFile didn't work...?\")\n            set(enableWin3rdParty OFF)\n        endif()\n    endif()\n\n    ## Try to auto set ${prefix}_WIN3RDPARTY_DIR or let user set it manually\n    set(${prefix}_WIN3RDPARTY_DIR \"\" CACHE PATH \"windows ${Win3rdPartyName} dir to ${prefix} dependencies of the project\")\n\n    if(NOT ${prefix}_WIN3RDPARTY_DIR AND ${prefix}_WIN3RDPARTY_USE)\n        if(EXISTS \"${Win3rdPartyName}\")\n            unset(${prefix}_WIN3RDPARTY_DIR CACHE)\n            set(${prefix}_WIN3RDPARTY_DIR \"${Win3rdPartyName}\" CACHE PATH \"dir to ${prefix} dependencies of the project\")\n        endif()\n    endif()\n\n    if(EXISTS ${${prefix}_WIN3RDPARTY_DIR})\n        message(STATUS \"Found a 3rdParty ${prefix} dir : ${${prefix}_WIN3RDPARTY_DIR}.\")\n        set(enableWin3rdParty ON)\n    elseif(${prefix}_WIN3RDPARTY_USE)\n        message(WARNING \"${prefix}_WIN3RDPARTY_USE=${${prefix}_WIN3RDPARTY_USE} but ${prefix}_WIN3RDPARTY_DIR=${${prefix}_WIN3RDPARTY_DIR}.\")\n        set(enableWin3rdParty OFF)\n    endif()\n\n    ## Final check\n    if(NOT enableWin3rdParty)\n        message(\"Disable ${prefix}_WIN3RDPARTY_USE (cmake cached var will be not set), due to a win3rdParty problem.\")\n        message(\"You still can set ${prefix}_WIN3RDPARTY_DIR to an already downloaded Win3rdParty directory location.\")\n        set(${prefix}_WIN3RDPARTY_USE OFF CACHE BOOL \"Use required 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist\" FORCE)\n    endif()\n\n    ## 2nd use : handle multi values args to set cached cmake variables in order to ease the next find_package call\n    if(${prefix}_WIN3RDPARTY_USE AND ${prefix}_WIN3RDPARTY_DIR)\n        if(w3p_VERBOSE)\n            message(STATUS \"Try to set cmake cached variables for ${prefix} required libraries directly from : ${${prefix}_WIN3RDPARTY_DIR}.\")\n        endif()\n\n        include(parse_arguments_multi)\n        # message (STATUS \"before defining an override of parse_arguments_multi_function\")\n        function(parse_arguments_multi_function ) ## overloaded function to handle all CHECK_CACHED_VAR values list (see: parse_arguments_multi)\n            # message(STATUS \"inside overloaded parse_arguments_multi_function defined in Win3rdParty.cmake\")\n            # message(STATUS ${ARGN})\n            ## we know the function take 3 args : var cacheType resetedCachedValue (see check_cached_var)\n            cmake_parse_arguments(pamf \"\" \"DOC\" \"LIST\" ${ARGN})\n\n            ## var and cacheType are mandatory (with the resetedCachedValue)\n            set(var         ${ARGV0})\n            set(cacheType   ${ARGV1})\n            # message(STATUS \"var=${var} and cacheType=${cacheType} list=${pamf_LIST}\")\n            if(pamf_DOC)\n                set(cacheDoc    ${pamf_DOC})\n            else()\n                set(cacheDoc    \"\")\n            endif()\n            if(pamf_LIST)\n                set(value ${pamf_LIST})\n            else()\n                # message(\"USING ARGV2 with value ${ARGV2}\")\n                set(value ${ARGV2})\n            endif()\n            # message(\"inside override function in Win3rdparty.cmake value+ ${value}\")\n            if(\"${cacheType}\" MATCHES \"PATH\" AND EXISTS \"${${prefix}_WIN3RDPARTY_DIR}/${value}\")\n                # message(\"math with path\")\n                set(resetedCachedValue \"${${prefix}_WIN3RDPARTY_DIR}/${value}\") ## path relative to ${prefix}_WIN3RDPARTY_DIR\n            elseif (\"${cacheType}\" MATCHES \"PATH\" AND EXISTS \"${${prefix}_WIN3RDPARTY_DIR}\")\n                set(resetedCachedValue \"${${prefix}_WIN3RDPARTY_DIR}\") ## path relative to ${prefix}_WIN3RDPARTY_DIR\n            elseif(\"${cacheType}\" MATCHES \"STRING\")\n                foreach(var IN LISTS value)\n                    if(EXISTS \"${${prefix}_WIN3RDPARTY_DIR}/${var}\")\n                        list(APPEND resetedCachedValue \"${${prefix}_WIN3RDPARTY_DIR}/${var}\") ## string item of the string list is a path => make relative to ${prefix}_WIN3RDPARTY_DIR\n                    else()\n                        list(APPEND resetedCachedValue ${var}) ## string item of the string list is not an existing path => simply use the item\n                    endif()\n                endforeach()\n            else()\n                set(resetedCachedValue \"${value}\") ## could be a BOOL or a STRING\n            endif()\n\n            ## call our macro to reset cmake cache variable if empty\n            check_cached_var(${var} \"${resetedCachedValue}\" ${cacheType} \"${cacheDoc}\" FORCE)\n\n        endfunction()\n        # message (STATUS \"after defining an override of parse_arguments_multi_function\")\n\n        if(w3p_MULTI_SET)\n            parse_arguments_multi(CHECK_CACHED_VAR w3p_MULTI_SET ${w3p_MULTI_SET}) ## internaly will call our overloaded parse_arguments_multi_function\n        elseif(w3p_SET)\n            # message(\"calling set version of parse_arguments_multi with w3p_set = ${w3p_SET}\")\n            parse_arguments_multi(CHECK_CACHED_VAR w3p_SET ${w3p_SET})\n        endif()\n\n    endif()\n\nendfunction()\n\n## cmake variables introspection to globally activate/deactivate ${prefix}_WIN3RDPARTY_USE\n## This \"one shot\" call (only one for the next cmake configure) will automatically then reset the global variable WIN3RDPARTY_USE to UserDefined (do nothing).\n## use (call it) before and after the call of all your win3rdParty functions\nfunction(Win3rdPartyGlobalCacheAction )\n\tset(WIN3RDPARTY_USE \"UserDefined\" CACHE STRING \"Choose how to handle all cmake cached *_WIN3RDPARTY_USE for the next configure.\\nCould be:\\nUserDefined [default]\\nActivateAll\\nDesactivateAll\" )\n\tset_property(CACHE WIN3RDPARTY_USE PROPERTY STRINGS \"UserDefined;ActivateAll;DesactivateAll\" )\n\tif(${WIN3RDPARTY_USE} MATCHES \"UserDefined\")\n\telse()\n\t\tif(${WIN3RDPARTY_USE} MATCHES \"ActivateAll\")\n\t\t\tset(win3rdPvalue ON)\n\t\telseif(${WIN3RDPARTY_USE} MATCHES \"DesactivateAll\")\n\t\t\tset(win3rdPvalue OFF)\n\t\tendif()\n\t\tget_cmake_property(_variableNames CACHE_VARIABLES)\n\t\tforeach (_variableName ${_variableNames})\n\t\t\tstring(REGEX MATCH \t\"[A-Za-z_0-9-]+_WIN3RDPARTY_USE\" win3rdpartyUseCacheVar ${_variableName})\n\t\t\tif(win3rdpartyUseCacheVar)\n\t\t\t\tstring(REGEX REPLACE \"([A-Za-z_0-9-]+_WIN3RDPARTY_USE)\" \"\\\\1\" win3rdpartyUseCacheVar ${_variableName})\n\t\t\t\tset(${win3rdpartyUseCacheVar} ${win3rdPvalue} CACHE BOOL \"Use required 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist\" FORCE)\n\t\t\t\tmessage(STATUS \"${win3rdpartyUseCacheVar} cached variable set to ${win3rdPvalue}.\")\n\t\t\tendif()\n\t\tendforeach()\n\t\tset(WIN3RDPARTY_USE \"UserDefined\" CACHE STRING \"Choose how to handle all cmake cached *_WIN3RDPARTY_USE for the next configure.\\nCould be:\\nUserDefined [default]\\nActivateAll\\nDesactivateAll\" FORCE)\n\t\tmessage(STATUS \"reset WIN3RDPARTY_USE to UserDefined.\")\n\tendif()\n\tmark_as_advanced(WIN3RDPARTY_USE)\nendfunction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/cmake_policies.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(__set_policies_INCLUDED__)\n\treturn()\nelse()\n\tset(__set_policies_INCLUDED__ ON)\nendif()\n\nmacro(setPolicies)\n\t# No more policies to enforce\nendmacro()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/dependencies.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## Included once for all sub project.\n## It contain the whole cmake instructions to find necessary common dependencies.\n## 3rdParty (provided by sibr_addlibrary win3rdParty or from external packages) are then available in cmake sub projects.\n##\n## Do not include this file more than once but you can modify it to fit to your own project.\n## So please, read it carefully because you can use on of these dependencies for your project or appen new one.\n##\n## As it is included after camke options, you can use conditional if(<CMAKE_PROJ_OPT>)/endif() to encapsulate your 3rdParty.\n##\n\n## win3rdParty function allowing to auto check/download/update binaries dependencies for current windows compiler\n## Please open this file in order to get more documentation and usage examples.\ninclude(Win3rdParty)\n\ninclude(sibr_library)\n\nWin3rdPartyGlobalCacheAction()\n\nfind_package(OpenGL REQUIRED)\n\n############\n## Find GLEW\n############\nif (MSVC11 OR MSVC12)\n    set(glew_multiset_arguments \n            CHECK_CACHED_VAR GLEW_INCLUDE_DIR\t    PATH \"glew-1.10.0/include\" DOC \"default empty doc\"\n            CHECK_CACHED_VAR GLEW_LIBRARIES         STRING LIST \"debug;glew-1.10.0/${LIB_BUILT_DIR}/glew32d.lib;optimized;glew-1.10.0/${LIB_BUILT_DIR}/glew32.lib\" DOC \"default empty doc\"\n        )\nelseif (MSVC14)\n    set(glew_multiset_arguments \n            CHECK_CACHED_VAR GLEW_INCLUDE_DIR\t    PATH \"glew-2.0.0/include\" DOC \"default empty doc\"\n            CHECK_CACHED_VAR GLEW_SHARED_LIBRARY_RELEASE       PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32.lib\"\n            CHECK_CACHED_VAR GLEW_STATIC_LIBRARY_RELEASE       PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32s.lib\"\n            CHECK_CACHED_VAR GLEW_SHARED_LIBRARY_DEBUG         PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32d.lib\"\n            CHECK_CACHED_VAR GLEW_STATIC_LIBRARY_DEBUG         PATH \"glew-2.0.0/${LIB_BUILT_DIR}/glew32sd.lib\"\n        )\nelse ()\n    message(\"There is no provided GLEW library for your version of MSVC\")\nendif()\nsibr_addlibrary(NAME GLEW #VERBOSE ON\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/glew-1.10.0.7z\"\n    MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/glew-1.10.0.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/glew-2.0.0.7z\"        # using recompiled version of glew\n    MULTI_SET ${glew_multiset_arguments}\n)\nset(GLEW_VERBOSE ON)\nFIND_PACKAGE(GLEW REQUIRED)\nIF(GLEW_FOUND)\n    INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIR})\nELSE(GLEW_FOUND)\n    MESSAGE(\"GLEW not found. Set GLEW_DIR to base directory of GLEW.\")\nENDIF(GLEW_FOUND)\n\n\n##############\n## Find ASSIMP\n##############\nif (MSVC11 OR MSVC12)\n    set(assimp_set_arguments \n        CHECK_CACHED_VAR ASSIMP_DIR PATH \"Assimp_3.1_fix\"\n    )\nelseif (MSVC14)\n    set(assimp_set_arguments \n        CHECK_CACHED_VAR ASSIMP_DIR PATH \"Assimp-4.1.0\"\n    )\nelse ()\n    message(\"There is no provided ASSIMP library for your version of MSVC\")\nendif()\n\nsibr_addlibrary(NAME ASSIMP #VERBOSE ON\n        MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/Assimp_3.1_fix.7z\"\n        MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/Assimp_3.1_fix.7z\"\n        MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/Assimp-4.1.0.7z\"\n        MULTI_SET\n            ${assimp_set_arguments}\n)\n\nfind_package(ASSIMP REQUIRED)\ninclude_directories(${ASSIMP_INCLUDE_DIR})\n\n################\n## Find FFMPEG\n################\nsibr_addlibrary(NAME FFMPEG\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/ffmpeg.zip\"\n    MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/ffmpeg.zip\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/ffmpeg-4.0.2-win64-win3rdParty.7z\"\n    SET CHECK_CACHED_VAR FFMPEG_DIR PATH ${FFMPEG_WIN3RDPARTY_DIR}\n)\nfind_package(FFMPEG QUIET)\ninclude_directories(${FFMPEG_INCLUDE_DIR})\n\n###################\n## Find embree3\n###################\nsibr_addlibrary(\n    NAME embree3\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/embree2.7.0.x64.windows.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/embree-3.6.1.x64.vc14.windows.7z\"     # TODO SV: provide a valid version if required\n)\n\n###################\n## Find eigen3\n###################\nsibr_addlibrary(\n\tNAME eigen3\n\t#MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/eigen-eigen-dc6cfdf9bcec.7z\"\n\t#MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/eigen-eigen-dc6cfdf9bcec.7z\"    # TODO SV: provide a valid version if required\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/eigen3.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/eigen3.7z\"\n    SET CHECK_CACHED_VAR eigen3_DIR PATH \"eigen/share/eigen3/cmake\"\n)\ninclude_directories(/usr/include/eigen3)\nadd_definitions(-DEIGEN_INITIALIZE_MATRICES_BY_ZERO)\n\n#############\n## Find Boost\n#############\nset(Boost_REQUIRED_COMPONENTS \"system;chrono;filesystem;date_time\" CACHE INTERNAL \"Boost Required Components\")\n\nif (WIN32)\n    # boost multiset arguments\n    if (MSVC11 OR MSVC12)\n        set(boost_multiset_arguments \n                CHECK_CACHED_VAR BOOST_ROOT                 PATH \"boost_1_55_0\"\n                CHECK_CACHED_VAR BOOST_INCLUDEDIR \t\t    PATH \"boost_1_55_0\"\n                CHECK_CACHED_VAR BOOST_LIBRARYDIR \t\t    PATH \"boost_1_55_0/${LIB_BUILT_DIR}\"\n                #CHECK_CACHED_VAR Boost_COMPILER             STRING \"-${Boost_WIN3RDPARTY_VCID}\" DOC \"vcid (eg: -vc110 for MSVC11)\"\n                CHECK_CACHED_VAR Boost_COMPILER             STRING \"-vc110\" DOC \"vcid (eg: -vc110 for MSVC11)\" # NOTE: if it doesnt work, uncomment this option and set the right value for VisualC id\n            )\n    elseif (MSVC14)\n        set(boost_multiset_arguments \n                CHECK_CACHED_VAR BOOST_ROOT                 PATH \"boost-1.71\"\n                CHECK_CACHED_VAR BOOST_INCLUDEDIR \t\t    PATH \"boost-1.71\"\n                CHECK_CACHED_VAR BOOST_LIBRARYDIR \t\t    PATH \"boost-1.71/${LIB_BUILT_DIR}\"\n                CHECK_CACHED_VAR Boost_COMPILER             STRING \"-vc141\" DOC \"vcid (eg: -vc110 for MSVC11)\" # NOTE: if it doesnt work, uncomment this option and set the right value for VisualC id\n            )\n        \n        option(BOOST_MINIMAL_VERSION \"Only get minimal Boost dependencies\" ON)\n\n        if(${BOOST_MINIMAL_VERSION})\n            set(BOOST_MSVC14_ZIP \"boost-1.71-ibr-minimal.7z\")\n        else()\n            set(BOOST_MSVC14_ZIP \"boost-1.71.7z\")\n        endif()\n    else ()\n        message(\"There is no provided Boost library for your version of MSVC\")\n    endif()\n\n    sibr_addlibrary(NAME Boost VCID TIMEOUT 600 #VERBOSE ON\n        MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/boost_1_55_0.7z\"\n        MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC11-splitted%20version/boost_1_55_0.7z\"\n        MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/${BOOST_MSVC14_ZIP}\"    # boost compatible with msvc14\n        MULTI_SET ${boost_multiset_arguments}\n            CHECK_CACHED_VAR Boost_NO_SYSTEM_PATHS      BOOL ON DOC \"Set to ON to disable searching in locations not specified by these boost cached hint variables\"\n            CHECK_CACHED_VAR Boost_NO_BOOST_CMAKE       BOOL ON DOC \"Set to ON to disable the search for boost-cmake (package cmake config file if boost was built with cmake)\"\n    )\n    if(NOT Boost_COMPILER AND Boost_WIN3RDPARTY_USE)\n        message(WARNING \"Boost_COMPILER is not set and it's needed.\")\n    endif()\nendif()\n\nfind_package(Boost 1.71.0 REQUIRED COMPONENTS ${Boost_REQUIRED_COMPONENTS})\n\nif(WIN32)\n\tadd_compile_options(\"$<$<COMPILE_LANGUAGE:CXX>:/EHsc>\")\n    #add_definitions(/EHsc)\nendif()\n\nif(Boost_LIB_DIAGNOSTIC_DEFINITIONS)\n    add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS})\nendif()\n\n#if(WIN32)\n    add_definitions(-DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB)\n#endif()\n\ninclude_directories(${BOOST_INCLUDEDIR} ${Boost_INCLUDE_DIRS})\nlink_directories(${BOOST_LIBRARYDIR} ${Boost_LIBRARY_DIRS})\n\n\n##############\n## Find OpenMP\n##############\nfind_package(OpenMP)\n\nsibr_addlibrary(\n    NAME NativeFileDialog\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/nfd.7z\"  \n)\n\n##############\n## Find OpenCV\n##############\nif (WIN32)\n\tif (${MSVC_TOOLSET_VERSION} EQUAL 143)\n\t\tMESSAGE(\"SPECIAL OPENCV HANDLING\")\n\t\tset(opencv_set_arguments \n\t\tCHECK_CACHED_VAR OpenCV_DIR PATH \"install\" ## see OpenCVConfig.cmake\n\t    )\n\telseif (MSVC11 OR MSVC12)\n\t    set(opencv_set_arguments \n\t\tCHECK_CACHED_VAR OpenCV_DIR PATH \"opencv/build\" ## see OpenCVConfig.cmake\n\t    )\n\telseif (MSVC14)\n\t    set(opencv_set_arguments \n\t\tCHECK_CACHED_VAR OpenCV_DIR PATH \"opencv-4.5.0/build\" ## see OpenCVConfig.cmake\n\t    )\n\telse ()\n\t    message(\"There is no provided OpenCV library for your compiler, relying on find_package to find it\")\n\tendif()\nelse()\n\t    message(\"There is no provided OpenCV library for your compiler, relying on find_package to find it\")\nendif()\n\nsibr_addlibrary(NAME OpenCV #VERBOSE ON\n        MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv.7z\"\n        MSVC12 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv.7z\"\n        MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv-4.5.0.7z\"    # opencv compatible with msvc14 and with contribs\n        MSVC17 \"https://repo-sam.inria.fr/fungraph/dependencies/sibr/~0.9/opencv4-8.7z\" \n\t\tSET ${opencv_set_arguments}\n    )\nfind_package(OpenCV REQUIRED) ## Use directly the OpenCVConfig.cmake provided\n\n    ##https://stackoverflow.com/questions/24262081/cmake-relwithdebinfo-links-to-debug-libs\nset_target_properties(${OpenCV_LIBS} PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE)\n\nadd_definitions(-DOPENCV_TRAITS_ENABLE_DEPRECATED) \n\nif(OpenCV_INCLUDE_DIRS)\n    foreach(inc ${OpenCV_INCLUDE_DIRS})\n        if(NOT EXISTS ${inc})\n            set(OpenCV_INCLUDE_DIR \"\" CACHE PATH \"additional custom include DIR (in case of trouble to find it (fedora 17 opencv package))\")\n        endif()\n    endforeach()\n    if(OpenCV_INCLUDE_DIR)\n        list(APPEND OpenCV_INCLUDE_DIRS ${OpenCV_INCLUDE_DIR})\n        include_directories(${OpenCV_INCLUDE_DIRS})\n    endif()\nendif()\n\n###################\n## Find GLFW\n###################\nsibr_addlibrary(\n    NAME GLFW\n    MSVC11 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/glfw-3.2.1.7z\"\n    MSVC14 \"https://repo-sam.inria.fr/fungraph/dependencies/ibr-common/win3rdParty-MSVC15-splitted%20version/glfw-3.2.1.7z\"     # TODO SV: provide a valid version if required\n)\n\nsibr_gitlibrary(TARGET imgui\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/imgui.git\"\n    GIT_TAG\t\t\t\"e7f0fa31b9fa3ee4ecd2620b9951f131b4e377c6\"\n)\n\nsibr_gitlibrary(TARGET mrf\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/mrf.git\"\n    GIT_TAG\t\t\t\"564e5e0b395c788d2f8b2cf4f879fed2493faea7\"\n)\n\nsibr_gitlibrary(TARGET nanoflann\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/nanoflann.git\"\n    GIT_TAG\t\t\t\"7a20a9ac0a1d34850fc3a9e398fc4a7618e8a69a\"\n)\n\nsibr_gitlibrary(TARGET picojson\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/picojson.git\"\n    GIT_TAG\t\t\t\"7cf8feee93c8383dddbcb6b64cf40b04e007c49f\"\n)\n\nsibr_gitlibrary(TARGET rapidxml\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/rapidxml.git\"\n    GIT_TAG\t\t\t\"069e87f5ec5ce1745253bd64d89644d6b894e516\"\n)\n\nsibr_gitlibrary(TARGET xatlas\n    GIT_REPOSITORY \t\"https://gitlab.inria.fr/sibr/libs/xatlas.git\"\n    GIT_TAG\t\t\t\"0fbe06a5368da13fcdc3ee48d4bdb2919ed2a249\"\n    INCLUDE_DIRS \t\"source/xatlas\"\n)\n\nWin3rdPartyGlobalCacheAction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/downloadAndExtractZipFile.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## downloadAndExtractZipFile cmake function\n## Provide a way to download zip file from public internet ZIP_URL host\n## and to extract it in a specific EXCTRATED_ZIP_PATH destination.\n## This function use 7-Zip external tool to maximize the compatibles formats.\n## This will be not download again if the EXCTRATED_ZIP_PATH already exist and DL_FORCE is set to OFF.\n## This will try to unzip file if already exist in the ZIP_DL_PATH.\n##\n## If EXCTRATED_ZIP_PATH and/or ZIP_DL_PATH are not full path,\n## it will be interpreted relative to CMAKE_BINARY_DIR\n##\n## Usage example :\n## include(downloadAndExtractZipFile)\n## downloadAndExtractZipFile(\n## \thttp://www.cs.cornell.edu/~snavely/bundler/distr/bundler-v0.4-source.zip\n## \t${CMAKE_BINARY_DIR}/Bundler/bundler-v0.4-source.zip\n## \t${CMAKE_BINARY_DIR}/Bundler\n##  [DL_FORCE ON|OFF]\n##  [TIMEOUT]\n##  [CHECK_DIRTY_URL]\n## )\n##\n## option DL_FORCE will redownload the zip file [deafult to OFF]\n## option TIMEOUT will end the unzip process after this period of time [default to 600s]\n## option CHECK_DIRTY_URL will write into the given file the downloaded URL and then,\n## next time, if the URL was updated, it detect it with this file\n## and will download the last version. This prevent to alway set manually DL_FORCE to ON...\n##\nif(__downloadAndExtractZipFile_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__downloadAndExtractZipFile_cmake_INCLUDED__ ON)\nendif()\n\nfunction(downloadAndExtractZipFile ZIP_URL ZIP_DL_PATH EXCTRATED_ZIP_PATH)\n\n    # message(STATUS \"zipUrl=${ZIP_URL} zipDlPath=${ZIP_DL_PATH} extractedZipPath=${EXCTRATED_ZIP_PATH}\")\n    cmake_parse_arguments(dwnlezf \"\" \"VERBOSE;DL_FORCE;TIMEOUT;CHECK_DIRTY_URL\" \"\" ${ARGN})\n\n\tset(PROGRAMFILESx86 \"PROGRAMFILES(x86)\")\n\n    ## Check entries mandatory args\n    if(IS_ABSOLUTE \"${ZIP_DL_PATH}\")\n    else()\n        set(ZIP_DL_PATH \"${CMAKE_BINARY_DIR}/${ZIP_DL_PATH}\")\n    endif()\n    if(IS_ABSOLUTE \"${EXCTRATED_ZIP_PATH}\")\n    else()\n        set(EXCTRATED_ZIP_PATH \"${CMAKE_BINARY_DIR}/${EXCTRATED_ZIP_PATH}\")\n    endif()\n    if(NOT EXISTS \"${EXCTRATED_ZIP_PATH}\")\n        file(MAKE_DIRECTORY ${EXCTRATED_ZIP_PATH})\n    endif()\n\n\t# SB: Once, one of downloaded zip was corrupted by an error message coming from the server.\n\tif(EXISTS \"${ZIP_DL_PATH}\")\n\t\t# So I check for removing such corrupted files\n\t\tmessage(\"Removing previous ${ZIP_DL_PATH} (might be corrupted)\")\n\t\tfile(REMOVE \"${ZIP_DL_PATH}\")\n\t\tif(EXISTS \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\t# and remove the previous (corrupted) made 'Win3rdPartyUrl' file\n\t\t\tfile(REMOVE \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\tendif()\n\tendif()\n\n    ## Check entries optional args\n\tmacro(readDirtyUrl )\n\t\tif(dwnlezf_CHECK_DIRTY_URL)\n\t\t\tif(IS_ABSOLUTE \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\telse()\n\t\t\t\tset(dwnlezf_CHECK_DIRTY_URL \"${CMAKE_BINARY_DIR}/${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\tendif()\n\t\t\tget_filename_component(unzipDir \t${EXCTRATED_ZIP_PATH} NAME)\n\t\t\tget_filename_component(unzipPath \t${EXCTRATED_ZIP_PATH} PATH)\n\t\t\tmessage(STATUS \"Checking ${unzipDir} [from ${unzipPath}]...\")\n\t\t\tif(EXISTS \"${dwnlezf_CHECK_DIRTY_URL}\")\n\t\t\t\tget_filename_component(CHECK_DIRTY_URL_FILENAME ${dwnlezf_CHECK_DIRTY_URL} NAME)\n\t\t\t\tfile(STRINGS \"${dwnlezf_CHECK_DIRTY_URL}\" contents)\n\t\t\t\tlist(GET contents 0 downloadURL)\n\t\t\t\tlist(REMOVE_AT contents 0)\n\t\t\t\tif(\"${downloadURL}\" MATCHES \"${ZIP_URL}\")\n\t\t\t\t\tif(dwnlezf_VERBOSE)\n\t\t\t\t\t\tmessage(STATUS \"Your downloaded version (URL) seems to be up to date. Let me check if nothing is missing... (see ${dwnlezf_CHECK_DIRTY_URL}).\")\n\t\t\t\t\tendif()\n\t\t\t\t\tfile(GLOB PATHNAME_PATTERN_LIST \"${EXCTRATED_ZIP_PATH}/*\") ## is there something inside the downloaded destination ?\n\t\t\t\t\tunset(NAME_PATTERN_LIST)\n\t\t\t\t\tforeach(realPathPattern ${PATHNAME_PATTERN_LIST})\n\t\t\t\t\t\tget_filename_component(itemName ${realPathPattern} NAME)\n\t\t\t\t\t\tlist(APPEND NAME_PATTERN_LIST ${itemName})\n\t\t\t\t\tendforeach()\n\t\t\t\t\tif(NAME_PATTERN_LIST)\n\t\t\t\t\t\tforeach(item ${contents})\n\t\t\t\t\t\t\tlist(FIND NAME_PATTERN_LIST ${item} id)\n\t\t\t\t\t\t\tif(${id} MATCHES \"-1\")\n\t\t\t\t\t\t\t\tmessage(STATUS \"${item} is missing, your downloaded version content changed, need to redownload it.\")\n\t\t\t\t\t\t\t\tset(ZIP_DL_FORCE ON)\n\t\t\t\t\t\t\t\tbreak()\n\t\t\t\t\t\t\telse()\n\t\t\t\t\t\t\t\tlist(REMOVE_AT NAME_PATTERN_LIST ${id})\n\t\t\t\t\t\t\t\tset(ZIP_DL_FORCE OFF)\n\t\t\t\t\t\t\tendif()\n\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\tif(NOT ZIP_DL_FORCE AND NAME_PATTERN_LIST)\n\t\t\t\t\t\t\tmessage(\"Yours seems to be up to date (regarding to ${CHECK_DIRTY_URL_FILENAME})!\\nBut there are additional files/folders into your downloaded destination (feel free to clean it if you want).\")\n\t\t\t\t\t\t\tforeach(item ${NAME_PATTERN_LIST})\n\t\t\t\t\t\t\t\tif(item)\n\t\t\t\t\t\t\t\t\tmessage(\"${item}\")\n\t\t\t\t\t\t\t\tendif()\n\t\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\tendif()\n\t\t\t\t\tendif()\n\t\t\t\telse()\n\t\t\t\t\tset(ZIP_DL_FORCE ON)\n\t\t\t\t\tmessage(STATUS \"Your downloaded version is dirty (too old).\")\n\t\t\t\tendif()\n\t\t\telse()\n\t\t\t\tfile(GLOB PATHNAME_PATTERN_LIST \"${EXCTRATED_ZIP_PATH}/*\") ## is there something inside the downloaded destination ?\n\t\t\t\tif(NOT PATHNAME_PATTERN_LIST)\n\t\t\t\t\tmessage(\"We found nothing into ${EXCTRATED_ZIP_PATH}, we will try to download it for you now.\")\n\t\t\t\tendif()\n\t\t\t\tset(ZIP_DL_FORCE ON)\n\t\t\tendif()\n\t\tendif()\n\tendmacro()\n\treadDirtyUrl()\n\tif(NOT ZIP_DL_FORCE)\n\t\treturn() ## do not need to further (as we are up to date, just exit the function\n\tendif()\n\n\tmacro(writeDirtyUrl )\n\t\tif(dwnlezf_CHECK_DIRTY_URL)\n\t\t\tfile(WRITE \"${dwnlezf_CHECK_DIRTY_URL}\" \"${ZIP_URL}\\n\")\n\t\t\tfile(GLOB PATHNAME_PATTERN_LIST \"${EXCTRATED_ZIP_PATH}/*\") ## is there something inside the downloaded destination ?\n\t\t\tunset(NAME_PATTERN_LIST)\n\t\t\tforeach(realPathPattern ${PATHNAME_PATTERN_LIST})\n\t\t\t\tget_filename_component(itemName ${realPathPattern} NAME)\n\t\t\t\tlist(APPEND NAME_PATTERN_LIST ${itemName})\n\t\t\tendforeach()\n\t\t\tif(NAME_PATTERN_LIST)\n\t\t\t\tforeach(item ${NAME_PATTERN_LIST})\n\t\t\t\t\tfile(APPEND \"${dwnlezf_CHECK_DIRTY_URL}\" \"${item}\\n\")\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\tendif()\n\tendmacro()\n\n\tif(dwnlezf_DL_FORCE)\n        set(ZIP_DL_FORCE ON)\n    endif()\n\n\tif(NOT dwnlezf_TIMEOUT)\n\t\tset(dwnlezf_TIMEOUT 600)\n\tendif()\n\tmath(EXPR dwnlezf_TIMEOUT_MIN \"${dwnlezf_TIMEOUT}/60\")\n\n\tmacro(unzip whichZipFile)\n\t\tif(NOT SEVEN_ZIP_CMD)\n\t\t\tfind_program(SEVEN_ZIP_CMD NAMES 7z 7za p7zip DOC \"7-zip executable\" PATHS \"$ENV{PROGRAMFILES}/7-Zip\" \"$ENV{${PROGRAMFILESx86}}/7-Zip\" \"$ENV{ProgramW6432}/7-Zip\")\n\t\tendif()\n\t\tif(SEVEN_ZIP_CMD)\n            if(dwnlezf_VERBOSE)\n                message(STATUS \"UNZIP: please, WAIT UNTIL ${SEVEN_ZIP_CMD} finished...\\n(no more than ${dwnlezf_TIMEOUT_MIN} min)\")\n            else()\n                message(STATUS \"UNZIP...wait...\")\n            endif()\n\t\t\texecute_process( \tCOMMAND ${SEVEN_ZIP_CMD} x ${whichZipFile} -y\n\t\t\t\t\t\t\t\tWORKING_DIRECTORY ${EXCTRATED_ZIP_PATH}\tTIMEOUT ${dwnlezf_TIMEOUT}\n\t\t\t\t\t\t\t\tRESULT_VARIABLE resVar OUTPUT_VARIABLE outVar ERROR_VARIABLE errVar\n\t\t\t\t\t\t\t)\n\t\t\tif(${resVar} MATCHES \"0\")\n                if(dwnlezf_VERBOSE)\n                    message(STATUS \"SUCESS to unzip in ${EXCTRATED_ZIP_PATH}. Now we can remove the downloaded zip file.\")\n                endif()\n\t\t\t\texecute_process(COMMAND ${CMAKE_COMMAND} -E remove ${whichZipFile})\n\t\t\t\tmark_as_advanced(SEVEN_ZIP_CMD)\n\t\t\telse()\n\t\t\t\tmessage(WARNING \"something wrong in ${EXCTRATED_ZIP_PATH}\\n with \\\"${SEVEN_ZIP_CMD} x ${whichZipFile} -y\\\", redo or try to unzip by yourself...\")\n\t\t\t\tmessage(\"unzip: resVar=${resVar}\")\n\t\t\t\tmessage(\"unzip: outVar=${outVar}\")\n\t\t\t\tmessage(\"unzip: errVar=${errVar}\")\n\t\t\t\tmessage(\"unzip: failed or canceled or timeout\")\n\t\t\tendif()\n\t\telse()\n\t\t\tmessage(WARNING \"You need 7zip (http://www.7-zip.org/download.html) to unzip the downloaded dir.\")\n\t\t\tset(SEVEN_ZIP_CMD \"\" CACHE FILEPATH \"7-zip executable\")\n\t\t\tmark_as_advanced(CLEAR SEVEN_ZIP_CMD)\n\t\tendif()\n\tendmacro()\n\n    if(dwnlezf_VERBOSE)\n        message(STATUS \"Trying to look ${ZIP_DL_PATH} if a zip file exist...\")\n    endif()\n\tif(EXISTS \"${ZIP_DL_PATH}\")\n\n\t\t## already downloaded, so just unzip it\n\t\tunzip(${ZIP_DL_PATH})\n\t\twriteDirtyUrl()\n\n\telseif(ZIP_DL_FORCE)\n\n\t\t## the download part (+ unzip)\n\t\tmessage(STATUS \"Let me try to download package for you : ${ZIP_URL}\")\n        if(dwnlezf_VERBOSE)\n            message(STATUS \"Downloading...\\n   SRC=${ZIP_URL}\\n   DEST=${ZIP_DL_PATH}.tmp\\n   INACTIVITY_TIMEOUT=180s\")\n        endif()\n\t\tfile(DOWNLOAD ${ZIP_URL} ${ZIP_DL_PATH}.tmp INACTIVITY_TIMEOUT 360 STATUS status SHOW_PROGRESS)\n\n\t\tlist(GET status 0 numResult)\n\t\tif(${numResult} MATCHES \"0\")\n\n            if(dwnlezf_VERBOSE)\n                message(STATUS \"Download succeed, so let me rename the tmp file to unzip it\")\n            endif()\n\t\t\texecute_process(COMMAND ${CMAKE_COMMAND} -E rename ${ZIP_DL_PATH}.tmp ${ZIP_DL_PATH})\n\t\t\tunzip(${ZIP_DL_PATH})\n\t\t\twriteDirtyUrl()\n\n\t\telse()\n\n\t\t\tlist(GET status 1 errMsg)\n\t\t\tmessage(WARNING \"DOWNLOAD ${ZIP_URL} to ${ZIP_DL_PATH} failed\\n:${errMsg}\")\n\t\t\tmessage(WARNING \"OK, you need to download the ${ZIP_URL} manually and put it into ${ZIP_DL_PATH}\")\n\t\t\tmessage(\"Take a look at the project website page to check available URL.\")\n\n\t\tendif()\n\n\tendif()\n\n\t## clean up the tmp downloaded file\n\tif(EXISTS \"${ZIP_DL_PATH}.tmp\")\n\t\texecute_process(COMMAND ${CMAKE_COMMAND} -E remove ${ZIP_DL_PATH}.tmp)\n\tendif()\n\nendfunction()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/git_describe.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(__git_describe_INCLUDED__)\n\treturn()\nelse()\n\tset(__git_describe_INCLUDED__ ON)\nendif()\n\nfind_package(Git)\nif(Git_FOUND)\n  message(STATUS \"Git found: ${GIT_EXECUTABLE}\")\nelse()\n  message(FATAL_ERROR \"Git not found. Aborting\")\nendif()\n\nmacro(git_describe)\n    cmake_parse_arguments(GIT_DESCRIBE \"\" \"GIT_URL;GIT_BRANCH;GIT_COMMIT_HASH;GIT_TAG;GIT_VERSION;PATH\" \"\" ${ARGN})\n\n    if(NOT GIT_DESCRIBE_PATH)\n        set(GIT_DESCRIBE_PATH ${CMAKE_SOURCE_DIR})\n    endif()\n\n    if(GIT_DESCRIBE_GIT_URL)\n        # Get the current remote\n        execute_process(\n            COMMAND git remote\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     GIT_DESCRIBE_GIT_REMOTE\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n\n        # Get the current remote\n        execute_process(\n            COMMAND git remote get-url ${GIT_DESCRIBE_GIT_REMOTE}\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_URL}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_BRANCH)\n        # Get the current working branch\n        execute_process(\n            COMMAND git rev-parse --abbrev-ref HEAD\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_BRANCH}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_COMMIT_HASH)\n        # Get the latest abbreviated commit hash of the working branch\n        execute_process(\n            COMMAND git rev-parse HEAD\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_COMMIT_HASH}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_TAG)\n        # Get the tag\n        execute_process(\n            COMMAND git describe --tags --exact-match\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_TAG}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n    endif()\n\n    if(GIT_DESCRIBE_GIT_VERSION)\n        # Get the version from git describe\n        execute_process(\n            COMMAND git describe\n            WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n            OUTPUT_VARIABLE     ${GIT_DESCRIBE_GIT_VERSION}\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            ERROR_QUIET\n        )\n\n        if(${GIT_DESCRIBE_GIT_VERSION} STREQUAL \"\")\n            execute_process(\n                COMMAND git rev-parse --abbrev-ref HEAD\n                WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n                OUTPUT_VARIABLE     GIT_DESCRIBE_GIT_VERSION_BRANCH\n                OUTPUT_STRIP_TRAILING_WHITESPACE\n                ERROR_QUIET\n            )\n            execute_process(\n                COMMAND git log -1 --format=%h\n                WORKING_DIRECTORY   ${GIT_DESCRIBE_PATH}\n                OUTPUT_VARIABLE     GIT_DESCRIBE_GIT_VERSION_COMMIT\n                OUTPUT_STRIP_TRAILING_WHITESPACE\n                ERROR_QUIET\n            )\n\n            set(${GIT_DESCRIBE_GIT_VERSION} \"${GIT_DESCRIBE_GIT_VERSION_BRANCH}-${GIT_DESCRIBE_GIT_VERSION_COMMIT}\")\n        endif()\n    endif()\n\nendmacro()"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/include_once.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nmacro(include_once file)\n    get_filename_component(INCLUDE_ONCE_FILEPATH ${file} REALPATH)\n    string(REGEX REPLACE \"(\\\\.|\\\\/+|\\\\:|\\\\\\\\+)\" \"_\" INCLUDE_ONCE_FILEPATH ${INCLUDE_ONCE_FILEPATH})\n    get_property(INCLUDED_${INCLUDE_ONCE_FILEPATH}_LOCAL GLOBAL PROPERTY INCLUDED_${INCLUDE_ONCE_FILEPATH})\n    if (INCLUDED_${INCLUDE_ONCE_FILEPATH}_LOCAL)\n        return()\n    else()\n        set_property(GLOBAL PROPERTY INCLUDED_${INCLUDE_ONCE_FILEPATH} true)\n\n        include(${file})\n    endif()\nendmacro()"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/install_runtime.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n## This file is mainly used to allow runtime installation\n## There are some utilities cmake functions to ease the generic deployement (abstract common usage of cmake)...\n##\n## You cannot run your programm automaticaly from your CNAKE_BINARY_DIR when you build\n## as it will miss all dependencies and ressources files...\n## You have to run install target in order to test your programm.\n##\n## The only one function/macros you may use inside your sub-CMakeLists.txt (sub-project) is :\n## ******************\n## ibr_install_target macro => see documentation at the end of this file\n## ******************\n## It use these utilities cmake functions to abstract the installation in an uniform way for all sub-projects.\n##\nif(__install_runtime_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__install_runtime_cmake_INCLUDED__ ON)\nendif()\n\n\n##\n## Allow to write a resource config file which contain additional ressource paths\n## (used by IBR_Common Resource system to load shaders and potentialy images, plugins and so on)\n##\n## ADD option list all the paths to add in the file (relative paths are interpreted relative to working dir of the executable)\n## INSTALL option to specify where we want to install this file\n##\n## Example usage:\n## resourceFile(ADD \"shaders\" \"${PROJECT_NAME}_rsc\" INSTALL bin)\n##\nmacro(resourceFile)\n\tcmake_parse_arguments(rsc \"\" \"INSTALL;FILE_PATH;CONFIG_TYPE\" \"ADD\" ${ARGN}) ## both args are directory path\n\n\tif(rsc_ADD)\n\t\tunset(IBR_RSC_FILE_CONTENT_LIST)\n\t\tif(EXISTS \"${rsc_FILE_PATH}\")\n\t\t\tfile(READ \"${rsc_FILE_PATH}\" IBR_RSC_FILE_CONTENT)\n\t\t\tstring(REGEX REPLACE \"\\n\" \";\" IBR_RSC_FILE_CONTENT_LIST \"${IBR_RSC_FILE_CONTENT}\")\n\t\tendif()\n\t\tlist(APPEND IBR_RSC_FILE_CONTENT_LIST \"${rsc_ADD}\")\n\t\tlist(REMOVE_DUPLICATES IBR_RSC_FILE_CONTENT_LIST)\n\t\tfile(WRITE \"${rsc_FILE_PATH}\" \"\")\n\t\tforeach(rscDir ${IBR_RSC_FILE_CONTENT_LIST})\n\t\t\tfile(APPEND \"${rsc_FILE_PATH}\" \"${rscDir}\\n\")\n\t\tendforeach()\n\t\tunset(rsc_ADD)\n\tendif()\n\n\tif(rsc_INSTALL)\n\t\tinstall(FILES ${rsc_FILE_PATH} CONFIGURATIONS ${rsc_CONFIG_TYPE} DESTINATION ${rsc_INSTALL})\n\t\tunset(rsc_INSTALL)\n\tendif()\nendmacro()\n\n\n##\n## Install *.pdb generated file for the current cmake project\n## assuming the output target name is the cmake project name.\n## This macro is useful for crossplateform multi config mode.\n##\n## Usage Example:\n##\n## \tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n##\t\tinstallPDB(${PROJECT_NAME} ${CMAKE_BUILD_TYPE} RUNTIME_DEST bin ARCHIVE_DEST lib LIBRARY_DEST lib)\n## \tendif()\n##\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n##\t\tinstallPDB(${PROJECT_NAME} ${CONFIG_TYPES} RUNTIME_DEST bin ARCHIVE_DEST lib LIBRARY_DEST lib)\n##\tendforeach()\n##\nmacro(installPDB targetName configType)\n\tcmake_parse_arguments(instpdb \"\" \"COMPONENT\" \"ARCHIVE_DEST;LIBRARY_DEST;RUNTIME_DEST\" ${ARGN}) ## both args are directory path\n\n\tif(NOT MSVC)\n\t\treturn()\n\tendif()\n\n    ## Check if DESTINATION are provided according to the TYPE of the given target (see install command doc to see correspodances)\n    get_target_property(type ${targetName} TYPE)\n    if(${type} MATCHES \"EXECUTABLE\" AND instpdb_RUNTIME_DEST)\n        set(pdb_DESTINATION ${instpdb_RUNTIME_DEST})\n    elseif(${type} MATCHES \"STATIC_LIBRARY\" AND instpdb_ARCHIVE_DEST)\n        set(pdb_DESTINATION ${instpdb_ARCHIVE_DEST})\n    elseif(${type} MATCHES \"MODULE_LIBRARY\" AND instpdb_LIBRARY_DEST)\n        set(pdb_DESTINATION ${instpdb_LIBRARY_DEST})\n    elseif(${type} MATCHES \"SHARED_LIBRARY\")\n        if(WIN32 AND instpdb_RUNTIME_DEST)\n            set(pdb_DESTINATION ${instpdb_RUNTIME_DEST})\n        else()\n            set(pdb_DESTINATION ${instpdb_LIBRARY_DEST})\n        endif()\n    endif()\n\n    if(NOT pdb_DESTINATION)\n\t\tset(pdb_DESTINATION bin) ## default destination of the pdb file\n\tendif()\n\n\tif(NOT instpdb_COMPONENT)\n\t\tset(instpdb_COMPONENT )\n\telse()\n\t\tset(instpdb_COMPONENT COMPONENT ${instpdb_COMPONENT})\n\tendif()\n\n\tstring(TOUPPER ${configType} CONFIG_TYPES_UC)\n\tget_target_property(PDB_PATH ${targetName} PDB_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC})\n\n\tget_target_property(confModePostfix ${targetName} ${CONFIG_TYPES_UC}_POSTFIX)\n\tif(NOT confModePostfix)\n\t\tset(confModePostfix \"\")\n\tendif()\n\tset_target_properties(${targetName} PROPERTIES  PDB_NAME_${CONFIG_TYPES_UC} ${targetName}${confModePostfix})\n\tget_target_property(PDB_NAME ${targetName} PDB_NAME_${CONFIG_TYPES_UC})# if not set, this is empty\n\n\tif(EXISTS \"${PDB_PATH}/${PDB_NAME}.pdb\")\n\t\tinstall(FILES \"${PDB_PATH}/${PDB_NAME}.pdb\" CONFIGURATIONS ${configType} DESTINATION ${pdb_DESTINATION} ${instpdb_COMPONENT} OPTIONAL)\n\tendif()\nendmacro()\n\n\n##\n## Add additional target to install a project independently and based on its component\n## configMode is used to prevent default Release installation (we want also to install in other build/config type)\n##\nmacro(installTargetProject targetOfProject targetOfInstallProject)\n \tif(DEFINED CMAKE_BUILD_TYPE) ## for make/nmake based\n\t\tset(configMode ${CMAKE_BUILD_TYPE})\n\telseif(MSVC)\n\t\t## $(Configuration) will be one of the following : Debug, Release, MinSizeRel, RelWithDebInfo\n\t\tset(configMode $(Configuration))\n \tendif()\n\tif(configMode)\n        get_target_property(srcFiles ${targetOfProject} SOURCES)\n\t\tadd_custom_target(\t${targetOfInstallProject} #ALL\n\t\t\t\t\t\t\t${CMAKE_COMMAND} -DBUILD_TYPE=${configMode} -DCOMPONENT=${targetOfInstallProject} -P ${CMAKE_BINARY_DIR}/cmake_install.cmake\n\t\t\t\t\t\t\tDEPENDS ${srcFiles}\n\t\t\t\t\t\t\tCOMMENT \"run the installation only for ${targetOfProject}\" VERBATIM\n\t\t\t\t\t\t\t)\n\t\tadd_dependencies(${targetOfInstallProject} ${targetOfProject})\n\n\t\tget_target_property(INSTALL_BUILD_FOLDER ${targetOfProject} FOLDER)\n\t\tset_target_properties(${targetOfInstallProject} PROPERTIES FOLDER ${INSTALL_BUILD_FOLDER})\n\tendif()\nendmacro()\n\n# Collect all currently added targets in all subdirectories\n#\n# Parameters:\n# - _result the list containing all found targets\n# - _dir root directory to start looking from\nfunction(get_all_targets _result _dir)\n    get_property(_subdirs DIRECTORY \"${_dir}\" PROPERTY SUBDIRECTORIES)\n    foreach(_subdir IN LISTS _subdirs)\n        get_all_targets(${_result} \"${_subdir}\")\n    endforeach()\n\n    get_directory_property(_sub_targets DIRECTORY \"${_dir}\" BUILDSYSTEM_TARGETS)\n    set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE)\nendfunction()\n\n##\n## Add targets for building and installing subdirectories\nmacro(subdirectory_target target directory build_folder)\n\tadd_custom_target(${target}\n\t\tCOMMENT \"run build for all projects in this directory\" VERBATIM\n\t)\n\tget_all_targets(ALL_TARGETS ${directory})\n\tadd_dependencies(${target} ${ALL_TARGETS})\n\tadd_custom_target(${target}_install\n\t\t${CMAKE_COMMAND} -DBUILD_TYPE=$<CONFIG> -DCOMPONENT=${target}_install -P ${CMAKE_BINARY_DIR}/cmake_install.cmake\n\t\tCOMMENT \"run install for all projects in this directory\" VERBATIM\n\t)\n\tadd_dependencies(${target}_install ${target})\n\n\tset_target_properties(${target}\t\t\tPROPERTIES FOLDER ${build_folder})\n\tset_target_properties(${target}_install PROPERTIES FOLDER ${build_folder})\nendmacro()\n\n\n##  CMAKE install all required dependencies for an application (included system OS files like msvc*.dll for example)\n##\n## install_runtime(<installedFilePathTargetAppToResolve>\n##      [TARGET                 name]\n##      [PLUGINS \t\t\t\tname \t\t[nameN ...] [PLUGIN_PATH_NAME currentPathName [FROM_REL_PATH matchDirFromCurrentPathName] [PLUGIN_PATH_DEST installDir] ]\n##      [PLUGINS \t\t\t\t...]\n##      [DIRS \t\t\t\t\tpath \t\t[pathN ...] ]\n##\t\t[TARGET_LIBRARIES  \t\tfilePath\t[filePathN ...] ]\n##\t\t[TARGET_PACKAGES   \t\tpackageName [packageNameN ...] ]\n##\t\t[COMPONENT\t\t\t\tinstallComponentName]\n##\t\t[PLAUSIBLES_POSTFIX\t\tDebug_postfix [MinSizeRel_postfix relWithDebInfo_postfix ...] ]\n##      [VERBOSE]\n## )\n##\n## installedFilePathTargetAppToResolve : the final installed targetApp absolute full file path name you want to resolve\n##\n## TARGET           :   The target app we want to install. If given, it's used to look for link libraries paths (best choice to use, strongly advised to use it)\n##\n## PLUGINS \t\t\t: \tSome application built use/load some plugins which can't be detect inside its binary,\n##\t\t\t\t\t\tso, here you can specify which plugins the application use/load in order to install them\n##\t\t\t\t\t\tand resolve also there dependencies.\n## \t\tWith PLUGINS multi FLAGS \t:\n## \t \t\tPLUGIN_PATH_NAME \t: The current plugin full file path we want to install\n##\t\t\tFROM_REL_PATH\t\t: [optional: default only the file is kept] From which matching dir of the plugin path we want to install (keep the directories structure)\n##\t\t\tPLUGIN_PATH_DEST\t: [optional: default relative to executable directory] Where (full path to the install directory) we will install the plugin file (or file path)\n##\n## DIRS \t\t\t:\tA list of directories to looking for dependencies\n## TARGET_LIBRARIES :\tDEPRECATED (use TARGET flag instead) : The cmake content variables used for the target_link_libraries(<targetApp> ...)\n## TARGET_PACKAGES \t: \tDEPRECATED (use TARGET flag instead) : The cmake package names used for the findPackage(...) for your targetApp\n##\t\t\t\t\t\tADVICE: This flag add entries in cache (like: <packageName>_DIR), it could be useful to fill these variable!\n## COMPONENT\t\t:\t(default to runtime) Is the component name associated to the installation\n##\t\t\t\t\t\tIt is used when you want to install separatly some part of your projets (see install cmake doc)\n## VERBOSE\t\t\t: \tFor debug or to get more informations in the output console\n##\n## Usage:\n##\t install_runtime(${CMAKE_INSTALL_PREFIX}/${EXECUTABLE_NAME}${CMAKE_EXECUTABLE_SUFFIX}\n##\t\tVERBOSE\n##      TARGET  ${PROJECT_NAME}\n##      PLAUSIBLES_POSTFIX  _d\n##      PLUGINS\n##\t\t    PLUGIN_PATH_NAME    ${PLUGIN_PATH_NAME}${CMAKE_SHARED_MODULE_SUFFIX} ## will be installed (default exec path if no PLUGINS_DEST) and then will be resolved\n##\t\t\tFROM_REL_PATH\t\tplugins ## optional, used especially for keeping qt plugins tree structure\n##          PLUGIN_PATH_DEST    ${CMAKE_INSTALL_PREFIX}/plugins ## (or relative path 'plugins' will be interpreted relative to installed executable)\n##\t\tDIRS\t\t\t\t${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}\n##\t\tTARGET_LIBRARIES\t${OPENGL_LIBRARIES}         ## DEPRECATED (use TARGET flag instead)\n##\t\t\t\t\t\t\t${GLEW_LIBRARIES}\n##\t\t\t\t\t\t\t${GLUT_LIBRARIES}\n##\t\t\t\t\t\t\t${Boost_LIBRARIES}\n##\t\t\t\t\t\t\t${SuiteSparse_LIBRARIES}\n##\t\t\t\t\t\t\t${CGAL_LIBRARIES}\n##\t\tTARGET_PACKAGES\t\tOPENGL                      ## DEPRECATED (use TARGET flag instead)\n##\t\t\t\t\t\t\tGLEW\n##\t\t\t\t\t\t\tGLUT\n##\t\t\t\t\t\t\tCGAL\n##\t\t\t\t\t\t\tBoost\n##\t\t\t\t\t\t\tSuiteSparse\n##\t)\n##\n## For plugins part, it use our internal parse_arguments_multi.cmake\n##\nfunction(install_runtime installedFilePathTargetAppToResolve)\n    set(optionsArgs \"VERBOSE\")\n    set(oneValueArgs \"COMPONENT;INSTALL_FOLDER;CONFIG_TYPE\")\n    set(multiValueArgs \"DIRS;PLUGINS;TARGET_LIBRARIES;TARGET_PACKAGES;TARGET;PLAUSIBLES_POSTFIX\")\n    cmake_parse_arguments(inst_run \"${optionsArgs}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN} )\n\n    if(IS_ABSOLUTE ${installedFilePathTargetAppToResolve})\n    else()\n        set(installedFilePathTargetAppToResolve ${inst_run_INSTALL_FOLDER}/${installedFilePathTargetAppToResolve})\n    endif()\n\n\tget_filename_component(EXEC_NAME ${installedFilePathTargetAppToResolve} NAME_WE)\n\tget_filename_component(EXEC_PATH ${installedFilePathTargetAppToResolve} PATH)\n\n\tif(NOT inst_run_COMPONENT)\n\t\tset(inst_run_COMPONENT runtime)\n\tendif()\n\n\n    ## Try to append as more possible as possible paths to find dependencies (deprecated since we can use target_properties to get back paths)\n    set(libPaths )\n\tforeach(libraryFileName ${inst_run_TARGET_LIBRARIES})\n\t\tif(IS_DIRECTORY \"${libraryFileName}\")\n\t\t\tlist(APPEND libPaths \"${libraryFileName}\")\n\t\telse()\n\t\t\tget_filename_component(libpath \"${libraryFileName}\" PATH)\n\t\t\tif(EXISTS \"${libpath}\")\n\t\t\t\tlist(APPEND libPaths \"${libpath}\")\n\t\t\tendif()\n\t\tendif()\n\tendforeach()\n\n    ## This macro is used internaly here to recursilvely get path of LINK_LIBRARIES of each non imported target\n    ## Typically if you have 2 internal dependencies between cmake targets, we want cmake to be able to get back path where are these dependencies\n    macro(recurseDepList target)\n        get_target_property(linkLibs ${target} LINK_LIBRARIES)\n        foreach(lib ${linkLibs})\n            string(FIND ${lib} \">\" strId) ## cmake is using generator-expression?\n\t\t\tif(TARGET ${lib})\n\t\t\t\t## Skipping interface libraries as they're system ones\n                get_target_property(type ${lib} TYPE)\n\t\t\t\tget_target_property(imported ${lib} IMPORTED)\n\t\t\t\tif(type STREQUAL \"INTERFACE_LIBRARY\")\n\t\t\t\t\tget_target_property(imp_loc ${lib} INTERFACE_IMPORTED_LOCATION)\n\t\t\t\t\tif(imp_loc)\n\t\t\t\t\t\tget_filename_component(imp_loc ${imp_loc} PATH)\n\t\t\t\t\t\tlist(APPEND targetLibPath ${imp_loc})\n\t\t\t\t\tendif()\n\t\t\t\t\tget_target_property(loc ${lib} INTERFACE_LOCATION)\n\t\t\t\t\tif(loc)\n\t\t\t\t\t\tget_filename_component(loc ${loc} PATH)\n\t\t\t\t\t\tlist(APPEND targetLibPath ${loc})\n\t\t\t\t\tendif()\n                ## it's not a path but a single target name\n                ## for build-target which are part of the current cmake configuration : nothing to do as cmake already know the output path\n                ## for imported target, we need to look for theire imported location\n                elseif(imported)\n                    get_target_property(imp_loc ${lib} IMPORTED_LOCATION)\n                    if(imp_loc)\n                        get_filename_component(imp_loc ${imp_loc} PATH)\n                        list(APPEND targetLibPath ${imp_loc})\n                    endif()\n                    get_target_property(loc ${lib} LOCATION)\n                    if(loc)\n                        get_filename_component(loc ${loc} PATH)\n                        list(APPEND targetLibPath ${loc})\n                    endif()\n                else()\n                    recurseDepList(${lib})\n                endif()\n            elseif(NOT ${strId} MATCHES -1) ## mean cmake use generator-expression (CMAKE VERSION > 3.0)\n                string(REGEX MATCH      \">:[@A-Za-z_:/.0-9-]+\"           targetLibPath ${lib})\n                string(REGEX REPLACE    \">:([@A-Za-z_:/.0-9-]+)\" \"\\\\1\"   targetLibPath ${targetLibPath})\n                get_filename_component(targetLibPath ${targetLibPath} PATH)\n            elseif(EXISTS ${lib})\n                set(targetLibPath ${lib})\n                get_filename_component(targetLibPath ${targetLibPath} PATH)\n            else()\n                #message(STATUS \"[install_runtime] skip link library : ${lib} , of target ${target}\")\n            endif()\n            if(targetLibPath)\n                list(APPEND targetLinkLibsPathList ${targetLibPath})\n            endif()\n        endforeach()\n        if(targetLinkLibsPathList)\n            list(REMOVE_DUPLICATES targetLinkLibsPathList)\n        endif()\n    endmacro()\n    if(inst_run_TARGET)\n        recurseDepList(${inst_run_TARGET})\n        if(targetLinkLibsPathList)\n            list(APPEND libPaths ${targetLinkLibsPathList})\n        endif()\n    endif()\n\n\tif(libPaths)\n\t\tlist(REMOVE_DUPLICATES libPaths)\n        foreach(libPath ${libPaths})\n            get_filename_component(path ${libPath} PATH)\n            list(APPEND libPaths ${path})\n        endforeach()\n\tendif()\n\n\n\t## possible speciale dir(s) according to the build system and OS\n\tif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\t\tset(BUILD_TYPES_FOR_DLL \"x64\")\n\t\tif(WIN32)\n\t\t\tlist(APPEND BUILD_TYPES_FOR_DLL \"Win64\")\n\t\tendif()\n\telse()\n\t\tset(BUILD_TYPES_FOR_DLL \"x86\")\n\t\tif(WIN32)\n\t\t\tlist(APPEND BUILD_TYPES_FOR_DLL \"Win32\")\n\t\tendif()\n\tendif()\n\n\n\t## Try to append as more as possible paths to find dependencies (here, mainly for *.dll)\n\tforeach(dir ${inst_run_DIRS} ${libPaths})\n\t\tif(EXISTS \"${dir}/bin\")\n\t\t\tlist(APPEND inst_run_DIRS \"${dir}/bin\")\n        elseif(EXISTS \"${dir}\")\n            list(APPEND inst_run_DIRS \"${dir}\")\n\t\tendif()\n\tendforeach()\n    list(REMOVE_DUPLICATES inst_run_DIRS)\n\tforeach(dir ${inst_run_DIRS})\n\t\tif(EXISTS \"${dir}\")\n\t\t\tlist(APPEND argDirs ${dir})\n\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\tif(EXISTS \"${dir}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tlist(APPEND argDirs \"${dir}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\t\tif(EXISTS \"${dir}/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\t\tif(EXISTS \"${dir}/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tendif()\n\t\t\t\tendif()\n\t\t\tendforeach()\n\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\tif(EXISTS \"${dir}/${OUTPUTCONFIG}\")\n\t\t\t\t\tlist(APPEND argDirs \"${dir}/${OUTPUTCONFIG}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${dir}/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendforeach()\n\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\tif(EXISTS \"${dir}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tlist(APPEND argDirs \"${dir}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${dir}/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND argDirs \"${dir}/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\tendif()\n\tendforeach()\n\tif(argDirs)\n\t\tlist(REMOVE_DUPLICATES argDirs)\n\tendif()\n\n\n\t## Try to append as more possible paths to find dependencies (here, mainly for *.dll)\n\tforeach(packageName ${inst_run_TARGET_PACKAGES})\n\t\tif(EXISTS \"${${packageName}_DIR}\")\n\t\t\tlist(APPEND packageDirs ${${packageName}_DIR})\n\t\t\tlist(APPEND packageDirs ${${packageName}_DIR}/bin)\n\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${OUTPUTCONFIG}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${BUILD_TYPE_FOR_DLL}/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tendif()\n\t\t\t\tendif()\n\t\t\tendforeach()\n\t\t\tforeach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) ## for windows multi-generator (MSVC)\n\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}\")\n\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${OUTPUTCONFIG}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendforeach()\n\t\t\tif(CMAKE_BUILD_TYPE) ## for single generator (makefiles)\n\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}\")\n\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}\")\n\t\t\t\tendif()\n\t\t\t\tforeach(BUILD_TYPE_FOR_DLL ${BUILD_TYPES_FOR_DLL})\n\t\t\t\t\tif(EXISTS \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\t\tlist(APPEND packageDirs \"${${packageName}_DIR}/bin/${CMAKE_BUILD_TYPE}/${BUILD_TYPE_FOR_DLL}\")\n\t\t\t\t\tendif()\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\telse()\n\t\t\tset(${packageName}_DIR \"$ENV{${packageName}_DIR}\" CACHE PATH \"${packageName}_DIR root directory for looking for dirs containning *.dll\")\n\t\tendif()\n\tendforeach()\n\tif(packageDirs)\n\t\tlist(REMOVE_DUPLICATES packageDirs)\n\tendif()\n\n\n\tset(dirsToLookFor \"${EXEC_PATH}\")\n\tif(packageDirs)\n\t\tlist(APPEND dirsToLookFor ${packageDirs})\n\tendif()\n\tif(argDirs)\n\t\tlist(APPEND dirsToLookFor ${argDirs})\n\tendif()\n\tget_property(used_LINK_DIRECTORIES DIRECTORY PROPERTY LINK_DIRECTORIES)\n\tif (used_LINK_DIRECTORIES)\n\t\tlist(APPEND dirsToLookFor ${used_LINK_DIRECTORIES})\n\t\tlist(REMOVE_DUPLICATES dirsToLookFor)\n\tendif()\n\n\n    ## handle plugins\n\tset(pluginsList \"\")\n    include(parse_arguments_multi) ## this function will process recursively items of the sub-list [default print messages]\n    function(parse_arguments_multi_function results)\n        cmake_parse_arguments(pamf \"VERBOSE\" \"PLUGIN_PATH_DEST;FROM_REL_PATH;EXEC_PATH;COMPONENT\" \"\" ${ARGN}) ## EXEC_PATH and COMPONENT are for exclusive internal use\n\t\tlist(REMOVE_DUPLICATES pamf_UNPARSED_ARGUMENTS)\n        foreach(PLUGIN_PATH_NAME ${pamf_UNPARSED_ARGUMENTS})\n            if(EXISTS ${PLUGIN_PATH_NAME})\n                if(IS_DIRECTORY ${PLUGIN_PATH_NAME})\n                    if(pamf_VERBOSE)\n                        message(WARNING \"${PLUGIN_PATH_NAME} IS_DIRECTORY, cannot installed a directory, please give a path filename\")\n                    endif()\n                else()\n                    if(NOT pamf_PLUGIN_PATH_DEST)\n                        set(PLUGIN_PATH_DEST ${pamf_EXEC_PATH}) ## the default dest value\n\t\t\t\t\telse()\n\t\t\t\t\t\tset(PLUGIN_PATH_DEST ${pamf_PLUGIN_PATH_DEST})\n                    endif()\n\n\t\t\t\t\tif(pamf_FROM_REL_PATH)\n\t\t\t\t\t\tfile(TO_CMAKE_PATH ${PLUGIN_PATH_NAME} PLUGIN_PATH_NAME)\n\t\t\t\t\t\tget_filename_component(PLUGIN_PATH ${PLUGIN_PATH_NAME} PATH)\n\t\t\t\t\t\tunset(PLUGIN_PATH_LIST)\n\t\t\t\t\t\tunset(PLUGIN_PATH_LIST_COUNT)\n\t\t\t\t\t\tunset(PLUGIN_REL_PATH_LIST)\n\t\t\t\t\t\tunset(PLUGIN_REL_PATH)\n\t\t\t\t\t\tstring(REPLACE \"/\" \";\" PLUGIN_PATH_LIST ${PLUGIN_PATH}) ## create a list of dir\n\t\t\t\t\t\tlist(FIND \tPLUGIN_PATH_LIST ${pamf_FROM_REL_PATH} id)\n\t\t\t\t\t\tlist(LENGTH PLUGIN_PATH_LIST PLUGIN_PATH_LIST_COUNT)\n\t\t\t\t\t\tif(${id} GREATER 0)\n\t\t\t\t\t\t\tmath(EXPR id \"${id}+1\") ## matches relative path not include\n\t\t\t\t\t\t\tmath(EXPR PLUGIN_PATH_LIST_COUNT \"${PLUGIN_PATH_LIST_COUNT}-1\") ## the end of the list\n\t\t\t\t\t\t\tforeach(i RANGE ${id} ${PLUGIN_PATH_LIST_COUNT})\n\t\t\t\t\t\t\t\tlist(GET \tPLUGIN_PATH_LIST \t${i} out)\n\t\t\t\t\t\t\t\tlist(APPEND PLUGIN_REL_PATH_LIST \t${out})\n\t\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\t\tforeach(dir ${PLUGIN_REL_PATH_LIST})\n\t\t\t\t\t\t\t\tset(PLUGIN_REL_PATH \"${PLUGIN_REL_PATH}/${dir}\")\n\t\t\t\t\t\t\tendforeach()\n\t\t\t\t\t\tendif()\n\t\t\t\t\t\tset(PLUGIN_PATH_DEST ${PLUGIN_PATH_DEST}${PLUGIN_REL_PATH})\n\t\t\t\t\tendif()\n\n                    install(FILES ${PLUGIN_PATH_NAME} CONFIGURATIONS ${inst_run_CONFIG_TYPE} DESTINATION ${PLUGIN_PATH_DEST} COMPONENT ${pamf_COMPONENT})\n                    get_filename_component(pluginName ${PLUGIN_PATH_NAME} NAME)\n                    if(IS_ABSOLUTE ${PLUGIN_PATH_DEST})\n                    else()\n                        set(PLUGIN_PATH_DEST ${inst_run_INSTALL_FOLDER}/${PLUGIN_PATH_DEST})\n                    endif()\n                    list(APPEND pluginsList ${PLUGIN_PATH_DEST}/${pluginName})\n                endif()\n            else()\n                message(WARNING \"You need to provide a valid PLUGIN_PATH_NAME\")\n                set(pluginsList )\n            endif()\n        endforeach()\n        set(${results} ${pluginsList} PARENT_SCOPE)\n    endfunction()\n\n    if(inst_run_VERBOSE)\n        list(APPEND extra_flags_to_add VERBOSE)\n    endif()\n    list(APPEND extra_flags_to_add EXEC_PATH ${EXEC_PATH} COMPONENT ${inst_run_COMPONENT}) ## for internal use inside overloaded function\n    list(LENGTH inst_run_PLUGINS inst_run_PLUGINS_count)\n    if(${inst_run_PLUGINS_count} GREATER 0)\n        parse_arguments_multi(PLUGIN_PATH_NAME inst_run_PLUGINS ${inst_run_PLUGINS} ## see internal overload parse_arguments_multi_function for processing each sub-list\n                                NEED_RESULTS ${inst_run_PLUGINS_count}  ## this is used to check when we are in the first loop (in order to reset parse_arguments_multi_results)\n                                EXTRAS_FLAGS ${extra_flags_to_add}      ## this is used to allow catching additional internal flags of our overloaded function\n        )\n    endif()\n\n    #message(parse_arguments_multi_results = ${parse_arguments_multi_results})\n    list(APPEND pluginsList ${parse_arguments_multi_results})\n\n\n\n\t## Install rules for required system runtimes such as MSVCRxx.dll\n\tset(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON)\n\tinclude(InstallRequiredSystemLibraries)\n\tif(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)\n\t\tinstall(FILES \t\t\t${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}\n\t\t\t\tCONFIGURATIONS \t${inst_run_CONFIG_TYPE}\n\t\t\t\tDESTINATION \t${EXEC_PATH}\n\t\t\t\tCOMPONENT   \t${inst_run_COMPONENT}\n\t\t)\n\tendif()\n\n\t## print what we are doing to do\n\tif(inst_run_VERBOSE)\n\t\tmessage(STATUS \"[install_runtime] On install target call, cmake will try to resolve dependencies for given app:\\n ${installedFilePathTargetAppToResolve} (with plausible postfix: ${inst_run_PLAUSIBLES_POSTFIX})\")\n\t\tif(pluginsList)\n\t\t\tmessage(STATUS \"   and also for plugins :\")\n\t\t\tforeach(plugin ${pluginsList})\n\t\t\t\tmessage(STATUS \"      ${plugin}\")\n\t\t\tendforeach()\n\t\tendif()\n\t\tmessage(STATUS \"   Looking for dependencies into:\")\n\t\tforeach(dir ${dirsToLookFor})\n\t\t\tmessage(STATUS \"      ${dir}\")\n\t\tendforeach()\n\tendif()\n\n\t## Install rules for required dependencies libs/plugins for the target app\n\t## will resolve all installed target files with config modes postfixes\n\tstring(TOUPPER ${inst_run_CONFIG_TYPE} inst_run_CONFIG_TYPE_UC)\n\tget_target_property(postfix ${inst_run_TARGET} \"${inst_run_CONFIG_TYPE_UC}_POSTFIX\")\n\tinstall(CODE \"set(target \t\t\t\t\t\t\\\"${inst_run_TARGET}\\\")\" \t\t\t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(inst_run_CONFIG_TYPE \t\t\t\\\"${inst_run_CONFIG_TYPE}\\\")\" \t\t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(inst_run_INSTALL_FOLDER \t\t\\\"${inst_run_INSTALL_FOLDER}\\\")\" \t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(app\t \t\t\t\t\t\t\\\"${EXEC_PATH}/${EXEC_NAME}${postfix}${CMAKE_EXECUTABLE_SUFFIX}\\\")\" \tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE \"set(dirsToLookFor \t\t\t\t\\\"${dirsToLookFor}\\\")\" \t\t\t\t\t\t\t\t\t\t\t\t\tCOMPONENT ${inst_run_COMPONENT}  CONFIGURATIONS ${CONFIG_TYPE})\n\tinstall(CODE\n\t\t[[\n\t\t\tif(\"${CMAKE_INSTALL_CONFIG_NAME}\" STREQUAL \"${inst_run_CONFIG_TYPE}\")\n\t\t\t\tmessage(STATUS \"Installing ${target} dependencies...\")\n\n\t\t\t\tfile(GET_RUNTIME_DEPENDENCIES\n\t\t\t\t\tEXECUTABLES ${app}\n\t\t\t\t\tRESOLVED_DEPENDENCIES_VAR _r_deps\n\t\t\t\t\tUNRESOLVED_DEPENDENCIES_VAR _u_deps\n\t\t\t\t\tCONFLICTING_DEPENDENCIES_PREFIX _c_deps\n\t\t\t\t\tDIRECTORIES ${dirsToLookFor}\n\t\t\t\t\tPRE_EXCLUDE_REGEXES \"api-ms-*\"\n\t\t\t\t\tPOST_EXCLUDE_REGEXES \".*system32/.*\\\\.dll\" \".*SysWOW64/.*\\\\.dll\"\n\t\t\t\t)\n\t\t\t\n\t\t\t\tif(_u_deps)\n\t\t\t\t\tmessage(WARNING \"There were unresolved dependencies for executable ${EXEC_FILE}: \\\"${_u_deps}\\\"!\")\n\t\t\t\tendif()\n\t\t\t\tif(_c_deps_FILENAMES)\n\t\t\t\t\tmessage(WARNING \"There were conflicting dependencies for executable ${EXEC_FILE}: \\\"${_c_deps_FILENAMES}\\\"!\")\n\t\t\t\tendif()\n\t\t\t\n\t\t\t\tforeach(_file ${_r_deps})\n\t\t\t\t\tfile(INSTALL\n\t\t\t\t\tDESTINATION \"${inst_run_INSTALL_FOLDER}/bin\"\n\t\t\t\t\tTYPE SHARED_LIBRARY\n\t\t\t\t\tFOLLOW_SYMLINK_CHAIN\n\t\t\t\t\tFILES \"${_file}\"\n\t\t\t\t)\n\t\t\t\tendforeach()\n\t\t\tendif()\n\t\t]]\n\t   COMPONENT ${inst_run_COMPONENT} CONFIGURATIONS ${CONFIG_TYPE}\n\t)\n\t\n\nendfunction()\n\n## High level macro to install resources in the correct folder\n##\n## EXECUTABLE: [opt] option to copy files as programs\n## RELATIVE  : [opt] copy files relatively to current folder\n## TYPE      : [opt] type and folder where to store the files\n## FOLDER    : [opt] subfolder to use\n## FILES     : [opt] contains a list of resources files to copy to install folder\nmacro(ibr_install_rsc target)\n\tcmake_parse_arguments(install_rsc_${target} \"EXECUTABLE;RELATIVE\" \"TYPE;FOLDER\" \"FILES\" ${ARGN})\n\tset(rsc_target \"${target}_${install_rsc_${target}_TYPE}\")\n\n\tif(install_rsc_${target}_FOLDER)\n\t\tset(rsc_folder \"${install_rsc_${target}_TYPE}/${install_rsc_${target}_FOLDER}\")\n\telse()\n\t\tset(rsc_folder \"${install_rsc_${target}_TYPE}\")\n\tendif()\n\n\tadd_custom_target(${rsc_target}\n\t\t\t\t\tCOMMENT \"run the ${install_rsc_${target}_TYPE} installation only for ${target} (component ${rsc_target})\"\n\t\t\t\t\tVERBATIM)\n\tforeach(scriptFile ${install_rsc_${target}_FILES})\n\t\tif(install_rsc_${target}_RELATIVE)\n\t\t\tfile(RELATIVE_PATH relativeFilename ${CMAKE_CURRENT_SOURCE_DIR} ${scriptFile})\n\t\telse()\n\t\t\tget_filename_component(relativeFilename ${scriptFile} NAME)\n\t\tendif()\n\n\t\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\tadd_custom_command(TARGET ${rsc_target} POST_BUILD\n\t\t\t\t\t\t\tCOMMAND ${CMAKE_COMMAND} -E\n\t\t\t\t\t\t\tcopy_if_different ${scriptFile} ${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/${rsc_folder}/${relativeFilename})\n\t\tendif()\n\t\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\tadd_custom_command(TARGET ${rsc_target} POST_BUILD\n\t\t\t\t\t\t\tCOMMAND ${CMAKE_COMMAND} -E\n\t\t\t\t\t\t\tcopy_if_different ${scriptFile} ${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/${rsc_folder}/${relativeFilename})\n\t\tendforeach()\n\tendforeach()\n\n\tget_target_property(INSTALL_RSC_BUILD_FOLDER ${target} FOLDER)\n\tset_target_properties(${rsc_target} PROPERTIES FOLDER ${INSTALL_RSC_BUILD_FOLDER})\n\n\tadd_dependencies(${target} ${rsc_target})\n\tadd_dependencies(PREBUILD ${rsc_target})\n\n\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\tresourceFile(ADD ${rsc_folder} CONFIG_TYPE ${CMAKE_BUILD_TYPE} FILE_PATH \"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/ibr_resources.ini\")\n\t\t\n\t\tif(install_rsc_${target}_EXECUTABLE)\n\t\t\tinstall(\n\t\t\t\tPROGRAMS ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/${rsc_folder}\"\n\t\t\t)\n\t\telse()\n\t\t\tinstall(\n\t\t\t\tFILES ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}/${rsc_folder}\"\n\t\t\t)\n\t\tendif()\n\tendif()\n\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\tresourceFile(ADD ${rsc_folder} CONFIG_TYPE ${CONFIG_TYPES} FILE_PATH \"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/ibr_resources.ini\")\n\t\t\n\t\tif(install_rsc_${target}_EXECUTABLE)\n\t\t\tinstall(\n\t\t\t\tPROGRAMS ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/${rsc_folder}\"\n\t\t\t)\n\t\telse()\n\t\t\tinstall(\n\t\t\t\tFILES ${install_rsc_${target}_FILES}\n\t\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}/${rsc_folder}\"\n\t\t\t)\n\t\tendif()\n\tendforeach()\nendmacro()\n\n\n## High level macro to install in an homogen way all our ibr targets (it use some functions inside this file)\n##\n## RSC_FILE_ADD : [opt] is used to auto write/append relative paths of target resources into a common file\n## INSTALL_PDB  : [opt] is used to auto install PDB file (when using MSVC according to the target type)\n## STANDALONE   : [opt] bool ON/OFF var to call install_runtime or not (for bundle resolution)\n##       DIRS   : [opt] used if STANDALONE set to ON, see install_runtime doc\n##       PLUGINS: [opt] used if STANDALONE set to ON, see install_runtime doc\n## MSVC_CMD     : [opt] used to specify an absolute filePathName application to launch with the MSVC IDE Debugger associated to this target (project file)\n## MSVC_ARGS    : [opt] load the MSVC debugger with correct settings (app path, args, working dir)\n##\nmacro(ibr_install_target target)\n\tcmake_parse_arguments(ibrInst${target} \"VERBOSE;INSTALL_PDB\" \"COMPONENT;MSVC_ARGS;STANDALONE;RSC_FOLDER\" \"SHADERS;RESOURCES;SCRIPTS;DIRS;PLUGINS\" ${ARGN})\n\t\n\tif(ibrInst${target}_RSC_FOLDER)\n\t\tset(rsc_folder \"${ibrInst${target}_RSC_FOLDER}\")\n\telse()\n\t\tset(rsc_folder \"${target}\")\n\tendif()\n\n\tif(ibrInst${target}_SHADERS)\n\t\tibr_install_rsc(${target} EXECUTABLE TYPE \"shaders\" FOLDER ${rsc_folder} FILES \"${ibrInst${target}_SHADERS}\")\n    endif()\n\t\n\tif(ibrInst${target}_RESOURCES)\n\t\tibr_install_rsc(${target} TYPE \"resources\" FOLDER ${rsc_folder} FILES \"${ibrInst${target}_RESOURCES}\")\n    endif()\n\t\n\tif(ibrInst${target}_SCRIPTS)\n\t\tibr_install_rsc(${target} EXECUTABLE TYPE \"scripts\" FOLDER ${rsc_folder} FILES \"${ibrInst${target}_SCRIPTS}\")\n    endif()\n\n    if(ibrInst${target}_COMPONENT)\n        set(installCompArg COMPONENT ${ibrInst${target}_COMPONENT})\n        ## Create a custom install target based on COMPONENT\n        installTargetProject(${target} ${ibrInst${target}_COMPONENT})\n\tendif()\n\t\n\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\tstring(TOUPPER ${CMAKE_BUILD_TYPE} CONFIG_TYPES_UC)\n\t\tset_target_properties(${target} PROPERTIES ${CONFIG_TYPES_UC}_POSTFIX \t\"${CMAKE_${CONFIG_TYPES_UC}_POSTFIX}\")\n\t\tget_target_property(CURRENT_TARGET_BUILD_TYPE_POSTFIX ${target} ${CONFIG_TYPES_UC}_POSTFIX)\n\tendif()\n\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\tset_target_properties(${target} PROPERTIES ${CONFIG_TYPES_UC}_POSTFIX \t\"${CMAKE_${CONFIG_TYPES_UC}_POSTFIX}\")\n\t\tget_target_property(CURRENT_TARGET_BUILD_TYPE_POSTFIX ${target} ${CONFIG_TYPES_UC}_POSTFIX)\n\tendforeach()\n\n\t## Specify default installation rules\n\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\tstring(TOUPPER ${CMAKE_BUILD_TYPE} CONFIG_TYPES_UC)\n\t\tinstall(TARGETS\t${target}\n\t\t\tCONFIGURATIONS ${CMAKE_BUILD_TYPE}\n\t\t\tLIBRARY\t\tDESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t\tARCHIVE\t\tDESTINATION ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t\tRUNTIME \tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}} ${installCompArg}\n\t\t)\n\tendif()\n\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\tinstall(TARGETS\t${target}\n\t\t\tCONFIGURATIONS ${CONFIG_TYPES}\n\t\t\tLIBRARY\t\tDESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t\tARCHIVE\t\tDESTINATION ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t\tRUNTIME \tDESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}} ${installCompArg}\n\t\t)\n\tendforeach()\n\n    if(ibrInst${target}_INSTALL_PDB)\n        if(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\tinstallPDB(${target} ${CMAKE_BUILD_TYPE}\n\t\t\t\tLIBRARY_DEST ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t\tARCHIVE_DEST ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t\tRUNTIME_DEST ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE}}\n\t\t\t)\n        endif()\n        foreach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\tinstallPDB(${target} ${CONFIG_TYPES}\n\t\t\t\tLIBRARY_DEST ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\tARCHIVE_DEST ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\tRUNTIME_DEST ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t)\n        endforeach()\n    endif()\n\n    ## install dynamic necessary dependencies\n    if(ibrInst${target}_STANDALONE)\n        get_target_property(type ${target} TYPE)\n        if(${type} MATCHES \"EXECUTABLE\")\n\n            if(ibrInst${target}_VERBOSE)\n                set(VERBOSE VERBOSE)\n            else()\n                set(VERBOSE )\n\t\t\tendif()\n\t\t\t\n\t\t\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\t\tinstall_runtime(bin/${target}${CMAKE_EXECUTABLE_SUFFIX} ## default relative to CMAKE_INSTALL_PREFIX\n\t\t\t\t\tINSTALL_FOLDER\t\t\"${CMAKE_INSTALL_PREFIX_${CMAKE_BUILD_TYPE}}\"\n\t\t\t\t\tCONFIG_TYPE\t\t\t${CMAKE_BUILD_TYPE}\n\t\t\t\t\t${VERBOSE}\n\t\t\t\t\tTARGET              ${target}\n\t\t\t\t\t${installCompArg}\n\t\t\t\t\tPLUGINS\t## will be installed\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_PLUGINS}\n\t\t\t\t\tDIRS\t\t\t\t${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_DIRS}\n\t\t\t\t)\n\t\t\tendif()\n\t\t\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\t\tinstall_runtime(bin/${target}${CMAKE_EXECUTABLE_SUFFIX} ## default relative to CMAKE_INSTALL_PREFIX\n\t\t\t\t\tINSTALL_FOLDER\t\t\"${CMAKE_INSTALL_PREFIX_${CONFIG_TYPES_UC}}\"\n\t\t\t\t\tCONFIG_TYPE\t\t\t${CONFIG_TYPES}\n\t\t\t\t\t${VERBOSE}\n\t\t\t\t\tTARGET              ${target}\n\t\t\t\t\t${installCompArg}\n\t\t\t\t\tPLUGINS\t## will be installed\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_PLUGINS}\n\t\t\t\t\tDIRS\t\t\t\t${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPES_UC}}\n\t\t\t\t\t\t\t\t\t\t${ibrInst${target}_DIRS}\n\t\t\t\t)\n\t\t\tendforeach()\n        else()\n            message(WARNING \"STANDALONE option is only compatible with EXECUTABLES target type. Skip the STANDALONE installation process.\")\n        endif()\n    endif()\n\n    ## Provide a way to directly load the MSVC debugger with correct settings\n    if(MSVC)\n        if(ibrInst${target}_MSVC_CMD)  ## command absolute filePathName is optional as the default is to use the installed target file application\n            set(msvcCmdArg  COMMAND ${ibrInst${target}_MSVC_CMD}) ## flag following by the value (both to pass to the MSVCsetUserCommand function)\n        endif()\n        if(ibrInst${target}_MSVC_ARGS) ## args (between quotes) are optional\n            set(msvcArgsArg ARGS ${ibrInst${target}_MSVC_ARGS})   ## flag following by the value (both to pass to the MSVCsetUserCommand function)\n        endif()\n        get_target_property(type ${target} TYPE)\n        if( (ibrInst${target}_MSVC_CMD OR ibrInst${target}_MSVC_ARGS) OR (${type} MATCHES \"EXECUTABLE\") )\n\t\t\tinclude(MSVCsetUserCommand)\n\t\t\tif(DEFINED CMAKE_BUILD_TYPE)\t\t\t\t\t\t## for make/nmake based\n\t\t\t\tMSVCsetUserCommand(\t${target}\n\t\t\t\t\tPATH \t\t\t${CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE}} ##FILE option not necessary since it deduced from targetName\n\t\t\t\t\t\t\t\t\tARGS\t\t\t\t\"${SIBR_PROGRAMARGS}\"\n\t\t\t\t\t${msvcCmdArg}\n\t\t\t\t\t#${msvcArgsArg}\n\t\t\t\t\tWORKING_DIR\t\t${CMAKE_OUTPUT_BIN_${CMAKE_BUILD_TYPE}}\n\t\t\t\t)\n\t\t\tendif()\n\t\t\tforeach(CONFIG_TYPES ${CMAKE_CONFIGURATION_TYPES}) \t## for multi config types (MSVC based)\n\t\t\t\tstring(TOUPPER ${CONFIG_TYPES} CONFIG_TYPES_UC)\n\t\t\t\tMSVCsetUserCommand(\t${target}\n\t\t\t\t\tPATH \t\t\t${CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC}} ##FILE option not necessary since it deduced from targetName\n\t\t\t\t\t\t\t\t\tARGS\t\t\t\t\"${SIBR_PROGRAMARGS}\"\n\t\t\t\t\t${msvcCmdArg}\n\t\t\t\t\t#${msvcArgsArg}\n\t\t\t\t\tWORKING_DIR\t\t${CMAKE_OUTPUT_BIN_${CONFIG_TYPES_UC}}\n\t\t\t\t)\n\t\t\tendforeach()\n        elseif(NOT ${type} MATCHES \"EXECUTABLE\")\n            #message(\"Cannot set MSVCsetUserCommand with target ${target} without COMMAND parameter as it is not an executable (skip it)\")\n        endif()\n    endif()\n\nendmacro()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/parse_arguments_multi.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nif(NOT WIN32 OR __parse_arguments_multi_cmake_INCLUDED__)\n\treturn()\nelse()\n\tset(__parse_arguments_multi_cmake_INCLUDED__ ON)\nendif()\n\n## This macro allow to process repeating multi value args from a given function which use cmake_parse_arguments module.\n##\n## cmake_parse_arguments multi args standard behavior:\n##    function(foo)\n##        cmake_parse_arguments(arg \"\" \"\" \"MULTI\" ${ARGN})\n##        foreach(item IN LISTS arg_MULTI)\n##            message(STATUS \"${item}\")\n##        endforeach()\n##    endfunction()\n##    foo(MULTI x y MULTI z w)\n##  The above code outputs 'z' and 'w'. It originally expected it to output all of 'x' 'y' 'z' 'w'.\n##\n## Using this macro inside a function which want to handle repeating multi args values\n## will recursively iterate onto the multi tags list to process each sub list.\n## It take as 1st argument the subTag flag to separate sub list from the main multi list.\n## It take as 2nd argument the nameList of the main multi list (the multiValuesArgs from cmake_parse_arguments: here it is MULTI in the example)\n## and that's why it is important that it should be a macro and not a function (to get access to external variable).\n## Then you give the content of this list allowing to be processed by the macro.\n##\n## parse_arguments_multi macro call a parse_arguments_multi_function which do actually the process from the given sub-list.\n## By default this function only print infos about what variables you are trying to pass/process (only verbose messages),\n## but, by overloading this cmake function, you will be able to externalize the process of your multi argument list.\n##\n## Usage (into a function) : \n## parse_arguments_multi(<multiArgsSubTag> <multiArgsList> <multiArgsListContent> \n##      [NEED_RESULTS <multiArgsListSize>] [EXTRAS_FLAGS <...> <...> ...]\n## )\n##\n## Simple usage example [user point of view]:\n## foo(MULTI\n##    SUB_MULTI x y\n##    SUB_MULTI z w\n## )\n##\n## Simple usage example [inside a function]:\n##    function(foo)\n##        cmake_parse_arguments(arg \"\" \"\" \"MULTI\" ${ARGN})\n##        include(parse_arguments_multi)\n##        function(parse_arguments_multi_function )\n##          #message(\"I'm an overloaded cmake function used by parse_arguments_multi\")\n##          #message(\"I'm processing first part of my sub list: ${ARGN}\")\n##          message(\"ARGV0=${ARGV0}\")\n##          message(\"ARGV1=${ARGV1}\")\n##        endfunction()\n##        parse_arguments_multi(SUB_MULTI arg_MULTI ${arg_MULTI}) ## this function will process recusively items of the sub-list [default print messages]\n##    endfunction()\n##\n##  Will print:\n##      ARGV0=z\n##      ARGV1=w\n##      ARGV0=x\n##      ARGV1=y\n##\n## WARNING : DO NEVER ADD EXTRA THINGS TO parse_arguments_multi MACRO :\n##          parse_arguments_multi(SUB_MULTI arg_MULTI ${arg_MULTI} EXTRAS foo bar SOMTHING) => will failed !!\n## use EXTRAS_FLAGS instead !!\n##\n## Advanced usage example [user point of view]:\n## bar(C:/prout/test.exe VERBOSE \n##      PLUGINS\n##          PLUGIN_PATH_NAME x      PLUGIN_PATH_DEST w\n##          PLUGIN_PATH_NAME a b    PLUGIN_PATH_DEST y\n##          PLUGIN_PATH_NAME c\n## )\n##\n## Advanced usage example [inside a function]:\n##    function(bar execFilePathName)\n##        cmake_parse_arguments(arg \"VERBOSE\" \"\" \"PLUGINS\" ${ARGN})\n##\n##        include(parse_arguments_multi)\n##        function(parse_arguments_multi_function results)\n##            cmake_parse_arguments(pamf \"VERBOSE\" \"PLUGIN_PATH_DEST;EXEC_PATH\" \"\" ${ARGN}) ## EXEC_PATH is for internal use\n##            message(\"\")\n##            message(\"I'm an overloaded cmake function used by parse_arguments_multi from install_runtime function\")\n##            message(\"I'm processing first part of my sub list: ${ARGN}\")\n##            message(\"PLUGIN_PATH_NAME = ${pamf_UNPARSED_ARGUMENTS}\")\n##            message(pamf_VERBOSE = ${pamf_VERBOSE})\n##            message(\"pamf_PLUGIN_PATH_DEST = ${pamf_PLUGIN_PATH_DEST}\")\n##            message(pamf_EXEC_PATH = ${pamf_EXEC_PATH})\n##            if(NOT ${pamf_PLUGIN_PATH_DEST})\n##              set(pamf_PLUGIN_PATH_DEST ${pamf_EXEC_PATH})\n##            endif()\n##            foreach(plugin ${pamf_UNPARSED_ARGUMENTS})\n##              get_filename_component(pluginName ${plugin} NAME)\n##              list(APPEND pluginsList ${pamf_PLUGIN_PATH_DEST}/${pluginName})\n##            endforeach()\n##            set(${results} ${pluginsList} PARENT_SCOPE)\n##        endfunction()\n##\n##        if(arg_VERBOSE)\n##            list(APPEND extra_flags_to_add VERBOSE) ## here we transmit the VERNOSE flag\n##        endif()\n##        get_filename_component(EXEC_PATH ${execFilePathName} PATH) ## will be the default value if PLUGIN_PATH_DEST option is not provided\n##        list(APPEND extra_flags_to_add EXEC_PATH ${EXEC_PATH})  \n##        list(LENGTH arg_PLUGINS arg_PLUGINS_count)\n##        parse_arguments_multi(PLUGIN_PATH_NAME arg_PLUGINS ${arg_PLUGINS}\n##                            NEED_RESULTS ${arg_PLUGINS_count}  ## this is used to check when we are in the first loop (in order to reset parse_arguments_multi_results)\n##                            EXTRAS_FLAGS ${extra_flags_to_add} ## this is used to allow catching VERBOSE and PLUGIN_PATH_DEST flags of our overloaded function\n##        )\n##    endfunction()\n##    message(parse_arguments_multi_results = ${parse_arguments_multi_results}) ## list of the whole pluginsList\n##    #Will print w/x;a/y;b/y;C:/prout/c\n##\n##  NOTE that here, since our overloaded function need to provide a result list, we use the other parse_arguments_multi_function signature (the which one with a results arg)\n##\n\nfunction(parse_arguments_multi_function_default) ## used in case of you want to reset the default behavior of this function process\n    message(\"[default function] parse_arguments_multi_function(ARGC=${ARGC} ARGV=${ARGV} ARGN=${ARGN})\")\n    message(\"This function is used by parse_arguments_multi and have to be overloaded to process sub list of multi values args\")\nendfunction()\n\nfunction(parse_arguments_multi_function )   ## => the function to overload\n    parse_arguments_multi_function_default(${ARGN})\nendfunction()\n\n## first default signature above\n##------------------------------\n## second results signature behind\n\nfunction(parse_arguments_multi_function_default result) ## used in case of you want to reset the default behavior of this function process\n    message(\"[default function] parse_arguments_multi_function(ARGC=${ARGC} ARGV=${ARGV} ARGN=${ARGN})\")\n    message(\"This function is used by parse_arguments_multi and have to be overloaded to process sub list of muluti values args\")\nendfunction()\n\nfunction(parse_arguments_multi_function result)   ## => the function to overload\n    parse_arguments_multi_function_default(result ${ARGN})\nendfunction()\n\n## => the macro to use inside your function which use cmake_parse_arguments\n# NOTE: entry point of parse_arguments_multi, which is called from win3rdPart)\nmacro(parse_arguments_multi multiArgsSubTag multiArgsList #<${multiArgsList}> the content of the list\n)\n    # message (STATUS \"\")\n    # message(STATUS \"calling parse_arguemnts_multi defined in parse_arguments_multi.cmake:141\")\n    # message(STATUS \"multiArgsSubTag = ${multiArgsSubTag}\")\t# CHECK_CACHED_VAR\n    # message(STATUS \"multiArgsList = ${multiArgsList}\")\t# it contains the name of the variable which is holding the list i.e w3p_MULTI_SET\n    # message(STATUS \"value of ${multiArgsList} = ${${multiArgsList}}\") # a semicolon separated list of values passed to SET or MULTISET keyword in win3rdParty\n    # message(STATUS \"actual values ARGN = ${ARGN}\")  # the same as ${${multiArgsList}}\n\n    ## INFO\n    ## starting from CMake 3.5 cmake_parse_arguments is not a module anymore and now is a native CMake command.\n    ## the behaviour is different though\n    ## In CMake 3.4, if you pass multiple times a multi_value_keyword, CMake returns the values of the LAST match\n    ## In CMake 3.5 and above, CMake returns the whole list of values that were following that multi_value_keyword\n    ## example:\n    ## cmake_parse_arguments(\n    ##\t\t\t<prefix>\n    ##\t\t\t\"\"\t\t# options\n    ##\t\t\t\"\"\t\t# one value keywords\n    ##\t\t\t\"MY_MULTI_VALUE_TAG\"\n    ##\t\t\t\tMY_MULTI_VALUE_TAG value1 value2\n    ##\t\t\t\tMY_MULTI_VALUE_TAG value3 value4\n    ##\t\t\t\tMY_MULTI_VALUE_TAG value5 value6\n    ##\t\t\t)\n    ## result in CMake 3.4\n    ## <prefix>_MY_MULTI_VALUE_TAG = \"value5;value6\"\n    ##\n    ## result in CMake 3.8\n    ## <prefix>_MY_MULTI_VALUE_TAG = \"value5;value6\"\n\n    #include(CMakeParseArguments) #module CMakeParseArguments is obsolete since cmake 3.5\n    # cmake_parse_arguments (<prefix> <options> <one_value_keywords> <multi_value_keywords> args)\n    # <options> : options (flags) pass to the macro\n    # <one_value_keywords> : options that neeed a value\n    # <multi_value_keywords> : options that neeed more than one value\n    cmake_parse_arguments(_pam \"\" \"NEED_RESULTS\" \"${multiArgsSubTag};EXTRAS_FLAGS\" ${ARGN})\n    \n    ## multiArgsList is the name of the list used by the multiValuesOption flag from the cmake_parse_arguments of the user function\n    ## that's why we absolutly need to use MACRO here (and also for passing parse_arguments_multi_results when NEED_RESULTS flag is set)\n    \n    ## for debugging\n    #message(\"\")\n    #message(\"[parse_arguments_multi] => ARGN = ${ARGN}\")\n    #message(\"_pam_NEED_RESULTS=${_pam_NEED_RESULTS}\")\n    #message(\"_pam_EXTRAS_FLAGS=${_pam_EXTRAS_FLAGS}\")\n    # foreach(var ${_pam_${multiArgsSubTag}})\n    #     message(\"arg=${var}\")\n    # endforeach()\n\n    if (${CMAKE_VERSION} VERSION_GREATER \"3.5\")\n        # lets make ${_pam_${multiArgsSubTag}} behave as it is in version 3.4\n        # that means, cmake_parse_arguments should have only the last values of a multi set for a given keyword\n\n        # message(\"\")\n        # message(\"values in multiArgsList\")\n        # foreach(val ${${multiArgsList}})\n        #     message(STATUS ${val})\n        # endforeach()\n        # message(\"end values in multiArgsList\")\n\n\n        set(lastIndexFound OFF)\n        list(LENGTH ${multiArgsList} argnLength)\n        # message(${argnLength})\n        math(EXPR argnLength \"${argnLength}-1\")             # make last index a valid one\n        set(recordIndex 0)\n        set(records \"\")                                     # clear records list\n        set(record0 \"\")                                    # clear first record list\n        foreach(iter RANGE ${argnLength})\n            list(GET ${multiArgsList} ${iter} value)\n            # message(STATUS \"index=${iter} value=${value}\")\n            if (${value} STREQUAL ${multiArgsSubTag})\n                if (lastIndexFound)\n                    list(APPEND records ${recordIndex})    # records store the list NAMES\n                    math(EXPR recordIndex \"${recordIndex}+1\")\n                    set(record${recordIndex} \"\")            # clear record list\n                else ()\n                    set(lastIndexFound ON)\n                endif()\n\n                set(lastIndex ${iter})\n            else ()\n                if (lastIndexFound)\n                    # message(${value})\n                    list(APPEND record${recordIndex} ${value})\n                endif()\n            endif()\n        endforeach()\n\n        # save the last list of values\n        if (lastIndexFound)\n            list(APPEND records ${recordIndex})    # records store the list NAMES\n        endif()\n\n        # set multiArgsList to make it behave like CMake 3.4\n        # message(\"\")\n        # message(\"using my records\")\n        foreach(recordName ${records})\n            # message(${recordName})\n            # foreach(value ${record${recordName}})\n            #     message(${value})\n            # endforeach()\n            # message(\"\")\n            set(_pam_${multiArgsSubTag} ${record${recordName}})\n        endforeach()\n        # message(${_pam_${multiArgsSubTag}})\n\n        # message(\"\")\n        # message(\"using argn\")\n        # foreach(value ${ARGN})\n        #     message(${value})\n        # endforeach()\n    endif() # end if cmake > 3.5\n\n    # message(\"values with pam ${_pam_${multiArgsSubTag}}\")\n\n    ## check and init\n    list(LENGTH ${multiArgsList} globalListCount)\t# GLUT_TRACE: globalListCound=16 in CMake3.4 and CMake3.8\n    # message(STATUS \"nr items in multiArgsList: ${globalListCount}\")\n    math(EXPR globalListCount \"${globalListCount}-1\") ## because it will contain [multiArgsSubTag + ${multiArgsList}]\n    if(_pam_NEED_RESULTS)\n        if(${globalListCount} EQUAL ${_pam_NEED_RESULTS})\n            ## first time we enter into this macro (because we call it recursively)\n            unset(parse_arguments_multi_results)\n        endif()\n    endif()\n    \n    ## process the part of the multi agrs list\n    ## ${ARGN} shouldn't be passed to the function in order to avoid missmatch size list ${multiArgsList} and _pam_${multiArgsSubTag}\n    ## if you want to pass extra internal flags from your function to this callback, use EXTRAS_FLAGS\n    if(_pam_NEED_RESULTS)\n        parse_arguments_multi_function(parse_arguments_multi_function_result ${_pam_${multiArgsSubTag}} ${_pam_EXTRAS_FLAGS})\n        list(APPEND parse_arguments_multi_results ${parse_arguments_multi_function_result})\n    else()\n        # message(STATUS \"about to call parse_arguments_multi_function in parse_arguments_multi.cmake:177 ${_pam_${multiArgsSubTag}} and extra flags ${_pam_EXTRAS_FLAGS}\")\n        parse_arguments_multi_function(${_pam_${multiArgsSubTag}} ${_pam_EXTRAS_FLAGS})\n    endif()\n\n    ## remove just processed items from the main list to process (multiArgsList)\n    list(REVERSE ${multiArgsList})\n    list(LENGTH _pam_${multiArgsSubTag} subTagListCount)\n    unset(ids)\n    foreach(id  RANGE ${subTagListCount})\n         list(APPEND ids ${id})\n    endforeach()\n    list(REMOVE_AT  ${multiArgsList} ${ids})\n    list(REVERSE    ${multiArgsList})\n    \n    ## test if remain sub multi list to process (recursive call) or finish the process\n    list(LENGTH ${multiArgsList} mainTagListCount)\n    if(${mainTagListCount} GREATER 1)\n        ## do not pass ${ARGN} just because it will re pass the initial 2 inputs args and we wont as they was consumed (in order to avoir conflicts)\n        # message(STATUS \"about to call a parse_arguments_multi but without knowing where the definition is going to be taken from\")\n        parse_arguments_multi(${multiArgsSubTag} ${multiArgsList} ${${multiArgsList}} \n                                NEED_RESULTS ${_pam_NEED_RESULTS} EXTRAS_FLAGS ${_pam_EXTRAS_FLAGS}\n            )\n    endif()\nendmacro()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/cmake/windows/sibr_library.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n# NOTE\n# This feature is used to easily download, store and link external dependencies. This\n# requires to prepare pre-compiled libraries (to download). For now, packages have\n# only be prepare for Windows 64-bit with Visual Studio 2012. (You should re-build\n# everything if you want to use another version of Visual Studio/ another compiler).\n\n# NOTE ABOUT UNIX SYSTEMS\n# There is no need for \"searching mechanism\". This function is discard and your\n# libraries should be installed is the standard folders that are:\n#\n# /usr/include/\n# /usr/lib/\n# /usr/lib64/\n# for packages downloaded using apt-get/yum\n# \n# /usr/local/include/\n# /usr/local/lib/\n# /usr/local/lib64/\n# for packages manually installed (\"make install\")\n#\n# if you encounter problems when linking (e.g. lib not found even if it is installed),\n# please check these folders are in your search PATH environment variables.\n\nset(EXTLIBS_PACKAGE_FOLDER \"${CMAKE_SOURCE_DIR}/extlibs\")\n\nfunction(sibr_addlibrary)\n    if(NOT WIN32)\n        return()\n    endif()\n\n    file(MAKE_DIRECTORY ${EXTLIBS_PACKAGE_FOLDER})\n    cmake_parse_arguments(args \"VCID\" \"VERBOSE;TIMEOUT;DEFAULT_USE;NAME;VERSION;MSVC11;MSVC12;MSVC14;MSVC17\" \"MULTI_SET;SET\" ${ARGN})\n\n\n    if (NOT \"${args_VERSION}\" MATCHES \"\")\n        message(WARNING \"VERSION is not implemented yet\")\n    endif()\n\n    set(lcname \"\")\n    set(ucname \"\")\n    string(TOLOWER \"${args_NAME}\" lcname)\n    string(TOUPPER \"${args_NAME}\" ucname)\n\n    set(LIB_PACKAGE_FOLDER \"${EXTLIBS_PACKAGE_FOLDER}/${lcname}\")\n    win3rdParty(${ucname}\n                    $<args_VCID:VCID>\n                    VERBOSE     ${args_VERBOSE}\n                    TIMEOUT     ${args_TIMEOUT}\n                    DEFAULT_USE ${args_DEFAULT_USE}\n                    MSVC11 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC11}\"\n                    MSVC12 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC12}\"\n                    MSVC14 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC14}\" # TODO SV: make sure to build this library if required\n\t\t\t\t\tMSVC17 \"${LIB_PACKAGE_FOLDER}\" \"${args_MSVC17}\"\n                    SET         ${args_SET}\n                    MULTI_SET   ${args_MULTI_SET}\n                )\n\t\t\t\n    # Add include/ directory\n    # and lib/ directories\n\n    # TODO SV: paths not matching with current hierarchy. example: libraw/libraw-0.17.1/include\n    # SR:\tThe link directories will also be used to lookup for dependency DLLs to copy in the install directory.\n    #\t\tSome libraries put the DLLs in the bin/ directory, so we include those.\n    file(GLOB subdirs RELATIVE ${LIB_PACKAGE_FOLDER} ${LIB_PACKAGE_FOLDER}/*)\n    set(dirlist \"\")\n    foreach(dir ${subdirs})\n        if(IS_DIRECTORY ${LIB_PACKAGE_FOLDER}/${dir})\n            # message(\"adding ${LIB_PACKAGE_FOLDER}/${dir}/include/ to the include directories\")\n            include_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/include/\")\n            # message(\"adding ${LIB_PACKAGE_FOLDER}/${dir}/lib[64] to the link directories\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/lib/\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/lib64/\")\n            link_directories(\"${LIB_PACKAGE_FOLDER}/${dir}/bin/\")\n        endif()\n    endforeach()\n\nendfunction()\n\ninclude(FetchContent)\ninclude(git_describe)\n\nfunction(sibr_gitlibrary)\n    cmake_parse_arguments(args \"\" \"TARGET;GIT_REPOSITORY;GIT_TAG;ROOT_DIR;SOURCE_DIR\" \"INCLUDE_DIRS\" ${ARGN})\n    if(NOT args_TARGET)\n        message(FATAL \"Error on sibr_gitlibrary : please define your target name.\")\n        return()\n    endif()\n\n    if(NOT args_ROOT_DIR)\n        set(args_ROOT_DIR ${args_TARGET})\n    endif()\n\n    if(NOT args_SOURCE_DIR)\n        set(args_SOURCE_DIR ${args_TARGET})\n    endif()\n\n    if(args_GIT_REPOSITORY AND args_GIT_TAG)\n        if(EXISTS ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}/.git)\n            git_describe(\n                PATH ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}\n                GIT_URL SIBR_GITLIBRARY_URL\n                GIT_BRANCH SIBR_GITLIBRARY_BRANCH\n                GIT_COMMIT_HASH SIBR_GITLIBRARY_COMMIT_HASH\n                GIT_TAG SIBR_GITLIBRARY_TAG\n            )\n\n            if((SIBR_GITLIBRARY_URL STREQUAL args_GIT_REPOSITORY) AND\n                ((SIBR_GITLIBRARY_BRANCH STREQUAL args_GIT_TAG) OR\n                 (SIBR_GITLIBRARY_TAG STREQUAL args_GIT_TAG) OR\n                 (SIBR_GITLIBRARY_COMMIT_HASH STREQUAL args_GIT_TAG)))\n                message(STATUS \"Library ${args_TARGET} already available, skipping.\")\n                set(SIBR_GITLIBRARY_DECLARED ON)\n            else()\n                message(STATUS \"Adding library ${args_TARGET} from git...\")\n            endif()\n        endif()\n\n        FetchContent_Declare(${args_TARGET}\n            GIT_REPOSITORY \t${args_GIT_REPOSITORY}\n            GIT_TAG\t\t\t${args_GIT_TAG}\n            GIT_SHALLOW\t\tON\n            SOURCE_DIR \t\t${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}\n            SUBBUILD_DIR    ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/subbuild\n            BINARY_DIR      ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/build\n        )\n        FetchContent_GetProperties(${args_TARGET})\n        string(TOLOWER \"<name>\" lcTargetName)\n\n        if((NOT SIBR_GITLIBRARY_DECLARED) AND (NOT ${lcTargetName}_POPULATED))\n            message(STATUS \"Populating library ${args_TARGET}...\")\n            FetchContent_Populate(${args_TARGET} QUIET\n                GIT_REPOSITORY \t${args_GIT_REPOSITORY}\n                GIT_TAG\t\t\t${args_GIT_TAG}\n                SOURCE_DIR \t\t${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR}\n                SUBBUILD_DIR    ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/subbuild\n                BINARY_DIR      ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/build\n            )\n        endif()\n\n        add_subdirectory(${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/${args_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/extlibs/${args_ROOT_DIR}/build)\n\n        get_target_property(type ${args_TARGET} TYPE)\n        if(NOT (type STREQUAL \"INTERFACE_LIBRARY\"))\n            set_target_properties(${args_TARGET} PROPERTIES FOLDER \"extlibs\")\n        endif()\n\n        list(APPEND ${args_TARGET}_INCLUDE_DIRS ${EXTLIBS_PACKAGE_FOLDER}/${args_ROOT_DIR})\n        list(APPEND ${args_TARGET}_INCLUDE_DIRS ${EXTLIBS_PACKAGE_FOLDER}/${args_ROOT_DIR}/${args_SOURCE_DIR})\n\n        foreach(args_INCLUDE_DIR ${args_INCLUDE_DIRS})\n            list(APPEND ${args_TARGET}_INCLUDE_DIRS ${EXTLIBS_PACKAGE_FOLDER}/${args_ROOT_DIR}/${args_SOURCE_DIR}/${args_INCLUDE_DIR})\n        endforeach()\n\n        include_directories(${${args_TARGET}_INCLUDE_DIRS})\n    else()\n        message(FATAL \"Error on sibr_gitlibrary for target ${args_TARGET}: missing git tag or git url.\")\n    endif()\nendfunction()"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#########################################################\n# Include doxygen documentation target\n#########################################################\noption(BUILD_DOCUMENTATION \"build doxygen documentation ('Build' DOCUMENTATION target, and find the compiled docs in install/docs/index.html)\" OFF)\nif(BUILD_DOCUMENTATION)\n\tset(DOXYGEN_REQUIRED_VERSION \"1.8.17\")\n\tfind_package(Doxygen)\n\tif(NOT DOXYGEN_FOUND)\n\t\tmessage(FATAL_ERROR \"Doxygen not found, unable to generate documentation.\")\n\telseif(DOXYGEN_VERSION VERSION_LESS DOXYGEN_REQUIRED_VERSION)\n\t\tmessage(FATAL_ERROR \"Doxygen version is less than ${DOXYGEN_REQUIRED_VERSION} (Current version is ${DOXYGEN_VERSION}).\")\n\telse()\n\t\tset(DOXY_DOC_DEST_DIR\t${CMAKE_INSTALL_ROOT}/docs)\t\t\t\t\t\t## used in the doxyfile.in\n\t\t\n\t\tset(DOXY_DOC_INPUT_ROOT_DIRS        \"${CMAKE_HOME_DIRECTORY}/src ${CMAKE_HOME_DIRECTORY}/docs ${CMAKE_CURRENT_BINARY_DIR}/generated\")    \t\t\t\t## used in the doxyfile.in\n\t\tset(DOXY_DOC_EXCLUDE_PATTERNS_DIRS  \"${DOXY_DOC_EXCLUDE_PATTERNS_DIRS}\") ## used in the doxyfile.in\n\t\tset(DOXY_DOC_COMMON_IMG_PATH        \"${CMAKE_CURRENT_SOURCE_DIR}/img ${CMAKE_HOME_DIRECTORY}/src/projects\")\n\t\tset(DOXY_DOC_PAGES_DIR\t\t\t\t\"${CMAKE_CURRENT_SOURCE_DIR}/pages\")\n\t\tset(DOXY_DOC_GENERATED_DOC_DIR\t\t\"${CMAKE_CURRENT_BINARY_DIR}/generated\")\n\t\t\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_SAMPLES_SUBPAGE_REF\t\"${SIBR_PROJECTS_SAMPLES_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_OURS_SUBPAGE_REF\t\t\"${SIBR_PROJECTS_OURS_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF\t\"${SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_OTHERS_SUBPAGE_REF\t\t\"${SIBR_PROJECTS_OTHERS_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_SAMPLES_REF_REF\t\t\"${SIBR_PROJECTS_SAMPLES_REF_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_OURS_REF_REF\t\t\t\"${SIBR_PROJECTS_OURS_REF_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_TOOLBOX_REF_REF\t\t\"${SIBR_PROJECTS_TOOLBOX_REF_REF}\")\n\t\tstring(REPLACE \"\\\\\" \"\\\\\\\\\" SIBR_PROJECTS_OTHERS_REF_REF\t\t\t\"${SIBR_PROJECTS_OTHERS_REF_REF}\")\n\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_SAMPLES_SUBPAGE_REF\t\t\"${SIBR_PROJECTS_SAMPLES_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_OURS_SUBPAGE_REF\t\t\"${SIBR_PROJECTS_OURS_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF\t\t\"${SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_OTHERS_SUBPAGE_REF\t\t\"${SIBR_PROJECTS_OTHERS_SUBPAGE_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_SAMPLES_REF_REF\t\t\t\"${SIBR_PROJECTS_SAMPLES_REF_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_OURS_REF_REF\t\t\t\"${SIBR_PROJECTS_OURS_REF_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_TOOLBOX_REF_REF\t\t\t\"${SIBR_PROJECTS_TOOLBOX_REF_REF}\")\n\t\tstring(REPLACE \"\\n\" \"\\\\n\" SIBR_PROJECTS_OTHERS_REF_REF\t\t\t\"${SIBR_PROJECTS_OTHERS_REF_REF}\")\n\n\t\tfile(GLOB doxygen_config_files \"*.in\")\n\t\tforeach(filename ${doxygen_config_files})\n\t\t\tmessage(STATUS \"Generating ${filename}...\")\n\t\t\tget_filename_component(output_filename ${filename} NAME_WLE)\n\t\t\tmessage(STATUS \"Output in ${CMAKE_CURRENT_BINARY_DIR}/${output_filename}...\")\n\t\t\tconfigure_file(${filename} ${CMAKE_CURRENT_BINARY_DIR}/${output_filename} @ONLY)\n\t\tendforeach()\n\n\t\tadd_custom_target(DOCUMENTATION ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/doxygen_prebuild.cmake\n\t\t\tCOMMAND ${DOXYGEN_EXECUTABLE} \"${CMAKE_CURRENT_BINARY_DIR}/doxyfile\"\n\t\t\tWORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}\n\t\t\tCOMMENT \"Building user's documentation into ${DOXY_DOC_DEST_DIR} dir...\"\n\t\t)\n\tendif()\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/doxyfile.in",
    "content": "# Doxyfile 1.8.20\r\n\r\n# This file describes the settings to be used by the documentation system\r\n# doxygen (www.doxygen.org) for a project.\r\n#\r\n# All text after a double hash (##) is considered a comment and is placed in\r\n# front of the TAG it is preceding.\r\n#\r\n# All text after a single hash (#) is considered a comment and will be ignored.\r\n# The format is:\r\n# TAG = value [value, ...]\r\n# For lists, items can also be appended using:\r\n# TAG += value [value, ...]\r\n# Values that contain spaces should be placed between quotes (\\\" \\\").\r\n\r\n#---------------------------------------------------------------------------\r\n# Project related configuration options\r\n#---------------------------------------------------------------------------\r\n\r\n# This tag specifies the encoding used for all characters in the configuration\r\n# file that follow. The default is UTF-8 which is also the encoding used for all\r\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\r\n# iconv built into libc) for the transcoding. See\r\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\r\n# The default value is: UTF-8.\r\n\r\nDOXYFILE_ENCODING      = UTF-8\r\n\r\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\r\n# double-quotes, unless you are using Doxywizard) that should identify the\r\n# project for which the documentation is generated. This name is used in the\r\n# title of most generated pages and in a few other places.\r\n# The default value is: My Project.\r\n\r\nPROJECT_NAME           = SIBR\r\n\r\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\r\n# could be handy for archiving the generated documentation or if some version\r\n# control system is used.\r\n\r\nPROJECT_NUMBER         = @SIBR_CORE_VERSION@\r\n\r\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\r\n# for a project that appears at the top of each page and should give viewer a\r\n# quick idea about the purpose of the project. Keep the description short.\r\n\r\nPROJECT_BRIEF          =\r\n\r\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\r\n# in the documentation. The maximum height of the logo should not exceed 55\r\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\r\n# the logo to the output directory.\r\n\r\nPROJECT_LOGO           =\r\n\r\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\r\n# into which the generated documentation will be written. If a relative path is\r\n# entered, it will be relative to the location where doxygen was started. If\r\n# left blank the current directory will be used.\r\n\r\nOUTPUT_DIRECTORY       = @DOXY_DOC_DEST_DIR@\r\n\r\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\r\n# directories (in 2 levels) under the output directory of each output format and\r\n# will distribute the generated files over these directories. Enabling this\r\n# option can be useful when feeding doxygen a huge amount of source files, where\r\n# putting all generated files in the same directory would otherwise causes\r\n# performance problems for the file system.\r\n# The default value is: NO.\r\n\r\nCREATE_SUBDIRS         = NO\r\n\r\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\r\n# characters to appear in the names of generated files. If set to NO, non-ASCII\r\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\r\n# U+3044.\r\n# The default value is: NO.\r\n\r\nALLOW_UNICODE_NAMES    = NO\r\n\r\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\r\n# documentation generated by doxygen is written. Doxygen will use this\r\n# information to generate all constant output in the proper language.\r\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\r\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\r\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\r\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\r\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\r\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\r\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\r\n# Ukrainian and Vietnamese.\r\n# The default value is: English.\r\n\r\nOUTPUT_LANGUAGE        = English\r\n\r\n# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all\r\n# documentation generated by doxygen is written. Doxygen will use this\r\n# information to generate all generated output in the proper direction.\r\n# Possible values are: None, LTR, RTL and Context.\r\n# The default value is: None.\r\n\r\nOUTPUT_TEXT_DIRECTION  = None\r\n\r\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\r\n# descriptions after the members that are listed in the file and class\r\n# documentation (similar to Javadoc). Set to NO to disable this.\r\n# The default value is: YES.\r\n\r\nBRIEF_MEMBER_DESC      = YES\r\n\r\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\r\n# description of a member or function before the detailed description\r\n#\r\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\r\n# brief descriptions will be completely suppressed.\r\n# The default value is: YES.\r\n\r\nREPEAT_BRIEF           = YES\r\n\r\n# This tag implements a quasi-intelligent brief description abbreviator that is\r\n# used to form the text in various listings. Each string in this list, if found\r\n# as the leading text of the brief description, will be stripped from the text\r\n# and the result, after processing the whole list, is used as the annotated\r\n# text. Otherwise, the brief description is used as-is. If left blank, the\r\n# following values are used ($name is automatically replaced with the name of\r\n# the entity):The $name class, The $name widget, The $name file, is, provides,\r\n# specifies, contains, represents, a, an and the.\r\n\r\nABBREVIATE_BRIEF       =\r\n\r\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\r\n# doxygen will generate a detailed section even if there is only a brief\r\n# description.\r\n# The default value is: NO.\r\n\r\nALWAYS_DETAILED_SEC    = NO\r\n\r\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\r\n# inherited members of a class in the documentation of that class as if those\r\n# members were ordinary class members. Constructors, destructors and assignment\r\n# operators of the base classes will not be shown.\r\n# The default value is: NO.\r\n\r\nINLINE_INHERITED_MEMB  = NO\r\n\r\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\r\n# before files name in the file list and in the header files. If set to NO the\r\n# shortest path that makes the file name unique will be used\r\n# The default value is: YES.\r\n\r\nFULL_PATH_NAMES        = YES\r\n\r\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\r\n# Stripping is only done if one of the specified strings matches the left-hand\r\n# part of the path. The tag can be used to show relative paths in the file list.\r\n# If left blank the directory from which doxygen is run is used as the path to\r\n# strip.\r\n#\r\n# Note that you can specify absolute paths here, but also relative paths, which\r\n# will be relative from the directory where doxygen is started.\r\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\r\n\r\nSTRIP_FROM_PATH        =\r\n\r\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\r\n# path mentioned in the documentation of a class, which tells the reader which\r\n# header file to include in order to use a class. If left blank only the name of\r\n# the header file containing the class definition is used. Otherwise one should\r\n# specify the list of include paths that are normally passed to the compiler\r\n# using the -I flag.\r\n\r\nSTRIP_FROM_INC_PATH    =\r\n\r\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\r\n# less readable) file names. This can be useful is your file systems doesn't\r\n# support long names like on DOS, Mac, or CD-ROM.\r\n# The default value is: NO.\r\n\r\nSHORT_NAMES            = NO\r\n\r\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\r\n# first line (until the first dot) of a Javadoc-style comment as the brief\r\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\r\n# style comments (thus requiring an explicit @brief command for a brief\r\n# description.)\r\n# The default value is: NO.\r\n\r\nJAVADOC_AUTOBRIEF      = YES\r\n\r\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\r\n# such as\r\n# /***************\r\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\r\n# Javadoc-style will behave just like regular comments and it will not be\r\n# interpreted by doxygen.\r\n# The default value is: NO.\r\n\r\nJAVADOC_BANNER         = NO\r\n\r\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\r\n# line (until the first dot) of a Qt-style comment as the brief description. If\r\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\r\n# requiring an explicit \\brief command for a brief description.)\r\n# The default value is: NO.\r\n\r\nQT_AUTOBRIEF           = YES\r\n\r\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\r\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\r\n# a brief description. This used to be the default behavior. The new default is\r\n# to treat a multi-line C++ comment block as a detailed description. Set this\r\n# tag to YES if you prefer the old behavior instead.\r\n#\r\n# Note that setting this tag to YES also means that rational rose comments are\r\n# not recognized any more.\r\n# The default value is: NO.\r\n\r\nMULTILINE_CPP_IS_BRIEF = NO\r\n\r\n# By default Python docstrings are displayed as preformatted text and doxygen's\r\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\r\n# doxygen's special commands can be used and the contents of the docstring\r\n# documentation blocks is shown as doxygen documentation.\r\n# The default value is: YES.\r\n\r\nPYTHON_DOCSTRING       = YES\r\n\r\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\r\n# documentation from any documented member that it re-implements.\r\n# The default value is: YES.\r\n\r\nINHERIT_DOCS           = YES\r\n\r\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\r\n# page for each member. If set to NO, the documentation of a member will be part\r\n# of the file/class/namespace that contains it.\r\n# The default value is: NO.\r\n\r\nSEPARATE_MEMBER_PAGES  = NO\r\n\r\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\r\n# uses this value to replace tabs by spaces in code fragments.\r\n# Minimum value: 1, maximum value: 16, default value: 4.\r\n\r\nTAB_SIZE               = 4\r\n\r\n# This tag can be used to specify a number of aliases that act as commands in\r\n# the documentation. An alias has the form:\r\n# name=value\r\n# For example adding\r\n# \"sideeffect=@par Side Effects:\\n\"\r\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\r\n# documentation, which will result in a user-defined paragraph with heading\r\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\r\n# newlines (in the resulting output). You can put ^^ in the value part of an\r\n# alias to insert a newline as if a physical newline was in the original file.\r\n# When you need a literal { or } or , in the value part of an alias you have to\r\n# escape them by means of a backslash (\\), this can lead to conflicts with the\r\n# commands \\{ and \\} for these it is advised to use the version @{ and @} or use\r\n# a double escape (\\\\{ and \\\\})\r\n\r\nALIASES                =\r\n\r\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\r\n# only. Doxygen will then generate output that is more tailored for C. For\r\n# instance, some of the names that are used will be different. The list of all\r\n# members will be omitted, etc.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_FOR_C  = NO\r\n\r\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\r\n# Python sources only. Doxygen will then generate output that is more tailored\r\n# for that language. For instance, namespaces will be presented as packages,\r\n# qualified scopes will look different, etc.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_JAVA   = NO\r\n\r\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\r\n# sources. Doxygen will then generate output that is tailored for Fortran.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_FOR_FORTRAN   = NO\r\n\r\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\r\n# sources. Doxygen will then generate output that is tailored for VHDL.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_VHDL   = NO\r\n\r\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\r\n# sources only. Doxygen will then generate output that is more tailored for that\r\n# language. For instance, namespaces will be presented as modules, types will be\r\n# separated into more groups, etc.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_SLICE  = NO\r\n\r\n# Doxygen selects the parser to use depending on the extension of the files it\r\n# parses. With this tag you can assign which parser to use for a given\r\n# extension. Doxygen has a built-in mapping, but you can override or extend it\r\n# using this tag. The format is ext=language, where ext is a file extension, and\r\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\r\n# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,\r\n# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\r\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\r\n# tries to guess whether the code is fixed or free formatted code, this is the\r\n# default for Fortran type files). For instance to make doxygen treat .inc files\r\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\r\n# use: inc=Fortran f=C.\r\n#\r\n# Note: For files without extension you can use no_extension as a placeholder.\r\n#\r\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\r\n# the files are not read by doxygen.\r\n\r\nEXTENSION_MAPPING      =\r\n\r\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\r\n# according to the Markdown format, which allows for more readable\r\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\r\n# The output of markdown processing is further processed by doxygen, so you can\r\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\r\n# case of backward compatibilities issues.\r\n# The default value is: YES.\r\n\r\nMARKDOWN_SUPPORT       = YES\r\n\r\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\r\n# to that level are automatically included in the table of contents, even if\r\n# they do not have an id attribute.\r\n# Note: This feature currently applies only to Markdown headings.\r\n# Minimum value: 0, maximum value: 99, default value: 5.\r\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\r\n\r\nTOC_INCLUDE_HEADINGS   = 0\r\n\r\n# When enabled doxygen tries to link words that correspond to documented\r\n# classes, or namespaces to their corresponding documentation. Such a link can\r\n# be prevented in individual cases by putting a % sign in front of the word or\r\n# globally by setting AUTOLINK_SUPPORT to NO.\r\n# The default value is: YES.\r\n\r\nAUTOLINK_SUPPORT       = YES\r\n\r\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\r\n# to include (a tag file for) the STL sources as input, then you should set this\r\n# tag to YES in order to let doxygen match functions declarations and\r\n# definitions whose arguments contain STL classes (e.g. func(std::string);\r\n# versus func(std::string) {}). This also make the inheritance and collaboration\r\n# diagrams that involve STL classes more complete and accurate.\r\n# The default value is: NO.\r\n\r\nBUILTIN_STL_SUPPORT    = YES\r\n\r\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\r\n# enable parsing support.\r\n# The default value is: NO.\r\n\r\nCPP_CLI_SUPPORT        = NO\r\n\r\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\r\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\r\n# will parse them like normal C++ but will assume all classes use public instead\r\n# of private inheritance when no explicit protection keyword is present.\r\n# The default value is: NO.\r\n\r\nSIP_SUPPORT            = NO\r\n\r\n# For Microsoft's IDL there are propget and propput attributes to indicate\r\n# getter and setter methods for a property. Setting this option to YES will make\r\n# doxygen to replace the get and set methods by a property in the documentation.\r\n# This will only work if the methods are indeed getting or setting a simple\r\n# type. If this is not the case, or you want to show the methods anyway, you\r\n# should set this option to NO.\r\n# The default value is: YES.\r\n\r\nIDL_PROPERTY_SUPPORT   = YES\r\n\r\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\r\n# tag is set to YES then doxygen will reuse the documentation of the first\r\n# member in the group (if any) for the other members of the group. By default\r\n# all members of a group must be documented explicitly.\r\n# The default value is: NO.\r\n\r\nDISTRIBUTE_GROUP_DOC   = YES\r\n\r\n# If one adds a struct or class to a group and this option is enabled, then also\r\n# any nested class or struct is added to the same group. By default this option\r\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\r\n# The default value is: NO.\r\n\r\nGROUP_NESTED_COMPOUNDS = NO\r\n\r\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\r\n# (for instance a group of public functions) to be put as a subgroup of that\r\n# type (e.g. under the Public Functions section). Set it to NO to prevent\r\n# subgrouping. Alternatively, this can be done per class using the\r\n# \\nosubgrouping command.\r\n# The default value is: YES.\r\n\r\nSUBGROUPING            = YES\r\n\r\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\r\n# are shown inside the group in which they are included (e.g. using \\ingroup)\r\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\r\n# and RTF).\r\n#\r\n# Note that this feature does not work in combination with\r\n# SEPARATE_MEMBER_PAGES.\r\n# The default value is: NO.\r\n\r\nINLINE_GROUPED_CLASSES = NO\r\n\r\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\r\n# with only public data fields or simple typedef fields will be shown inline in\r\n# the documentation of the scope in which they are defined (i.e. file,\r\n# namespace, or group documentation), provided this scope is documented. If set\r\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\r\n# Man pages) or section (for LaTeX and RTF).\r\n# The default value is: NO.\r\n\r\nINLINE_SIMPLE_STRUCTS  = NO\r\n\r\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\r\n# enum is documented as struct, union, or enum with the name of the typedef. So\r\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\r\n# with name TypeT. When disabled the typedef will appear as a member of a file,\r\n# namespace, or class. And the struct will be named TypeS. This can typically be\r\n# useful for C code in case the coding convention dictates that all compound\r\n# types are typedef'ed and only the typedef is referenced, never the tag name.\r\n# The default value is: NO.\r\n\r\nTYPEDEF_HIDES_STRUCT   = NO\r\n\r\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\r\n# cache is used to resolve symbols given their name and scope. Since this can be\r\n# an expensive process and often the same symbol appears multiple times in the\r\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\r\n# doxygen will become slower. If the cache is too large, memory is wasted. The\r\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\r\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\r\n# symbols. At the end of a run doxygen will report the cache usage and suggest\r\n# the optimal cache size from a speed point of view.\r\n# Minimum value: 0, maximum value: 9, default value: 0.\r\n\r\nLOOKUP_CACHE_SIZE      = 0\r\n\r\n# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use\r\n# during processing. When set to 0 doxygen will based this on the number of\r\n# cores available in the system. You can set it explicitly to a value larger\r\n# than 0 to get more control over the balance between CPU load and processing\r\n# speed. At this moment only the input processing can be done using multiple\r\n# threads. Since this is still an experimental feature the default is set to 1,\r\n# which efficively disables parallel processing. Please report any issues you\r\n# encounter. Generating dot graphs in parallel is controlled by the\r\n# DOT_NUM_THREADS setting.\r\n# Minimum value: 0, maximum value: 32, default value: 1.\r\n\r\nNUM_PROC_THREADS       = 1\r\n\r\n#---------------------------------------------------------------------------\r\n# Build related configuration options\r\n#---------------------------------------------------------------------------\r\n\r\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\r\n# documentation are documented, even if no documentation was available. Private\r\n# class members and static file members will be hidden unless the\r\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\r\n# Note: This will also disable the warnings about undocumented members that are\r\n# normally produced when WARNINGS is set to YES.\r\n# The default value is: NO.\r\n\r\nEXTRACT_ALL            = YES\r\n\r\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\r\n# be included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_PRIVATE        = YES\r\n\r\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\r\n# methods of a class will be included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_PRIV_VIRTUAL   = NO\r\n\r\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\r\n# scope will be included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_PACKAGE        = YES\r\n\r\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\r\n# included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_STATIC         = YES\r\n\r\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\r\n# locally in source files will be included in the documentation. If set to NO,\r\n# only classes defined in header files are included. Does not have any effect\r\n# for Java sources.\r\n# The default value is: YES.\r\n\r\nEXTRACT_LOCAL_CLASSES  = YES\r\n\r\n# This flag is only useful for Objective-C code. If set to YES, local methods,\r\n# which are defined in the implementation section but not in the interface are\r\n# included in the documentation. If set to NO, only methods in the interface are\r\n# included.\r\n# The default value is: NO.\r\n\r\nEXTRACT_LOCAL_METHODS  = NO\r\n\r\n# If this flag is set to YES, the members of anonymous namespaces will be\r\n# extracted and appear in the documentation as a namespace called\r\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\r\n# the file that contains the anonymous namespace. By default anonymous namespace\r\n# are hidden.\r\n# The default value is: NO.\r\n\r\nEXTRACT_ANON_NSPACES   = NO\r\n\r\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\r\n# undocumented members inside documented classes or files. If set to NO these\r\n# members will be included in the various overviews, but no documentation\r\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\r\n# The default value is: NO.\r\n\r\nHIDE_UNDOC_MEMBERS     = NO\r\n\r\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\r\n# undocumented classes that are normally visible in the class hierarchy. If set\r\n# to NO, these classes will be included in the various overviews. This option\r\n# has no effect if EXTRACT_ALL is enabled.\r\n# The default value is: NO.\r\n\r\nHIDE_UNDOC_CLASSES     = NO\r\n\r\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\r\n# declarations. If set to NO, these declarations will be included in the\r\n# documentation.\r\n# The default value is: NO.\r\n\r\nHIDE_FRIEND_COMPOUNDS  = NO\r\n\r\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\r\n# documentation blocks found inside the body of a function. If set to NO, these\r\n# blocks will be appended to the function's detailed documentation block.\r\n# The default value is: NO.\r\n\r\nHIDE_IN_BODY_DOCS      = YES\r\n\r\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\r\n# \\internal command is included. If the tag is set to NO then the documentation\r\n# will be excluded. Set it to YES to include the internal documentation.\r\n# The default value is: NO.\r\n\r\nINTERNAL_DOCS          = NO\r\n\r\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\r\n# names in lower-case letters. If set to YES, upper-case letters are also\r\n# allowed. This is useful if you have classes or files whose names only differ\r\n# in case and if your file system supports case sensitive file names. Windows\r\n# (including Cygwin) and Mac users are advised to set this option to NO.\r\n# The default value is: system dependent.\r\n\r\nCASE_SENSE_NAMES       = YES\r\n\r\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\r\n# their full class and namespace scopes in the documentation. If set to YES, the\r\n# scope will be hidden.\r\n# The default value is: NO.\r\n\r\nHIDE_SCOPE_NAMES       = NO\r\n\r\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\r\n# append additional text to a page's title, such as Class Reference. If set to\r\n# YES the compound reference will be hidden.\r\n# The default value is: NO.\r\n\r\nHIDE_COMPOUND_REFERENCE= NO\r\n\r\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\r\n# the files that are included by a file in the documentation of that file.\r\n# The default value is: YES.\r\n\r\nSHOW_INCLUDE_FILES     = YES\r\n\r\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\r\n# grouped member an include statement to the documentation, telling the reader\r\n# which file to include in order to use the member.\r\n# The default value is: NO.\r\n\r\nSHOW_GROUPED_MEMB_INC  = NO\r\n\r\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\r\n# files with double quotes in the documentation rather than with sharp brackets.\r\n# The default value is: NO.\r\n\r\nFORCE_LOCAL_INCLUDES   = NO\r\n\r\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\r\n# documentation for inline members.\r\n# The default value is: YES.\r\n\r\nINLINE_INFO            = YES\r\n\r\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\r\n# (detailed) documentation of file and class members alphabetically by member\r\n# name. If set to NO, the members will appear in declaration order.\r\n# The default value is: YES.\r\n\r\nSORT_MEMBER_DOCS       = YES\r\n\r\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\r\n# descriptions of file, namespace and class members alphabetically by member\r\n# name. If set to NO, the members will appear in declaration order. Note that\r\n# this will also influence the order of the classes in the class list.\r\n# The default value is: NO.\r\n\r\nSORT_BRIEF_DOCS        = NO\r\n\r\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\r\n# (brief and detailed) documentation of class members so that constructors and\r\n# destructors are listed first. If set to NO the constructors will appear in the\r\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\r\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\r\n# member documentation.\r\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\r\n# detailed member documentation.\r\n# The default value is: NO.\r\n\r\nSORT_MEMBERS_CTORS_1ST = NO\r\n\r\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\r\n# of group names into alphabetical order. If set to NO the group names will\r\n# appear in their defined order.\r\n# The default value is: NO.\r\n\r\nSORT_GROUP_NAMES       = NO\r\n\r\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\r\n# fully-qualified names, including namespaces. If set to NO, the class list will\r\n# be sorted only by class name, not including the namespace part.\r\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\r\n# Note: This option applies only to the class list, not to the alphabetical\r\n# list.\r\n# The default value is: NO.\r\n\r\nSORT_BY_SCOPE_NAME     = YES\r\n\r\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\r\n# type resolution of all parameters of a function it will reject a match between\r\n# the prototype and the implementation of a member function even if there is\r\n# only one candidate or it is obvious which candidate to choose by doing a\r\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\r\n# accept a match between prototype and implementation in such cases.\r\n# The default value is: NO.\r\n\r\nSTRICT_PROTO_MATCHING  = NO\r\n\r\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\r\n# list. This list is created by putting \\todo commands in the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_TODOLIST      = YES\r\n\r\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\r\n# list. This list is created by putting \\test commands in the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_TESTLIST      = YES\r\n\r\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\r\n# list. This list is created by putting \\bug commands in the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_BUGLIST       = YES\r\n\r\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\r\n# the deprecated list. This list is created by putting \\deprecated commands in\r\n# the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_DEPRECATEDLIST= YES\r\n\r\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\r\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\r\n# ... \\endcond blocks.\r\n\r\nENABLED_SECTIONS       =\r\n\r\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\r\n# initial value of a variable or macro / define can have for it to appear in the\r\n# documentation. If the initializer consists of more lines than specified here\r\n# it will be hidden. Use a value of 0 to hide initializers completely. The\r\n# appearance of the value of individual variables and macros / defines can be\r\n# controlled using \\showinitializer or \\hideinitializer command in the\r\n# documentation regardless of this setting.\r\n# Minimum value: 0, maximum value: 10000, default value: 30.\r\n\r\nMAX_INITIALIZER_LINES  = 30\r\n\r\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\r\n# the bottom of the documentation of classes and structs. If set to YES, the\r\n# list will mention the files that were used to generate the documentation.\r\n# The default value is: YES.\r\n\r\nSHOW_USED_FILES        = YES\r\n\r\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\r\n# will remove the Files entry from the Quick Index and from the Folder Tree View\r\n# (if specified).\r\n# The default value is: YES.\r\n\r\nSHOW_FILES             = YES\r\n\r\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\r\n# page. This will remove the Namespaces entry from the Quick Index and from the\r\n# Folder Tree View (if specified).\r\n# The default value is: YES.\r\n\r\nSHOW_NAMESPACES        = YES\r\n\r\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\r\n# doxygen should invoke to get the current version for each file (typically from\r\n# the version control system). Doxygen will invoke the program by executing (via\r\n# popen()) the command command input-file, where command is the value of the\r\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\r\n# by doxygen. Whatever the program writes to standard output is used as the file\r\n# version. For an example see the documentation.\r\n\r\nFILE_VERSION_FILTER    =\r\n\r\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\r\n# by doxygen. The layout file controls the global structure of the generated\r\n# output files in an output format independent way. To create the layout file\r\n# that represents doxygen's defaults, run doxygen with the -l option. You can\r\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\r\n# will be used as the name of the layout file.\r\n#\r\n# Note that if you run doxygen from a directory containing a file called\r\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\r\n# tag is left empty.\r\n\r\nLAYOUT_FILE            = @CMAKE_CURRENT_BINARY_DIR@/layout.xml\r\n\r\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\r\n# the reference definitions. This must be a list of .bib files. The .bib\r\n# extension is automatically appended if omitted. This requires the bibtex tool\r\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\r\n# For LaTeX the style of the bibliography can be controlled using\r\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\r\n# search path. See also \\cite for info how to create references.\r\n\r\nCITE_BIB_FILES         =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to warning and progress messages\r\n#---------------------------------------------------------------------------\r\n\r\n# The QUIET tag can be used to turn on/off the messages that are generated to\r\n# standard output by doxygen. If QUIET is set to YES this implies that the\r\n# messages are off.\r\n# The default value is: NO.\r\n\r\nQUIET                  = NO\r\n\r\n# The WARNINGS tag can be used to turn on/off the warning messages that are\r\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\r\n# this implies that the warnings are on.\r\n#\r\n# Tip: Turn warnings on while writing the documentation.\r\n# The default value is: YES.\r\n\r\nWARNINGS               = YES\r\n\r\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\r\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\r\n# will automatically be disabled.\r\n# The default value is: YES.\r\n\r\nWARN_IF_UNDOCUMENTED   = NO\r\n\r\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\r\n# potential errors in the documentation, such as not documenting some parameters\r\n# in a documented function, or documenting parameters that don't exist or using\r\n# markup commands wrongly.\r\n# The default value is: YES.\r\n\r\nWARN_IF_DOC_ERROR      = YES\r\n\r\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\r\n# are documented, but have no documentation for their parameters or return\r\n# value. If set to NO, doxygen will only warn about wrong or incomplete\r\n# parameter documentation, but not about the absence of documentation. If\r\n# EXTRACT_ALL is set to YES then this flag will automatically be disabled.\r\n# The default value is: NO.\r\n\r\nWARN_NO_PARAMDOC       = NO\r\n\r\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\r\n# a warning is encountered.\r\n# The default value is: NO.\r\n\r\nWARN_AS_ERROR          = NO\r\n\r\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\r\n# can produce. The string should contain the $file, $line, and $text tags, which\r\n# will be replaced by the file and line number from which the warning originated\r\n# and the warning text. Optionally the format may contain $version, which will\r\n# be replaced by the version of the file (if it could be obtained via\r\n# FILE_VERSION_FILTER)\r\n# The default value is: $file:$line: $text.\r\n\r\nWARN_FORMAT            = \"$file:$line: $text\"\r\n\r\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\r\n# messages should be written. If left blank the output is written to standard\r\n# error (stderr).\r\n\r\nWARN_LOGFILE           =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the input files\r\n#---------------------------------------------------------------------------\r\n\r\n# The INPUT tag is used to specify the files and/or directories that contain\r\n# documented source files. You may enter file names like myfile.cpp or\r\n# directories like /usr/src/myproject. Separate the files or directories with\r\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\r\n# Note: If this tag is empty the current directory is searched.\r\n\r\nINPUT                  = @DOXY_DOC_INPUT_ROOT_DIRS@\r\n\r\n# This tag can be used to specify the character encoding of the source files\r\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\r\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\r\n# documentation (see: https://www.gnu.org/software/libiconv/) for the list of\r\n# possible encodings.\r\n# The default value is: UTF-8.\r\n\r\nINPUT_ENCODING         = UTF-8\r\n\r\n# If the value of the INPUT tag contains directories, you can use the\r\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\r\n# *.h) to filter out the source-files in the directories.\r\n#\r\n# Note that for custom extensions or not directly supported extensions you also\r\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\r\n# read by doxygen.\r\n#\r\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\r\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\r\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\r\n# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),\r\n# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen\r\n# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,\r\n# *.vhdl, *.ucf, *.qsf and *.ice.\r\n\r\nFILE_PATTERNS          = *.h \\\r\n                         *.hh \\\r\n                         *.hpp \\\r\n                         *.hxx \\\r\n                         *.cpp \\\r\n                         *.cxx \\\r\n                         *.cc \\\r\n                         *.fp \\\r\n                         *.vp \\\r\n                         *.gp \\\r\n                         *.vs \\\r\n                         *.fs \\\r\n                         *.gs \\\r\n                         *.vert \\\r\n                         *.frag \\\r\n                         *.geom \\\r\n                         *.md \\\r\n                         *.dox \\\r\n                         *.py\r\n\r\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\r\n# be searched for input files as well.\r\n# The default value is: NO.\r\n\r\nRECURSIVE              = YES\r\n\r\n# The EXCLUDE tag can be used to specify files and/or directories that should be\r\n# excluded from the INPUT source files. This way you can easily exclude a\r\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\r\n#\r\n# Note that relative paths are relative to the directory from which doxygen is\r\n# run.\r\n\r\nEXCLUDE                = @DOXY_DOC_EXCLUDE_PATTERNS_DIRS@\r\n\r\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\r\n# directories that are symbolic links (a Unix file system feature) are excluded\r\n# from the input.\r\n# The default value is: NO.\r\n\r\nEXCLUDE_SYMLINKS       = NO\r\n\r\n# If the value of the INPUT tag contains directories, you can use the\r\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\r\n# certain files from those directories.\r\n#\r\n# Note that the wildcards are matched against the file with absolute path, so to\r\n# exclude all test directories for example use the pattern */test/*\r\n\r\nEXCLUDE_PATTERNS       =\r\n\r\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\r\n# (namespaces, classes, functions, etc.) that should be excluded from the\r\n# output. The symbol name can be a fully qualified name, a word, or if the\r\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\r\n# AClass::ANamespace, ANamespace::*Test\r\n#\r\n# Note that the wildcards are matched against the file with absolute path, so to\r\n# exclude all test directories use the pattern */test/*\r\n\r\nEXCLUDE_SYMBOLS        =\r\n\r\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\r\n# that contain example code fragments that are included (see the \\include\r\n# command).\r\n\r\nEXAMPLE_PATH           =\r\n\r\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\r\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\r\n# *.h) to filter out the source-files in the directories. If left blank all\r\n# files are included.\r\n\r\nEXAMPLE_PATTERNS       =\r\n\r\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\r\n# searched for input files to be used with the \\include or \\dontinclude commands\r\n# irrespective of the value of the RECURSIVE tag.\r\n# The default value is: NO.\r\n\r\nEXAMPLE_RECURSIVE      = NO\r\n\r\n# The IMAGE_PATH tag can be used to specify one or more files or directories\r\n# that contain images that are to be included in the documentation (see the\r\n# \\image command).\r\n\r\nIMAGE_PATH             = @DOXY_DOC_COMMON_IMG_PATH@ \\\r\n                         @DOXY_APP_SPECIFIC_IMG_PATH@\r\n\r\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\r\n# invoke to filter for each input file. Doxygen will invoke the filter program\r\n# by executing (via popen()) the command:\r\n#\r\n# <filter> <input-file>\r\n#\r\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\r\n# name of an input file. Doxygen will then use the output that the filter\r\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\r\n# will be ignored.\r\n#\r\n# Note that the filter must not add or remove lines; it is applied before the\r\n# code is scanned, but not when the output code is generated. If lines are added\r\n# or removed, the anchors will not be placed correctly.\r\n#\r\n# Note that for custom extensions or not directly supported extensions you also\r\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\r\n# properly processed by doxygen.\r\n\r\nINPUT_FILTER           =\r\n\r\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\r\n# basis. Doxygen will compare the file name with each pattern and apply the\r\n# filter if there is a match. The filters are a list of the form: pattern=filter\r\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\r\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\r\n# patterns match the file name, INPUT_FILTER is applied.\r\n#\r\n# Note that for custom extensions or not directly supported extensions you also\r\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\r\n# properly processed by doxygen.\r\n\r\nFILTER_PATTERNS        =\r\n\r\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\r\n# INPUT_FILTER) will also be used to filter the input files that are used for\r\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\r\n# The default value is: NO.\r\n\r\nFILTER_SOURCE_FILES    = NO\r\n\r\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\r\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\r\n# it is also possible to disable source filtering for a specific pattern using\r\n# *.ext= (so without naming a filter).\r\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\r\n\r\nFILTER_SOURCE_PATTERNS =\r\n\r\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\r\n# is part of the input, its contents will be placed on the main page\r\n# (index.html). This can be useful if you have a project on for instance GitHub\r\n# and want to reuse the introduction page also for the doxygen output.\r\n\r\nUSE_MDFILE_AS_MAINPAGE =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to source browsing\r\n#---------------------------------------------------------------------------\r\n\r\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\r\n# generated. Documented entities will be cross-referenced with these sources.\r\n#\r\n# Note: To get rid of all source code in the generated output, make sure that\r\n# also VERBATIM_HEADERS is set to NO.\r\n# The default value is: NO.\r\n\r\nSOURCE_BROWSER         = YES\r\n\r\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\r\n# classes and enums directly into the documentation.\r\n# The default value is: NO.\r\n\r\nINLINE_SOURCES         = NO\r\n\r\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\r\n# special comment blocks from generated source code fragments. Normal C, C++ and\r\n# Fortran comments will always remain visible.\r\n# The default value is: YES.\r\n\r\nSTRIP_CODE_COMMENTS    = YES\r\n\r\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\r\n# entity all documented functions referencing it will be listed.\r\n# The default value is: NO.\r\n\r\nREFERENCED_BY_RELATION = YES\r\n\r\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\r\n# all documented entities called/used by that function will be listed.\r\n# The default value is: NO.\r\n\r\nREFERENCES_RELATION    = YES\r\n\r\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\r\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\r\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\r\n# link to the documentation.\r\n# The default value is: YES.\r\n\r\nREFERENCES_LINK_SOURCE = YES\r\n\r\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\r\n# source code will show a tooltip with additional information such as prototype,\r\n# brief description and links to the definition and documentation. Since this\r\n# will make the HTML file larger and loading of large files a bit slower, you\r\n# can opt to disable this feature.\r\n# The default value is: YES.\r\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\r\n\r\nSOURCE_TOOLTIPS        = YES\r\n\r\n# If the USE_HTAGS tag is set to YES then the references to source code will\r\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\r\n# source browser. The htags tool is part of GNU's global source tagging system\r\n# (see https://www.gnu.org/software/global/global.html). You will need version\r\n# 4.8.6 or higher.\r\n#\r\n# To use it do the following:\r\n# - Install the latest version of global\r\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\r\n# - Make sure the INPUT points to the root of the source tree\r\n# - Run doxygen as normal\r\n#\r\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\r\n# tools must be available from the command line (i.e. in the search path).\r\n#\r\n# The result: instead of the source browser generated by doxygen, the links to\r\n# source code will now point to the output of htags.\r\n# The default value is: NO.\r\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\r\n\r\nUSE_HTAGS              = NO\r\n\r\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\r\n# verbatim copy of the header file for each class for which an include is\r\n# specified. Set to NO to disable this.\r\n# See also: Section \\class.\r\n# The default value is: YES.\r\n\r\nVERBATIM_HEADERS       = YES\r\n\r\n# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the\r\n# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the\r\n# cost of reduced performance. This can be particularly helpful with template\r\n# rich C++ code for which doxygen's built-in parser lacks the necessary type\r\n# information.\r\n# Note: The availability of this option depends on whether or not doxygen was\r\n# generated with the -Duse_libclang=ON option for CMake.\r\n# The default value is: NO.\r\n\r\nCLANG_ASSISTED_PARSING = NO\r\n\r\n# If clang assisted parsing is enabled you can provide the compiler with command\r\n# line options that you would normally use when invoking the compiler. Note that\r\n# the include paths will already be set by doxygen for the files and directories\r\n# specified with INPUT and INCLUDE_PATH.\r\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\r\n\r\nCLANG_OPTIONS          =\r\n\r\n# If clang assisted parsing is enabled you can provide the clang parser with the\r\n# path to the directory containing a file called compile_commands.json. This\r\n# file is the compilation database (see:\r\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the\r\n# options used when the source files were built. This is equivalent to\r\n# specifying the \"-p\" option to a clang tool, such as clang-check. These options\r\n# will then be passed to the parser. Any options specified with CLANG_OPTIONS\r\n# will be added as well.\r\n# Note: The availability of this option depends on whether or not doxygen was\r\n# generated with the -Duse_libclang=ON option for CMake.\r\n\r\nCLANG_DATABASE_PATH    =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the alphabetical class index\r\n#---------------------------------------------------------------------------\r\n\r\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\r\n# compounds will be generated. Enable this if the project contains a lot of\r\n# classes, structs, unions or interfaces.\r\n# The default value is: YES.\r\n\r\nALPHABETICAL_INDEX     = NO\r\n\r\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\r\n# which the alphabetical index list will be split.\r\n# Minimum value: 1, maximum value: 20, default value: 5.\r\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\r\n\r\nCOLS_IN_ALPHA_INDEX    = 5\r\n\r\n# In case all classes in a project start with a common prefix, all classes will\r\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\r\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\r\n# while generating the index headers.\r\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\r\n\r\nIGNORE_PREFIX          =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the HTML output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\r\n# The default value is: YES.\r\n\r\nGENERATE_HTML          = YES\r\n\r\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: html.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_OUTPUT            = .\r\n\r\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\r\n# generated HTML page (for example: .htm, .php, .asp).\r\n# The default value is: .html.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_FILE_EXTENSION    = .html\r\n\r\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\r\n# each generated HTML page. If the tag is left blank doxygen will generate a\r\n# standard header.\r\n#\r\n# To get valid HTML the header file that includes any scripts and style sheets\r\n# that doxygen needs, which is dependent on the configuration options used (e.g.\r\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\r\n# default header using\r\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\r\n# YourConfigFile\r\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\r\n# for information on how to generate the default header that doxygen normally\r\n# uses.\r\n# Note: The header is subject to change so you typically have to regenerate the\r\n# default header when upgrading to a newer version of doxygen. For a description\r\n# of the possible markers and block names see the documentation.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_HEADER            =\r\n\r\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\r\n# generated HTML page. If the tag is left blank doxygen will generate a standard\r\n# footer. See HTML_HEADER for more information on how to generate a default\r\n# footer and what special commands can be used inside the footer. See also\r\n# section \"Doxygen usage\" for information on how to generate the default footer\r\n# that doxygen normally uses.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_FOOTER            =\r\n\r\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\r\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\r\n# the HTML output. If left blank doxygen will generate a default style sheet.\r\n# See also section \"Doxygen usage\" for information on how to generate the style\r\n# sheet that doxygen normally uses.\r\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\r\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\r\n# obsolete.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_STYLESHEET        =\r\n\r\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\r\n# cascading style sheets that are included after the standard style sheets\r\n# created by doxygen. Using this option one can overrule certain style aspects.\r\n# This is preferred over using HTML_STYLESHEET since it does not replace the\r\n# standard style sheet and is therefore more robust against future updates.\r\n# Doxygen will copy the style sheet files to the output directory.\r\n# Note: The order of the extra style sheet files is of importance (e.g. the last\r\n# style sheet in the list overrules the setting of the previous ones in the\r\n# list). For an example see the documentation.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_EXTRA_STYLESHEET  =\r\n\r\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\r\n# other source files which should be copied to the HTML output directory. Note\r\n# that these files will be copied to the base HTML output directory. Use the\r\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\r\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\r\n# files will be copied as-is; there are no commands or markers available.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_EXTRA_FILES       =\r\n\r\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\r\n# will adjust the colors in the style sheet and background images according to\r\n# this color. Hue is specified as an angle on a colorwheel, see\r\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\r\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\r\n# purple, and 360 is red again.\r\n# Minimum value: 0, maximum value: 359, default value: 220.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_COLORSTYLE_HUE    = 220\r\n\r\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\r\n# in the HTML output. For a value of 0 the output will use grayscales only. A\r\n# value of 255 will produce the most vivid colors.\r\n# Minimum value: 0, maximum value: 255, default value: 100.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_COLORSTYLE_SAT    = 100\r\n\r\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\r\n# luminance component of the colors in the HTML output. Values below 100\r\n# gradually make the output lighter, whereas values above 100 make the output\r\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\r\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\r\n# change the gamma.\r\n# Minimum value: 40, maximum value: 240, default value: 80.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_COLORSTYLE_GAMMA  = 80\r\n\r\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\r\n# page will contain the date and time when the page was generated. Setting this\r\n# to YES can help to show when doxygen was last run and thus if the\r\n# documentation is up to date.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_TIMESTAMP         = NO\r\n\r\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\r\n# documentation will contain a main index with vertical navigation menus that\r\n# are dynamically created via JavaScript. If disabled, the navigation index will\r\n# consists of multiple levels of tabs that are statically embedded in every HTML\r\n# page. Disable this option to support browsers that do not have JavaScript,\r\n# like the Qt help browser.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_DYNAMIC_MENUS     = YES\r\n\r\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\r\n# documentation will contain sections that can be hidden and shown after the\r\n# page has loaded.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_DYNAMIC_SECTIONS  = NO\r\n\r\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\r\n# shown in the various tree structured indices initially; the user can expand\r\n# and collapse entries dynamically later on. Doxygen will expand the tree to\r\n# such a level that at most the specified number of entries are visible (unless\r\n# a fully collapsed tree already exceeds this amount). So setting the number of\r\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\r\n# representing an infinite number of entries and will result in a full expanded\r\n# tree by default.\r\n# Minimum value: 0, maximum value: 9999, default value: 100.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_INDEX_NUM_ENTRIES = 100\r\n\r\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\r\n# generated that can be used as input for Apple's Xcode 3 integrated development\r\n# environment (see: https://developer.apple.com/xcode/), introduced with OSX\r\n# 10.5 (Leopard). To create a documentation set, doxygen will generate a\r\n# Makefile in the HTML output directory. Running make will produce the docset in\r\n# that directory and running make install will install the docset in\r\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\r\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\r\n# genXcode/_index.html for more information.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_DOCSET        = NO\r\n\r\n# This tag determines the name of the docset feed. A documentation feed provides\r\n# an umbrella under which multiple documentation sets from a single provider\r\n# (such as a company or product suite) can be grouped.\r\n# The default value is: Doxygen generated docs.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\r\n\r\n# This tag specifies a string that should uniquely identify the documentation\r\n# set bundle. This should be a reverse domain-name style string, e.g.\r\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\r\n# The default value is: org.doxygen.Project.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_BUNDLE_ID       = org.doxygen.Project\r\n\r\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\r\n# the documentation publisher. This should be a reverse domain-name style\r\n# string, e.g. com.mycompany.MyDocSet.documentation.\r\n# The default value is: org.doxygen.Publisher.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\r\n\r\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\r\n# The default value is: Publisher.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_PUBLISHER_NAME  = Publisher\r\n\r\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\r\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\r\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\r\n# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on\r\n# Windows.\r\n#\r\n# The HTML Help Workshop contains a compiler that can convert all HTML output\r\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\r\n# files are now used as the Windows 98 help format, and will replace the old\r\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\r\n# HTML files also contain an index, a table of contents, and you can search for\r\n# words in the documentation. The HTML workshop also contains a viewer for\r\n# compressed HTML files.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_HTMLHELP      = NO\r\n\r\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\r\n# file. You can add a path in front of the file if the result should not be\r\n# written to the html output directory.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nCHM_FILE               =\r\n\r\n# The HHC_LOCATION tag can be used to specify the location (absolute path\r\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\r\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\r\n# The file has to be specified with full path.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nHHC_LOCATION           =\r\n\r\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\r\n# (YES) or that it should be included in the main .chm file (NO).\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nGENERATE_CHI           = NO\r\n\r\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\r\n# and project file content.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nCHM_INDEX_ENCODING     =\r\n\r\n# The BINARY_TOC flag controls whether a binary table of contents is generated\r\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\r\n# enables the Previous and Next buttons.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nBINARY_TOC             = NO\r\n\r\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\r\n# the table of contents of the HTML help documentation and to the tree view.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nTOC_EXPAND             = NO\r\n\r\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\r\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\r\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\r\n# (.qch) of the generated HTML documentation.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_QHP           = NO\r\n\r\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\r\n# the file name of the resulting .qch file. The path specified is relative to\r\n# the HTML output folder.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQCH_FILE               =\r\n\r\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\r\n# Project output. For more information please see Qt Help Project / Namespace\r\n# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\r\n# The default value is: org.doxygen.Project.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_NAMESPACE          = org.doxygen.Project\r\n\r\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\r\n# Help Project output. For more information please see Qt Help Project / Virtual\r\n# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-\r\n# folders).\r\n# The default value is: doc.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_VIRTUAL_FOLDER     = doc\r\n\r\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\r\n# filter to add. For more information please see Qt Help Project / Custom\r\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\r\n# filters).\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_CUST_FILTER_NAME   =\r\n\r\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\r\n# custom filter to add. For more information please see Qt Help Project / Custom\r\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\r\n# filters).\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_CUST_FILTER_ATTRS  =\r\n\r\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\r\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\r\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_SECT_FILTER_ATTRS  =\r\n\r\n# The QHG_LOCATION tag can be used to specify the location of Qt's\r\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\r\n# generated .qhp file.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHG_LOCATION           =\r\n\r\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\r\n# generated, together with the HTML files, they form an Eclipse help plugin. To\r\n# install this plugin and make it available under the help contents menu in\r\n# Eclipse, the contents of the directory containing the HTML and XML files needs\r\n# to be copied into the plugins directory of eclipse. The name of the directory\r\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\r\n# After copying Eclipse needs to be restarted before the help appears.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_ECLIPSEHELP   = NO\r\n\r\n# A unique identifier for the Eclipse help plugin. When installing the plugin\r\n# the directory name containing the HTML and XML files should also have this\r\n# name. Each documentation set should have its own identifier.\r\n# The default value is: org.doxygen.Project.\r\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\r\n\r\nECLIPSE_DOC_ID         = org.doxygen.Project\r\n\r\n# If you want full control over the layout of the generated HTML pages it might\r\n# be necessary to disable the index and replace it with your own. The\r\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\r\n# of each HTML page. A value of NO enables the index and the value YES disables\r\n# it. Since the tabs in the index contain the same information as the navigation\r\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nDISABLE_INDEX          = YES\r\n\r\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\r\n# structure should be generated to display hierarchical information. If the tag\r\n# value is set to YES, a side panel will be generated containing a tree-like\r\n# index structure (just like the one that is generated for HTML Help). For this\r\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\r\n# (i.e. any modern browser). Windows users are probably better off using the\r\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\r\n# further fine-tune the look of the index. As an example, the default style\r\n# sheet generated by doxygen has an example that shows how to put an image at\r\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\r\n# the same information as the tab index, you could consider setting\r\n# DISABLE_INDEX to YES when enabling this option.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_TREEVIEW      = YES\r\n\r\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\r\n# doxygen will group on one line in the generated HTML documentation.\r\n#\r\n# Note that a value of 0 will completely suppress the enum values from appearing\r\n# in the overview section.\r\n# Minimum value: 0, maximum value: 20, default value: 4.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nENUM_VALUES_PER_LINE   = 4\r\n\r\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\r\n# to set the initial width (in pixels) of the frame in which the tree is shown.\r\n# Minimum value: 0, maximum value: 1500, default value: 250.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nTREEVIEW_WIDTH         = 250\r\n\r\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\r\n# external symbols imported via tag files in a separate window.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nEXT_LINKS_IN_WINDOW    = NO\r\n\r\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\r\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\r\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\r\n# the HTML output. These images will generally look nicer at scaled resolutions.\r\n# Possible values are: png (the default) and svg (looks nicer but requires the\r\n# pdf2svg or inkscape tool).\r\n# The default value is: png.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_FORMULA_FORMAT    = png\r\n\r\n# Use this tag to change the font size of LaTeX formulas included as images in\r\n# the HTML documentation. When you change the font size after a successful\r\n# doxygen run you need to manually remove any form_*.png images from the HTML\r\n# output directory to force them to be regenerated.\r\n# Minimum value: 8, maximum value: 50, default value: 10.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nFORMULA_FONTSIZE       = 10\r\n\r\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\r\n# generated for formulas are transparent PNGs. Transparent PNGs are not\r\n# supported properly for IE 6.0, but are supported on all modern browsers.\r\n#\r\n# Note that when changing this option you need to delete any form_*.png files in\r\n# the HTML output directory before the changes have effect.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nFORMULA_TRANSPARENT    = YES\r\n\r\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\r\n# to create new LaTeX commands to be used in formulas as building blocks. See\r\n# the section \"Including formulas\" for details.\r\n\r\nFORMULA_MACROFILE      =\r\n\r\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\r\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\r\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\r\n# installed or if you want to formulas look prettier in the HTML output. When\r\n# enabled you may also need to install MathJax separately and configure the path\r\n# to it using the MATHJAX_RELPATH option.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nUSE_MATHJAX            = NO\r\n\r\n# When MathJax is enabled you can set the default output format to be used for\r\n# the MathJax output. See the MathJax site (see:\r\n# http://docs.mathjax.org/en/latest/output.html) for more details.\r\n# Possible values are: HTML-CSS (which is slower, but has the best\r\n# compatibility), NativeMML (i.e. MathML) and SVG.\r\n# The default value is: HTML-CSS.\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_FORMAT         = HTML-CSS\r\n\r\n# When MathJax is enabled you need to specify the location relative to the HTML\r\n# output directory using the MATHJAX_RELPATH option. The destination directory\r\n# should contain the MathJax.js script. For instance, if the mathjax directory\r\n# is located at the same level as the HTML output directory, then\r\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\r\n# Content Delivery Network so you can quickly see the result without installing\r\n# MathJax. However, it is strongly recommended to install a local copy of\r\n# MathJax from https://www.mathjax.org before deployment.\r\n# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\r\n\r\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\r\n# extension names that should be enabled during MathJax rendering. For example\r\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_EXTENSIONS     =\r\n\r\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\r\n# of code that will be used on startup of the MathJax code. See the MathJax site\r\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\r\n# example see the documentation.\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_CODEFILE       =\r\n\r\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\r\n# the HTML output. The underlying search engine uses javascript and DHTML and\r\n# should work on any modern browser. Note that when using HTML help\r\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\r\n# there is already a search function so this one should typically be disabled.\r\n# For large projects the javascript based search engine can be slow, then\r\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\r\n# search using the keyboard; to jump to the search box use <access key> + S\r\n# (what the <access key> is depends on the OS and browser, but it is typically\r\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\r\n# key> to jump into the search results window, the results can be navigated\r\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\r\n# the search. The filter options can be selected when the cursor is inside the\r\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\r\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\r\n# option.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nSEARCHENGINE           = YES\r\n\r\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\r\n# implemented using a web server instead of a web client using JavaScript. There\r\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\r\n# setting. When disabled, doxygen will generate a PHP script for searching and\r\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\r\n# and searching needs to be provided by external tools. See the section\r\n# \"External Indexing and Searching\" for details.\r\n# The default value is: NO.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nSERVER_BASED_SEARCH    = NO\r\n\r\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\r\n# script for searching. Instead the search results are written to an XML file\r\n# which needs to be processed by an external indexer. Doxygen will invoke an\r\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\r\n# search results.\r\n#\r\n# Doxygen ships with an example indexer (doxyindexer) and search engine\r\n# (doxysearch.cgi) which are based on the open source search engine library\r\n# Xapian (see: https://xapian.org/).\r\n#\r\n# See the section \"External Indexing and Searching\" for details.\r\n# The default value is: NO.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nEXTERNAL_SEARCH        = NO\r\n\r\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\r\n# which will return the search results when EXTERNAL_SEARCH is enabled.\r\n#\r\n# Doxygen ships with an example indexer (doxyindexer) and search engine\r\n# (doxysearch.cgi) which are based on the open source search engine library\r\n# Xapian (see: https://xapian.org/). See the section \"External Indexing and\r\n# Searching\" for details.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nSEARCHENGINE_URL       =\r\n\r\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\r\n# search data is written to a file for indexing by an external tool. With the\r\n# SEARCHDATA_FILE tag the name of this file can be specified.\r\n# The default file is: searchdata.xml.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nSEARCHDATA_FILE        = searchdata.xml\r\n\r\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\r\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\r\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\r\n# projects and redirect the results back to the right project.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nEXTERNAL_SEARCH_ID     =\r\n\r\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\r\n# projects other than the one defined by this configuration file, but that are\r\n# all added to the same external search index. Each project needs to have a\r\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\r\n# to a relative location where the documentation can be found. The format is:\r\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nEXTRA_SEARCH_MAPPINGS  =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the LaTeX output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\r\n# The default value is: YES.\r\n\r\nGENERATE_LATEX         = NO\r\n\r\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: latex.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_OUTPUT           = latex\r\n\r\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\r\n# invoked.\r\n#\r\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\r\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\r\n# chosen this is overwritten by pdflatex. For specific output languages the\r\n# default can have been set differently, this depends on the implementation of\r\n# the output language.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_CMD_NAME         = latex\r\n\r\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\r\n# index for LaTeX.\r\n# Note: This tag is used in the Makefile / make.bat.\r\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\r\n# (.tex).\r\n# The default file is: makeindex.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nMAKEINDEX_CMD_NAME     = makeindex\r\n\r\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\r\n# generate index for LaTeX. In case there is no backslash (\\) as first character\r\n# it will be automatically added in the LaTeX code.\r\n# Note: This tag is used in the generated output file (.tex).\r\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\r\n# The default value is: makeindex.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_MAKEINDEX_CMD    = makeindex\r\n\r\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\r\n# documents. This may be useful for small projects and may help to save some\r\n# trees in general.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nCOMPACT_LATEX          = NO\r\n\r\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\r\n# printer.\r\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\r\n# 14 inches) and executive (7.25 x 10.5 inches).\r\n# The default value is: a4.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nPAPER_TYPE             = a4\r\n\r\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\r\n# that should be included in the LaTeX output. The package can be specified just\r\n# by its name or with the correct syntax as to be used with the LaTeX\r\n# \\usepackage command. To get the times font for instance you can specify :\r\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\r\n# To use the option intlimits with the amsmath package you can specify:\r\n# EXTRA_PACKAGES=[intlimits]{amsmath}\r\n# If left blank no extra packages will be included.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nEXTRA_PACKAGES         =\r\n\r\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\r\n# generated LaTeX document. The header should contain everything until the first\r\n# chapter. If it is left blank doxygen will generate a standard header. See\r\n# section \"Doxygen usage\" for information on how to let doxygen write the\r\n# default header to a separate file.\r\n#\r\n# Note: Only use a user-defined header if you know what you are doing! The\r\n# following commands have a special meaning inside the header: $title,\r\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\r\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\r\n# string, for the replacement values of the other commands the user is referred\r\n# to HTML_HEADER.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_HEADER           =\r\n\r\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\r\n# generated LaTeX document. The footer should contain everything after the last\r\n# chapter. If it is left blank doxygen will generate a standard footer. See\r\n# LATEX_HEADER for more information on how to generate a default footer and what\r\n# special commands can be used inside the footer.\r\n#\r\n# Note: Only use a user-defined footer if you know what you are doing!\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_FOOTER           =\r\n\r\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\r\n# LaTeX style sheets that are included after the standard style sheets created\r\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\r\n# will copy the style sheet files to the output directory.\r\n# Note: The order of the extra style sheet files is of importance (e.g. the last\r\n# style sheet in the list overrules the setting of the previous ones in the\r\n# list).\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_EXTRA_STYLESHEET =\r\n\r\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\r\n# other source files which should be copied to the LATEX_OUTPUT output\r\n# directory. Note that the files will be copied as-is; there are no commands or\r\n# markers available.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_EXTRA_FILES      =\r\n\r\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\r\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\r\n# contain links (just like the HTML output) instead of page references. This\r\n# makes the output suitable for online browsing using a PDF viewer.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nPDF_HYPERLINKS         = YES\r\n\r\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\r\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\r\n# files. Set this option to YES, to get a higher quality PDF documentation.\r\n#\r\n# See also section LATEX_CMD_NAME for selecting the engine.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nUSE_PDFLATEX           = YES\r\n\r\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\r\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\r\n# if errors occur, instead of asking the user for help. This option is also used\r\n# when generating formulas in HTML.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_BATCHMODE        = NO\r\n\r\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\r\n# index chapters (such as File Index, Compound Index, etc.) in the output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_HIDE_INDICES     = NO\r\n\r\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\r\n# code with syntax highlighting in the LaTeX output.\r\n#\r\n# Note that which sources are shown also depends on other settings such as\r\n# SOURCE_BROWSER.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_SOURCE_CODE      = NO\r\n\r\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\r\n# bibliography, e.g. plainnat, or ieeetr. See\r\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\r\n# The default value is: plain.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_BIB_STYLE        = plain\r\n\r\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\r\n# page will contain the date and time when the page was generated. Setting this\r\n# to NO can help when comparing the output of multiple runs.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_TIMESTAMP        = NO\r\n\r\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\r\n# path from which the emoji images will be read. If a relative path is entered,\r\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\r\n# LATEX_OUTPUT directory will be used.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_EMOJI_DIRECTORY  =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the RTF output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\r\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\r\n# readers/editors.\r\n# The default value is: NO.\r\n\r\nGENERATE_RTF           = NO\r\n\r\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: rtf.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_OUTPUT             = rtf\r\n\r\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\r\n# documents. This may be useful for small projects and may help to save some\r\n# trees in general.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nCOMPACT_RTF            = NO\r\n\r\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\r\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\r\n# output) instead of page references. This makes the output suitable for online\r\n# browsing using Word or some other Word compatible readers that support those\r\n# fields.\r\n#\r\n# Note: WordPad (write) and others do not support links.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_HYPERLINKS         = NO\r\n\r\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\r\n# configuration file, i.e. a series of assignments. You only have to provide\r\n# replacements, missing definitions are set to their default value.\r\n#\r\n# See also section \"Doxygen usage\" for information on how to generate the\r\n# default style sheet that doxygen normally uses.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_STYLESHEET_FILE    =\r\n\r\n# Set optional variables used in the generation of an RTF document. Syntax is\r\n# similar to doxygen's configuration file. A template extensions file can be\r\n# generated using doxygen -e rtf extensionFile.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_EXTENSIONS_FILE    =\r\n\r\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\r\n# with syntax highlighting in the RTF output.\r\n#\r\n# Note that which sources are shown also depends on other settings such as\r\n# SOURCE_BROWSER.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_SOURCE_CODE        = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the man page output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\r\n# classes and files.\r\n# The default value is: NO.\r\n\r\nGENERATE_MAN           = NO\r\n\r\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it. A directory man3 will be created inside the directory specified by\r\n# MAN_OUTPUT.\r\n# The default directory is: man.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_OUTPUT             = man\r\n\r\n# The MAN_EXTENSION tag determines the extension that is added to the generated\r\n# man pages. In case the manual section does not start with a number, the number\r\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\r\n# optional.\r\n# The default value is: .3.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_EXTENSION          = .3\r\n\r\n# The MAN_SUBDIR tag determines the name of the directory created within\r\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\r\n# MAN_EXTENSION with the initial . removed.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_SUBDIR             =\r\n\r\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\r\n# will generate one additional man file for each entity documented in the real\r\n# man page(s). These additional files only source the real man page, but without\r\n# them the man command would be unable to find the correct page.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_LINKS              = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the XML output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\r\n# captures the structure of the code including all documentation.\r\n# The default value is: NO.\r\n\r\nGENERATE_XML           = NO\r\n\r\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: xml.\r\n# This tag requires that the tag GENERATE_XML is set to YES.\r\n\r\nXML_OUTPUT             = xml\r\n\r\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\r\n# listings (including syntax highlighting and cross-referencing information) to\r\n# the XML output. Note that enabling this will significantly increase the size\r\n# of the XML output.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_XML is set to YES.\r\n\r\nXML_PROGRAMLISTING     = YES\r\n\r\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\r\n# namespace members in file scope as well, matching the HTML output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_XML is set to YES.\r\n\r\nXML_NS_MEMB_FILE_SCOPE = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the DOCBOOK output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\r\n# that can be used to generate PDF.\r\n# The default value is: NO.\r\n\r\nGENERATE_DOCBOOK       = NO\r\n\r\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\r\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\r\n# front of it.\r\n# The default directory is: docbook.\r\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\r\n\r\nDOCBOOK_OUTPUT         = docbook\r\n\r\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\r\n# program listings (including syntax highlighting and cross-referencing\r\n# information) to the DOCBOOK output. Note that enabling this will significantly\r\n# increase the size of the DOCBOOK output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\r\n\r\nDOCBOOK_PROGRAMLISTING = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options for the AutoGen Definitions output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\r\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\r\n# the structure of the code including all documentation. Note that this feature\r\n# is still experimental and incomplete at the moment.\r\n# The default value is: NO.\r\n\r\nGENERATE_AUTOGEN_DEF   = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the Perl module output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\r\n# file that captures the structure of the code including all documentation.\r\n#\r\n# Note that this feature is still experimental and incomplete at the moment.\r\n# The default value is: NO.\r\n\r\nGENERATE_PERLMOD       = NO\r\n\r\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\r\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\r\n# output from the Perl module output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\r\n\r\nPERLMOD_LATEX          = NO\r\n\r\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\r\n# formatted so it can be parsed by a human reader. This is useful if you want to\r\n# understand what is going on. On the other hand, if this tag is set to NO, the\r\n# size of the Perl module output will be much smaller and Perl will parse it\r\n# just the same.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\r\n\r\nPERLMOD_PRETTY         = YES\r\n\r\n# The names of the make variables in the generated doxyrules.make file are\r\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\r\n# so different doxyrules.make files included by the same Makefile don't\r\n# overwrite each other's variables.\r\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\r\n\r\nPERLMOD_MAKEVAR_PREFIX =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the preprocessor\r\n#---------------------------------------------------------------------------\r\n\r\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\r\n# C-preprocessor directives found in the sources and include files.\r\n# The default value is: YES.\r\n\r\nENABLE_PREPROCESSING   = YES\r\n\r\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\r\n# in the source code. If set to NO, only conditional compilation will be\r\n# performed. Macro expansion can be done in a controlled way by setting\r\n# EXPAND_ONLY_PREDEF to YES.\r\n# The default value is: NO.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nMACRO_EXPANSION        = NO\r\n\r\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\r\n# the macro expansion is limited to the macros specified with the PREDEFINED and\r\n# EXPAND_AS_DEFINED tags.\r\n# The default value is: NO.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nEXPAND_ONLY_PREDEF     = NO\r\n\r\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\r\n# INCLUDE_PATH will be searched if a #include is found.\r\n# The default value is: YES.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nSEARCH_INCLUDES        = YES\r\n\r\n# The INCLUDE_PATH tag can be used to specify one or more directories that\r\n# contain include files that are not input files but should be processed by the\r\n# preprocessor.\r\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\r\n\r\nINCLUDE_PATH           =\r\n\r\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\r\n# patterns (like *.h and *.hpp) to filter out the header-files in the\r\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\r\n# used.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nINCLUDE_FILE_PATTERNS  =\r\n\r\n# The PREDEFINED tag can be used to specify one or more macro names that are\r\n# defined before the preprocessor is started (similar to the -D option of e.g.\r\n# gcc). The argument of the tag is a list of macros of the form: name or\r\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\r\n# is assumed. To prevent a macro definition from being undefined via #undef or\r\n# recursively expanded use the := operator instead of the = operator.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nPREDEFINED             =\r\n\r\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\r\n# tag can be used to specify a list of macro names that should be expanded. The\r\n# macro definition that is found in the sources will be used. Use the PREDEFINED\r\n# tag if you want to use a different macro definition that overrules the\r\n# definition found in the source code.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nEXPAND_AS_DEFINED      =\r\n\r\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\r\n# remove all references to function-like macros that are alone on a line, have\r\n# an all uppercase name, and do not end with a semicolon. Such function macros\r\n# are typically used for boiler-plate code, and will confuse the parser if not\r\n# removed.\r\n# The default value is: YES.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nSKIP_FUNCTION_MACROS   = YES\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to external references\r\n#---------------------------------------------------------------------------\r\n\r\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\r\n# file the location of the external documentation should be added. The format of\r\n# a tag file without this location is as follows:\r\n# TAGFILES = file1 file2 ...\r\n# Adding location for the tag files is done as follows:\r\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\r\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\r\n# section \"Linking to external documentation\" for more information about the use\r\n# of tag files.\r\n# Note: Each tag file must have a unique name (where the name does NOT include\r\n# the path). If a tag file is not located in the directory in which doxygen is\r\n# run, you must also specify the path to the tagfile here.\r\n\r\nTAGFILES               =\r\n\r\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\r\n# tag file that is based on the input files it reads. See section \"Linking to\r\n# external documentation\" for more information about the usage of tag files.\r\n\r\nGENERATE_TAGFILE       =\r\n\r\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\r\n# the class index. If set to NO, only the inherited external classes will be\r\n# listed.\r\n# The default value is: NO.\r\n\r\nALLEXTERNALS           = NO\r\n\r\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\r\n# in the modules index. If set to NO, only the current project's groups will be\r\n# listed.\r\n# The default value is: YES.\r\n\r\nEXTERNAL_GROUPS        = YES\r\n\r\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\r\n# the related pages index. If set to NO, only the current project's pages will\r\n# be listed.\r\n# The default value is: YES.\r\n\r\nEXTERNAL_PAGES         = YES\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the dot tool\r\n#---------------------------------------------------------------------------\r\n\r\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\r\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\r\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\r\n# disabled, but it is recommended to install and use dot, since it yields more\r\n# powerful graphs.\r\n# The default value is: YES.\r\n\r\nCLASS_DIAGRAMS         = YES\r\n\r\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\r\n# then run dia to produce the diagram and insert it in the documentation. The\r\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\r\n# If left empty dia is assumed to be found in the default search path.\r\n\r\nDIA_PATH               =\r\n\r\n# If set to YES the inheritance and collaboration graphs will hide inheritance\r\n# and usage relations if the target is undocumented or is not a class.\r\n# The default value is: YES.\r\n\r\nHIDE_UNDOC_RELATIONS   = YES\r\n\r\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\r\n# available from the path. This tool is part of Graphviz (see:\r\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\r\n# Bell Labs. The other options in this section have no effect if this option is\r\n# set to NO\r\n# The default value is: NO.\r\n\r\nHAVE_DOT               = NO\r\n\r\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\r\n# to run in parallel. When set to 0 doxygen will base this on the number of\r\n# processors available in the system. You can set it explicitly to a value\r\n# larger than 0 to get control over the balance between CPU load and processing\r\n# speed.\r\n# Minimum value: 0, maximum value: 32, default value: 0.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_NUM_THREADS        = 0\r\n\r\n# When you want a differently looking font in the dot files that doxygen\r\n# generates you can specify the font name using DOT_FONTNAME. You need to make\r\n# sure dot is able to find the font, which can be done by putting it in a\r\n# standard location or by setting the DOTFONTPATH environment variable or by\r\n# setting DOT_FONTPATH to the directory containing the font.\r\n# The default value is: Helvetica.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_FONTNAME           =\r\n\r\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\r\n# dot graphs.\r\n# Minimum value: 4, maximum value: 24, default value: 10.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_FONTSIZE           = 10\r\n\r\n# By default doxygen will tell dot to use the default font as specified with\r\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\r\n# the path where dot can find it using this tag.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_FONTPATH           =\r\n\r\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\r\n# each documented class showing the direct and indirect inheritance relations.\r\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCLASS_GRAPH            = YES\r\n\r\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\r\n# graph for each documented class showing the direct and indirect implementation\r\n# dependencies (inheritance, containment, and class references variables) of the\r\n# class with other documented classes.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCOLLABORATION_GRAPH    = YES\r\n\r\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\r\n# groups, showing the direct groups dependencies.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nGROUP_GRAPHS           = YES\r\n\r\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\r\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\r\n# Language.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nUML_LOOK               = NO\r\n\r\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\r\n# class node. If there are many fields or methods and many nodes the graph may\r\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\r\n# number of items for each type to make the size more manageable. Set this to 0\r\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\r\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\r\n# but if the number exceeds 15, the total amount of fields shown is limited to\r\n# 10.\r\n# Minimum value: 0, maximum value: 100, default value: 10.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nUML_LIMIT_NUM_FIELDS   = 10\r\n\r\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\r\n# collaboration graphs will show the relations between templates and their\r\n# instances.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nTEMPLATE_RELATIONS     = NO\r\n\r\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\r\n# YES then doxygen will generate a graph for each documented file showing the\r\n# direct and indirect include dependencies of the file with other documented\r\n# files.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nINCLUDE_GRAPH          = YES\r\n\r\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\r\n# set to YES then doxygen will generate a graph for each documented file showing\r\n# the direct and indirect include dependencies of the file with other documented\r\n# files.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nINCLUDED_BY_GRAPH      = NO\r\n\r\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\r\n# dependency graph for every global function or class method.\r\n#\r\n# Note that enabling this option will significantly increase the time of a run.\r\n# So in most cases it will be better to enable call graphs for selected\r\n# functions only using the \\callgraph command. Disabling a call graph can be\r\n# accomplished by means of the command \\hidecallgraph.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCALL_GRAPH             = NO\r\n\r\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\r\n# dependency graph for every global function or class method.\r\n#\r\n# Note that enabling this option will significantly increase the time of a run.\r\n# So in most cases it will be better to enable caller graphs for selected\r\n# functions only using the \\callergraph command. Disabling a caller graph can be\r\n# accomplished by means of the command \\hidecallergraph.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCALLER_GRAPH           = NO\r\n\r\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\r\n# hierarchy of all classes instead of a textual one.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nGRAPHICAL_HIERARCHY    = YES\r\n\r\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\r\n# dependencies a directory has on other directories in a graphical way. The\r\n# dependency relations are determined by the #include relations between the\r\n# files in the directories.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDIRECTORY_GRAPH        = YES\r\n\r\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\r\n# generated by dot. For an explanation of the image formats see the section\r\n# output formats in the documentation of the dot tool (Graphviz (see:\r\n# http://www.graphviz.org/)).\r\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\r\n# to make the SVG files visible in IE 9+ (other browsers do not have this\r\n# requirement).\r\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\r\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\r\n# png:gdiplus:gdiplus.\r\n# The default value is: png.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_IMAGE_FORMAT       = png\r\n\r\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\r\n# enable generation of interactive SVG images that allow zooming and panning.\r\n#\r\n# Note that this requires a modern browser other than Internet Explorer. Tested\r\n# and working are Firefox, Chrome, Safari, and Opera.\r\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\r\n# the SVG files visible. Older versions of IE do not have SVG support.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nINTERACTIVE_SVG        = NO\r\n\r\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\r\n# found. If left blank, it is assumed the dot tool can be found in the path.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_PATH               =\r\n\r\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\r\n# contain dot files that are included in the documentation (see the \\dotfile\r\n# command).\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOTFILE_DIRS           =\r\n\r\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\r\n# contain msc files that are included in the documentation (see the \\mscfile\r\n# command).\r\n\r\nMSCFILE_DIRS           =\r\n\r\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\r\n# contain dia files that are included in the documentation (see the \\diafile\r\n# command).\r\n\r\nDIAFILE_DIRS           =\r\n\r\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\r\n# path where java can find the plantuml.jar file. If left blank, it is assumed\r\n# PlantUML is not used or called during a preprocessing step. Doxygen will\r\n# generate a warning when it encounters a \\startuml command in this case and\r\n# will not generate output for the diagram.\r\n\r\nPLANTUML_JAR_PATH      =\r\n\r\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\r\n# configuration file for plantuml.\r\n\r\nPLANTUML_CFG_FILE      =\r\n\r\n# When using plantuml, the specified paths are searched for files specified by\r\n# the !include statement in a plantuml block.\r\n\r\nPLANTUML_INCLUDE_PATH  =\r\n\r\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\r\n# that will be shown in the graph. If the number of nodes in a graph becomes\r\n# larger than this value, doxygen will truncate the graph, which is visualized\r\n# by representing a node as a red box. Note that doxygen if the number of direct\r\n# children of the root node in a graph is already larger than\r\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\r\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\r\n# Minimum value: 0, maximum value: 10000, default value: 50.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_GRAPH_MAX_NODES    = 50\r\n\r\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\r\n# generated by dot. A depth value of 3 means that only nodes reachable from the\r\n# root by following a path via at most 3 edges will be shown. Nodes that lay\r\n# further from the root node will be omitted. Note that setting this option to 1\r\n# or 2 may greatly reduce the computation time needed for large code bases. Also\r\n# note that the size of a graph can be further restricted by\r\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\r\n# Minimum value: 0, maximum value: 1000, default value: 0.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nMAX_DOT_GRAPH_DEPTH    = 0\r\n\r\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\r\n# background. This is disabled by default, because dot on Windows does not seem\r\n# to support this out of the box.\r\n#\r\n# Warning: Depending on the platform used, enabling this option may lead to\r\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\r\n# read).\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_TRANSPARENT        = NO\r\n\r\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\r\n# files in one run (i.e. multiple -o and -T options on the command line). This\r\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\r\n# this, this feature is disabled by default.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_MULTI_TARGETS      = NO\r\n\r\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\r\n# explaining the meaning of the various boxes and arrows in the dot generated\r\n# graphs.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nGENERATE_LEGEND        = YES\r\n\r\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\r\n# files that are used to generate the various graphs.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_CLEANUP            = YES\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/doxygen_prebuild.cmake.in",
    "content": "set(SIBR_PROJECTS_SAMPLES_SUBPAGE_REF   \"@SIBR_PROJECTS_SAMPLES_SUBPAGE_REF@\")\nset(SIBR_PROJECTS_OURS_SUBPAGE_REF      \"@SIBR_PROJECTS_OURS_SUBPAGE_REF@\")\nset(SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF   \"@SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF@\")\nset(SIBR_PROJECTS_OTHERS_SUBPAGE_REF    \"@SIBR_PROJECTS_OTHERS_SUBPAGE_REF@\")\nset(SIBR_PROJECTS_SAMPLES_REF_REF       \"@SIBR_PROJECTS_SAMPLES_REF_REF@\")\nset(SIBR_PROJECTS_OURS_REF_REF          \"@SIBR_PROJECTS_OURS_REF_REF@\")\nset(SIBR_PROJECTS_TOOLBOX_REF_REF       \"@SIBR_PROJECTS_TOOLBOX_REF_REF@\")\nset(SIBR_PROJECTS_OTHERS_REF_REF        \"@SIBR_PROJECTS_OTHERS_REF_REF@\")\nset(DOXY_DOC_DEST_DIR\t\t\t\t    \"@DOXY_DOC_DEST_DIR@\")\nset(DOXY_DOC_GENERATED_DOC_DIR\t\t    \"@DOXY_DOC_GENERATED_DOC_DIR@\")\nset(DOXY_DOC_PAGES_DIR\t\t\t\t    \"@DOXY_DOC_PAGES_DIR@\")\n\n## Cleaning documentation folders\nfile(REMOVE_RECURSE \"${DOXY_DOC_GENERATED_DOC_DIR}\")\nfile(REMOVE_RECURSE \"${DOXY_DOC_DEST_DIR}\")\n\n## Generating documentation pages with variables\nfile(GLOB_RECURSE doc_files \"${DOXY_DOC_PAGES_DIR}/*.in\")\nforeach(filename ${doc_files})\n    message(STATUS \"Generating ${filename}...\")\n    get_filename_component(output_filename ${filename} NAME_WLE)\n    message(STATUS \"Output in ${DOXY_DOC_GENERATED_DOC_DIR}/${output_filename}...\")\n    configure_file(${filename} ${DOXY_DOC_GENERATED_DOC_DIR}/${output_filename} @ONLY)\nendforeach()"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/layout.xml.in",
    "content": "<doxygenlayout version=\"1.0\">\n  <!-- Layout definition for the navigation index -->\n  <navindex>\n    <tab type=\"mainpage\" visible=\"yes\" title=\"\"/>\n    <tab type=\"pages\" visible=\"yes\" title=\"\" intro=\"\"/>\n    <tab type=\"usergroup\" title=\"Code Reference\" url=\"[none]\">\n      <tab type=\"modules\" visible=\"yes\" title=\"Modules\" intro=\"\"/>\n      <tab type=\"namespaces\" visible=\"yes\" title=\"Namespaces\">\n        <tab type=\"namespacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"namespacemembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"interfaces\" visible=\"yes\" title=\"Interfaces\">\n        <tab type=\"interfacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"interfaceindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/> \n        <tab type=\"interfacehierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"classes\" visible=\"yes\" title=\"Classes\">\n        <tab type=\"classlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"classindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/> \n        <tab type=\"hierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"classmembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"structs\" visible=\"yes\" title=\"Structs\">\n        <tab type=\"structlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"structindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/> \n      </tab>\n      <tab type=\"exceptions\" visible=\"yes\" title=\"Exceptions\">\n        <tab type=\"exceptionlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"exceptionindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/> \n        <tab type=\"exceptionhierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"files\" visible=\"yes\" title=\"Files\">\n        <tab type=\"filelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"globals\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"examples\" visible=\"yes\" title=\"Examples\" intro=\"\"/>\n    </tab>\n    <tab type=\"usergroup\" title=\"Changelog\" url=\"[none]\">\n      <tab type=\"user\" visible=\"yes\" url=\"@ref deprecated\" title=\"Deprecated features\" intro=\"\"/>\n      <tab type=\"user\" visible=\"yes\" url=\"@ref bug\" title=\"Known bugs\" intro=\"\"/>\n      <tab type=\"user\" visible=\"yes\" url=\"@ref todo\" title=\"Future changes\" intro=\"\"/>\n    </tab>\n  </navindex>\n\n  <!-- Layout definition for a class page -->\n  <class>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_INCLUDE_FILES\"/>\n    <inheritancegraph visible=\"$CLASS_GRAPH\"/>\n    <collaborationgraph visible=\"$COLLABORATION_GRAPH\"/>\n    <memberdecl>\n      <nestedclasses visible=\"yes\" title=\"\"/>\n      <publictypes title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <publicslots title=\"\"/>\n      <signals title=\"\"/>\n      <publicmethods title=\"\"/>\n      <publicstaticmethods title=\"\"/>\n      <publicattributes title=\"\"/>\n      <publicstaticattributes title=\"\"/>\n      <protectedtypes title=\"\"/>\n      <protectedslots title=\"\"/>\n      <protectedmethods title=\"\"/>\n      <protectedstaticmethods title=\"\"/>\n      <protectedattributes title=\"\"/>\n      <protectedstaticattributes title=\"\"/>\n      <packagetypes title=\"\"/>\n      <packagemethods title=\"\"/>\n      <packagestaticmethods title=\"\"/>\n      <packageattributes title=\"\"/>\n      <packagestaticattributes title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n      <privatetypes title=\"\"/>\n      <privateslots title=\"\"/>\n      <privatemethods title=\"\"/>\n      <privatestaticmethods title=\"\"/>\n      <privateattributes title=\"\"/>\n      <privatestaticattributes title=\"\"/>\n      <friends title=\"\"/>\n      <related title=\"\" subtitle=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <constructors title=\"\"/>\n      <functions title=\"\"/>\n      <related title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n    </memberdef>\n    <allmemberslink visible=\"yes\"/>\n    <usedfiles visible=\"$SHOW_USED_FILES\"/>\n    <authorsection visible=\"yes\"/>\n  </class>\n\n  <!-- Layout definition for a namespace page -->\n  <namespace>\n    <briefdescription visible=\"yes\"/>\n    <memberdecl>\n      <nestednamespaces visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <interfaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <structs visible=\"yes\" title=\"\"/>\n      <exceptions visible=\"yes\" title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </namespace>\n\n  <!-- Layout definition for a file page -->\n  <file>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_INCLUDE_FILES\"/>\n    <includegraph visible=\"$INCLUDE_GRAPH\"/>\n    <includedbygraph visible=\"$INCLUDED_BY_GRAPH\"/>\n    <sourcelink visible=\"yes\"/>\n    <memberdecl>\n      <interfaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <structs visible=\"yes\" title=\"\"/>\n      <exceptions visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n    </memberdef>\n    <authorsection/>\n  </file>\n\n  <!-- Layout definition for a group page -->\n  <group>\n    <briefdescription visible=\"yes\"/>\n    <groupgraph visible=\"$GROUP_GRAPHS\"/>\n    <memberdecl>\n      <nestedgroups visible=\"yes\" title=\"\"/>\n      <dirs visible=\"yes\" title=\"\"/>\n      <files visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <pagedocs/>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </group>\n\n  <!-- Layout definition for a directory page -->\n  <directory>\n    <briefdescription visible=\"yes\"/>\n    <directorygraph visible=\"yes\"/>\n    <memberdecl>\n      <dirs visible=\"yes\"/>\n      <files visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n  </directory>\n</doxygenlayout>"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/1_Getting_Started.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@mainpage Getting Started\n\n@section intro_sec Introduction\nThe System for Image-Based Rendering or **SIBR**, is a specialized collection of libraries and toolkits for quickly implementing Image-Based Rendering (IBR) algorithms, and includes implementations of several published IBR papers, mainly from Inria and UCL, but also (re-implementations) of projects from other research groups. Most of **SIBR** was developed over the years (since 2011) at Inria Sophia-Antipolis for the various IBR research projects in the group, but the codebase includes a significant part of code from the *fribr* codebase of P. Hedman from UCL. The codebase has a long history; see @ref sibr_history section.\n\nIn this first release, we are providing reference source code implementations and datasets for the following \\ref sibr_projects_available.\n\nWe will be progressively releasing more implementations of past and future projects (please see the @ref sibr_roadmap). For the first few releases, the main intended usage of the codebase is comparisons with previous algorithms. In the medium-term future, we hope that the codebase will be useful to others for the development of their own IBR algorithms.\n\nThe codebase contains three main components: the main *core* library, various utility/helper libraries and what we call *Projects* (see @ref sibr_projects_about), that are the implementations of Inria, UCL (and other) research projects.\nThe core library has support for multi-view (MV) datasets, processed with Structure-from-Motion (SfM) and Multi-view Stereo (MVS) software. We support data from various SfM/MVS sources, such as *colmap* and the (commercial) *RealityCapture* package (see @ref howto_generate_dataset), and a some interactive viewing utilities for developing and debugging IBR algorithms (e.g., ``top view'' shown below). \n\n@image HTML ulr_screenshot.png An example view of SIBR width=700px\n\nThe utilities include various MV dataset (pre-)processing utilities that are used by various *Projects* and can be of general use, and various utilities for different API interfaces (e.g., tensorflow and pytorch, OptiX) and the core *fribr* framework from UCL that is used in some *Projects*.\n\nEach *Project* provided in **SIBR** has (more or less) similar documentation and code structure: a binary release for easy use, how to checkout the code, configure, build and install the solution, and run the basic renderer; a set if fully-processed datasets are provided for each project. The *Project* has *apps*, typically including a *rendering app* that is usually enough to run the method to compare with a new algorithm on one of our datasets.\nMost projects also have a *preprocessing* step. The code for preprocessing allows processing of your own data with the corresponding algorithm; the datasets provided have been processed with these tools. \nPlease see the section on comparisons (@ref comparisons_sec) below.\n\nThe first *Project* is bundled in the *core* library, and has implementations of per-pixel variants of the Unstructured Lumigraph \\[Buehler 2001\\] (see the @ref ulrPage page). This implementation has been used as a baseline comparison in many of our projects, and is always a useful baseline for any new IBR algorithm.\n\nThe licensing of the *core* is free for non-commercial, research and evaluation purposes, by academic or industrial labs, as defined in the LICENSE.md file. For commercial usage in a for-profit product, a paid license is required; please contact George.Drettakis@inria.fr if you are interested.\n\nThe rest of this page explains how to download binaries, compile, configure and install the basic system and its documentation.\n\nIf you use this code in a publication, please cite the system as follows in your publications:\n\n```\n@misc{sibr2020,\n   author       = \"Bonopera, Sebastien and Hedman, Peter and Esnault, Jerome and Prakash, Siddhant and Rodriguez, Simon and Thonat, Theo and Benadel, Mehdi and Chaurasia, Gaurav and Philip, Julien and Drettakis, George\",\n   title        = \"sibr: A System for Image Based Rendering\",\n   year         = \"2020\",\n   url          = \"https://sibr.gitlabpages.inria.fr/\"\n}\n```\n\n@section comparison_sec Comparisons\n\nA major goal of this code release is to allow comparisons. Most renderers in the *Projects* are interactive, allowing free-viewpoint navigation in the corresponding scenes. For comparisons, most renderers take a <code>--pathFile</code> argument that is a path of cameras in the scene. These are written to the directory requested, or by default in the <code>pathOutput</code> directory at the root of the dataset. For more details on cameras and paths, see the tutorial page @ref howto_cameras_paths .\n\n@section install_sec Installation\n\n**Note**: The current release is for *Windows 10* only. We are planning a Linux release soon.\n\n@subsection sibr_binaries Binary distribution\n\nThe easiest way to use *SIBR* is to download the binary distribution. All steps described below, including all preprocessing for your datasets will work using this code.\nA binary distribution (200Mb) of the core is available here:\n\n```\nwget https://repo-sam.inria.fr/fungraph/sibr-release/sibr-core/install.zip\n```\n\nunzip to create the ``install'' directory. All instructions below on running the code can be performed using this binary distribution.\n\n@subsection sibr_example To run an example\n\nDownload a dataset from: https://repo-sam.inria.fr/fungraph/sibr-datasets/\n\ne.g., the *sibr-museum-front* dataset in the *DATASETS_PATH* directory.\n\n```\nwget https://repo-sam.inria.fr/fungraph/sibr-datasets/museum_front27_ulr.zip\n```\n\nOnce you have built the system or downloaded the binaries (see above), go to *install/bin* and you can run:\n```\n\tsibr_ulrv2_app.exe --path DATASETS_PATH/sibr-museum-front\n```\n\nYou will have an interactive viewer and you can navigate freely in the captured scene. \nOur default interactive viewer has a main view running the algorithm and a top view to visualize the position of the calibrated cameras. By default you are in WASD mode, and can toggle to trackball using the \"y\" key. Please see the page [Interface](https://sibr.gitlabpages.inria.fr/docs/nightly/howto_sibr_useful_objects.html) for more details on the interface.\n\n\n@subsection sibr_prerequisite Prerequisites\n\n- git\n- Visual Studio 2019 (https://visualstudio.microsoft.com/).\n- Cmake 3.16+,(https://cmake.org/).\n- 7zip should be installed (https://www.7-zip.org/download.html).\n- Python 3.8+ should be installed and configured in the PATH (https://www.python.org/downloads/).\n- PIL (pip install pillow, in an admin command if needed: https://pypi.org/project/Pillow/) might be needed for some preprocess scripts.\n- ImageMagick (https://imagemagick.org/script/download.php).\n- Doxygen 1.8.17+ should be installed and configured in the PATH for generating the documentation (http://www.doxygen.nl/download.html).\n- If needed (Optix, Tensorflow, Pytorch,... integration), CUDA 10.1+ (https://developer.nvidia.com/cuda-downloads) and cuDNN.\n- An internet connection, as external dependencies will be downloaded from our servers during the CMake configuration.\n\n@subsection sibr_checkout Checkout the code\n\n- Clone sibr_core repository (https://gitlab.inria.fr/sibr/sibr_core). We recommend that you checkout master branch, but you can also work with the develop branch (unstable).\n@code\n## through HTTPS\ngit clone https://gitlab.inria.fr/sibr/sibr_core.git -b master\n## through SSH\ngit clone git@gitlab.inria.fr:sibr/sibr_core.git -b master\n@endcode\n- You can add the projects' source code you would like to compile with SIBR, for this see @ref sibr_projects_add)\n@subsection sibr_configure_cmake Configuring the solution\n\n- Run Cmake, select SIBR root folder as a source directory and \\<sibr_root_folder\\>/build/ as the build directory.\n- Configure, select the Visual Studio C++ Win64 compiler.\n- Select the projects you want to generate among the BUILD_* variables in the list.\n- Generate.\n\n@subsection sibr_compile Compiling\n\n- Configure the solution & Generate like we did in @ref sibr_configure_cmake.\n- Open the generated Visual Studio solution (sibr_root/build/sibr_projects.sln).\n- Build the BUILD_ALL target, and then the INSTALL target.\n- The compiled executables will be put in install/bin.\n@note If install fails, you will have to copy the required .dll files which are not copied automatically in the install/bin directory.\n      In general make sure these .dll files are in the bin directory : boost_system-vc141-mt-1_64.dll, boost_filesystem-vc141-mt-1_64.dll, glew32.dll, assimp-vc140-mt.dll, embree.dll.\n      Alternatively, you can selectively execute some of the install targets for specific projects.\n\n@subsection sibr_generate_documentation Generating the documentation\n\n- Configure the solution like we did in @ref sibr_configure_cmake and choose BUILD_DOCUMENTATION along with the projects you want in the documentation. Then generate the solution.\n- Open the generated Visual Studio solution (sibr_root/build/sibr_projects.sln).\n- Build the DOCUMENTATION target.\n- The generated documentation will be put in docs and can be accessed through install/docs/index.html.\n\n@subsection sibr_troubleshooting Troubleshoot\n\n- Cmake can't find GLU, GLEW or another library: use an up-to-date CMake, check that you are connected to the Internet.\n- Weird OpenCV error: you probably selected the 32-bits compiler in cmake-gui.\n- 'Cmd.exe failed wither error 009' or similar: make sure Python is installed and in the PATH.\n- BUILD_ALL or INSTALL fail because of a project you don't really need: build and install each project separately by selecting the proper targets.\n- Some projects may depend on other projects. Make sure you have checked all the required projects before generating the solution.\n- Error in CUDA headers under Visual Studio 2019: make sure CUDA >= 10.1 is installed.\n\n@subsection sibr_bugs Bugs and Issues\n\nWe will track bugs and issues through the Issues interface on gitlab. Inria gitlab does not allow creation of external accounts, so if you have an issue/bug please email <code>sibr@inria.fr</code> and we will either create a guest account or create the issue on our side.\n\n@subsection sibr_main_authors Authors\n\nThe authors of **SIBR** *core* are Gaurav Chaurasia (Ph.D. Inria, 2011-2013), Rodrigo Ortiz-Cayon (Ph.D. Inria 2013-2016), Jerome Esnault (Software Engineer, Inria, DATES), Sebastien Bonopera (Software Engineer, Inria, DATES), Theo Thonat (Ph.D. Inria 2015-2019), Simon Rodriguez (Ph.D. Inria, 2017-2020), Julien Philip (Ph.D. Inria, 2017-2020), Siddhant Prakash (Soft. Engineer & currently Ph.D. Inria). Mehdi Benadel (Soft. Engineer, Inria) is currently in charge of **SIBR**. George Drettakis had the overall supervision of the project throughout. See also @ref sibr_history.\n\n\nEach *Project* has different authors, who are listed in the corresponding project pages. A special mention is due to Peter Hedman (at UCL) who wrote the *fribr* framework used in several projects.\n\n@subsection sibr_funding Funding\n\nThe various projects in **SIBR** were funded by Inria, French national and European research funds. These include French Ministry of Education and University of Nice Sophia-Antipolis (now Universite Cote d'Azur) funds (G. Chaurasia, S. Rodriguez), the ANR SEMAPOLIS project (https://project.inria.fr/semapolis/) and the Region Provence Alpes Cote d'Azur (T. Thonat), the EU projects VERVE (https://gv2.scss.tcd.ie/VERVE/) G. Chaurasia; CR-PLAY (http://www.cr-play.eu/) R. Ortiz-Cayon, J. Esnault, S. Bonopera; EMOTIVE (https://emotiveproject.eu/) S. Duchene, J. Philip and the ERC FUNGRAPH project (http://fungraph.inria.fr), J. Philip, S. Rodriguez, S. Prakash, S. Morgenthaler, M. Benadel. The FRIBR code was funded by the Rabin Ezra scholarship fund for P. Hedman at UCL.\n\n@section sibr_history History of SIBR\n\nThe oldest code in SIBR was written by G. Chaurasia for his Ph.D. in 2011-2013 at what was then the REVES (LINK) research group at Inria Sophia-Antipolis. The initial ULR implementation, and parts of the code in the Superpixel Warp project [Chaurasia 2013] are from this work. Gaurav re-factored the code to allow it work first with Qt and then with OpenSceneGraph, in a code base called *ibr_collection*, used for the EU project VERVE (https://gv2.scss.tcd.ie/VERVE/). In the followup CR-PLAY (http://www.cr-play.eu/) project, the code was refactored by S. Bonopera, and as part of the ERC FUNGRAPH S. Prakash was in charge of designing the current core/projects structure. M. Benadel has been in charge of cleanup for the release. The overall development of the entire project was managed by G. Drettakis.\n\n@section sibr_roadmap SIBR Release Roadmap\n\nAfter the first release (see \\ref sibr_projects_available ), we plan to release code for the deep learning solution for outdoor relighting [Philip 19] (Jan 21), and potentially for other recent IBR projects from the GRAPHDECO group. \nWe will be releasing a Linux version of the core, and for some of the *Projects* soon. Stay tuned.\n\n@subsection sibr_main_authors Authors\n\nThe authors of **SIBR** *core* are Gaurav Chaurasia (Ph.D. Inria, 2011-2013), Rodrigo Ortiz-Cayon (Ph.D. Inria 2013-2016), Jerome Esnault (Software Engineer, Inria, DATES), Sebastien Bonopera (Software Engineer, Inria, DATES), Theo Thonat (Ph.D. Inria 2015-2019), Simon Rodriguez (Ph.D. Inria, 2017-2020), Julien Philip (Ph.D. Inria, 2017-2020), Siddhant Prakash (Soft. Engineer & currently Ph.D. Inria). Mehdi Benadel (Soft. Engineer, Inria) is currently in charge of **SIBR**. George Drettakis had the overall supervision of the project throughout. See also @ref sibr_history.\n\n\nEach *Project* has different authors, who are listed in the corresponding project pages. A special mention is due to Peter Hedman (at UCL) who wrote the *fribr* framework used in several projects.\n\n@subsection sibr_funding Funding\n\nThe various projects in **SIBR** were funded by Inria, French national and European research funds. These include French Ministry of Education and University of Nice Sophia-Antipolis (now Universite Cote d'Azur) funds (G. Chaurasia, S. Rodriguez), the ANR SEMAPOLIS project (https://project.inria.fr/semapolis/) and the Region Provence Alpes Cote d'Azur (T. Thonat), the EU projects VERVE (https://gv2.scss.tcd.ie/VERVE/) G. Chaurasia; CR-PLAY (http://www.cr-play.eu/) R. Ortiz-Cayon, J. Esnault, S. Bonopera; EMOTIVE (https://emotiveproject.eu/) S. Duchene, J. Philip and the ERC FUNGRAPH project (http://fungraph.inria.fr), J. Philip, S. Rodriguez, S. Prakash, S. Morgenthaler, M. Benadel. The FRIBR code was funded by the Rabin Ezra scholarship fund for P. Hedman at UCL.\n\n@section sibr_references References\n[Chaurasia 13] G. Chaurasia, S. Duchene, O. Sorkine-Hornung, & G. Drettakis. (2013). Depth synthesis and local warps for plausible image-based navigation. ACM Transactions on Graphics (TOG), 32(3), 30. http://www-sop.inria.fr/reves/Basilic/2013/CDSD13/\n\n[Philip 19] J. Philip, M. Gharbi, T. Zhou, A. Efros, G. Drettakis (2019), Multi-view Relighting Using a Geometry-Aware Network   Multi-view-Relighting.pdf ACM Transactions on Graphics (SIGGRAPH Conference Proceedings), Volume 38, Number 4 - July 2019 http://www-sop.inria.fr/reves/Basilic/2019/PGZED19/\n\n\n */\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/2_Projects.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page projects Projects\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/2_Projects.dox.in",
    "content": "/*!\n@page projects Projects\n\n@section sibr_projects_about What are projects ?\n\nResearch algorithms and toolboxes have been implemented for SIBR as plugins named \"Projects\".\\n\nUnstructured Lumigraph Rendering (ULR) application is provided by default with SIBR to help users get started.\\n\nSome projects called toolboxes might also be used by other projects to bring additional functionalities (for instance the SIBR/Optix integration).\\n\n\n@subsection sibr_projects_available Available Projects \n\nThe list of projects that have been added to this generated version of SIBR's documentation is given below.\\n\nFor projects corresponding to publications, the link to the paper is provided.\\n\nMost of other projects are helper libraries that can be used to augment SIBR with new functionalities.\n\n- @subpage sibr_projects_samples\n@SIBR_PROJECTS_SAMPLES_REF_REF@\n- @subpage sibr_projects_ours\n@SIBR_PROJECTS_OURS_REF_REF@\n- @subpage sibr_projects_others\n@SIBR_PROJECTS_OTHERS_REF_REF@\n- @subpage sibr_projects_toolbox\n@SIBR_PROJECTS_TOOLBOX_REF_REF@\n\n@subsection sibr_projects_documentation Access projects documentation\n\nEach project documentation is compilable through SIBR.\\n\nTo access the projects documentation, first :\\n\n- Add the project to SIBR like explained in @ref sibr_projects_add\n- Compile the documentation \n\n@subsection sibr_projects_add Adding projects to SIBR\n\nExisting projects can be added as subdirectories in the src/projects directory.\\n\nFor this, you need to access to the project's source code (most of them are in https://gitlab.inria.fr/sibr/projects) to clone it, build it with SIBR and use it.\\n\nIf you want to create your own project, see @ref howto_setup_project .\\n\nYou can follow the given steps to add a project once access is given.\\n\n- You will need to checkout SIBR Core source code (see @ref sibr_checkout).\n- Go to src/projects\n- Clone/Copy the project source code in the correct project folder (it should be the same as the project repository, or check the README for more information).\n- You can then resume with @ref sibr_generate_documentation or @ref sibr_compile\n\n@subsection sibr_project_structure Project structure\n\nSee @ref project_structure\n\n@subsection sibr_your_project Creating your own project\n\nSee @ref howto_setup_project\n\n*/\n\n/*!\n@page sibr_projects_samples Sample algorithms & toolboxes\n\n@SIBR_PROJECTS_SAMPLES_SUBPAGE_REF@\n*/\n\n/*!\n@page sibr_projects_ours Our algorithms\n\n@SIBR_PROJECTS_OURS_SUBPAGE_REF@\n*/\n\n/*!\n@page sibr_projects_others Other algorithms\n\n@SIBR_PROJECTS_OTHERS_SUBPAGE_REF@\n*/\n\n/*!\n@page sibr_projects_toolbox Integrated toolboxes\n\n@SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF@\n*/\n\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/3_Tutorials.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page tutorials Dataset Documentation & Tutorials\n\n- @subpage howto_generate_dataset\n- @subpage howto_sibr_useful_objects\n- @subpage howto_setup_project\n- @subpage howto_cameras_paths\n\n */\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/4_Architecture.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page architecture Architecture\n\nThis is a diagram describing the overall SIBR architecture:\n@image html sibr_new_architecture.png \"Architecture Diagram\" width=700px\n\nSIBR is built using layers.\n\n@section Core\n\nSIBR core module exposes internal libraries (system, graphics, assets, scene, raycaster, imgproc, view, renderer, video) which can be used to implement multiple IBR algorithms.\n\n@subsection system\nAt the very low level, we have **core/system** that contains OS tools (e.g. filesystems), mathematical tools (e.g. vector operations), and standard tools (e.g. string operations). It also contains a configuration file (Config.hpp) that defines many useful macros/const.\n\nIn short:\n- we use STL and C++11 (std::shared_ptr are heavily used)\n- we use Boost Libraries to manage filesystems.\n- we use Eigen for math tools. (Dev tips: Please use sibr::Vector<NumComp, Type> (e.g. sibr::Vector3f), because they define important flags.)\n\n@subsection graphics\nNext we expose **core/graphics** which contains graphics tools, such as images, meshes, textures, rendertargets, shaders,... We use OpenCV for managing images and image operations. Note that we wrapped OpenCV's cv::Mat in sibr::Image to control/check types statically. See sibr::Image class for details.\n\n@subsection assets\nThe classes contained in **core/assets** represent basic resource files present in IBR datasets. These classes are useful for loading and reading different types of files found in a typical dataset.\n\n@subsection scene\n**core/scene** contains a full IBR dataset representation and storage, based on multiple components that form a \"scene\". A good example is sibr::BasicIBRScene, containing a default set of assets (cameras, images, proxies etc.) which can be initialized by means of a scene metadata file.\n\n@subsection raycaster\nThe **core/raycaster** library provides raycasting and intersection test utilities, wrapping Intel Embree for fast ray/triangle tests.\n\n@subsection imgproc\nBasic image processing utilities cam be found in **core/imgproc**. For more complex tasks, you can use OpenCV algorithms as our Images are backed by OpenCV matrices.\n\n@subsection video\nYou can load and save videos using the **core/video** module. It internally relies on ffmpeg.\n\n@subsection view\nThe **core/view** library exposes tools for making viewer apps for live rendering and debugging of the algorithms. It define a basic view interface along with interactive camera modes, a multi-window management system,...\n\n@subsection renderer\nThe classes in **core/renderer** library implements general rendering passes and functionalities required for many IBR applications. usually, when designing a View for a custom rendering algorithm, you will use multiple renderers, some customs and some out-of-the-box.\n\n */\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How to generate your dataset/How_to_create_dataset_from_colmap.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page HowToColmap How to create a dataset from Colmap\n\nWe provide a python script that runs the entire Colmap pipeline, see here: @ref sibr_projects_dataset_tools_preprocess_tools_fullColmapProcess\n\n@note The previous link might not be available if you did not build the doc with `BUILD_IBR_DATASET_TOOLS` on\n\nHowever, you can also run your own Colmap reconstruction by yourself, and use `ColmapToSIBR` to create an SIBR project (i.e., images without borders, of the same size) from colmap data. In the install\\scripts directory run:\n\n@code\npython colmap2sibr PATH_TO_DATASET\n@endcode\n\nThis will create a *sibr_cm* subdirectory containing the modified scene.\n\n\\section HowToColmap_example_datasets Example Datasets\n\nExample datasets processed with *fullColmapProcess* and *colmap2Sibr* are here:\n\\n\n\thttps://repo-sam.inria.fr/fungraph/sibr-datasets/\n\\n\n\n\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How to generate your dataset/How_to_create_dataset_from_realitycapture.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page HowToCapreal How to create a dataset from Reality Capture\n\n\\tableofcontents\n\n@section capreal_usage How to use\n\nWe use Reality Capture to generate a reconstruction of a mesh from several images gererated with multiple point of view. The program can open a group of images and determinate the position of the initial cameras.\nPlease be aware that RealityCapture does not let you export the results without a license (which you might need to pay for).\nHere is a detailed explanation of all the steps:\n\n@subsection capreal_usage_layout Choose your layout\n\nBefore anything, you'll have to choose your layout.\nYou can modify them with the icons on the top of the screen.\nThe interface can be a bit wonky, especially the 3D view. If you want to avoid possible display issues, you can choose `1 + 1 + 1 Layout`.\n\n@image html caprealnew.png Layout\n\n@subsection capreal_usage_input_images Select input images\n\nFor reconstruction, you will need to provide a set of images of your scene. You can do so by clicking on `Inputs` or `Folder` buttons in the `Workflow` tab.\n\n@image html caprealaddinputs.png Add input images \n\n@image html caprealinputsonly.png Now the input images are set \n\n@subsection capreal_usage_align_images Align images\n\nWhen the image set is properly provided, you can align them with the `Align Images` option, in the `Alignment` tab.\n\n@image html caprealalignimages.png Align images option\n\nNow, you can see a point cloud in the 3D view.\n\n@image html caprealpointcloud.png The point cloud you get when you align images\n\n@subsection capreal_usage_mesh_reconstruction Mesh reconstruction\n\nNow you'd want to have a mesh from the images. For this, you need to select one of the `Calculate Model` options in the `Reconstruction` tab. We'll go for the `Normal Detail` option.\n\n@image html caprealmeshreconstruction.png The mesh reconstruction options\n\nNow you should be able to see the reconstruction.\n\nYou can click on `Colorize` to colorize the mesh (or `Texture` if you want to texturize it : colorization only apprixomate vertices color, while texturing gives you an approximated texture).\n\nThe generated mesh is likely to be pretty complex in terms of triangle count.\nYou can decreased the number of triangles by doing : `Tools > Simplify Tools`\nYou can take 1 or 2  For teh render optionmillion of triangle for the simplification\n\nYou now have calibrated cameras and a reconstructed 3D mesh that are ready for use by **SIBR**. The next two sections explain whow to create a set of directories that will be useful for dataset managements, then save the data required from RealityCapture.\n\n@image html caprealcolorizesimplify.png Colorize and Simplify Tools options \n\n@section capreal_DirStruc Suggested directory structure\n(Note: This directory structure is only suggested for user accessibility. You can store/create the dataset in separate directories as you like, as long as you provide correct input to the scripts to generate SIBR datasets.)\n\n@li dataset\\\\raw\n\\n Contains the original images from the cameras.\n\\n\n@li dataset\\\\rcprojs\n\\n Contains the .rcproj files and the data directories (these are big after reconstruction, since they contain the mesh and texture).\n\\n\n@li dataset\\\\sfm_mvs_rc\n\\n Contains the exported undistorted images with black borders, the file  bundle.out, pmvs_recon.ply and optionally textured.obj, textured.mtl and textured_u1_v1.png (see what to save below)\n\\n\n@li dataset\\\\sibr_rc\n\\n Contains the extracted data to create scene(s) using SIBR, containing bundle file, reconstructed mesh, list of images, scene metadata etc. in proper directory structure.\n\\n\n\n@section capreal_WhatToSave What to save from RealityCapture\n\nIn the selected directory (*sfm_mvs_rc*) save the following:\n\n@li After alignment and reconstruction, save Registration (choose optionsbundler v0.3 Negative-Z format, jpg image type,  fit=Inner_region), and save to file *bundle.out*\n@li After reconstruction -> Mesh -> save to the file *recon.ply*\n@li After texturing -> Mesh -> save textured.obj (which will save textured.mtl and textured_u1_v1.png containing the texture); \n@todo Textures verify\n\n@subsection Restore\n\n@todo Does this exist ?\n@li If something goes wrong, use python restore_dataset.py to restore all original files (bundle, ply, obj) as exported from RealityCapture\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How to setup your own project/Configuring_your_project.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page configure_project Configuring your project\n\n@ingroup setup_project\n\n@section gen_setup_config General setup\n\n- Create a repository in `src/projects/my_project` (name your project at your convenience)\n- Setup your project structure as stated in @ref project_structure\n- Use the following sample files (form the following sections) for your `CMakeLists.txt` files and `Config.hpp` structure\n- Re-run configure for the main SIBR `CMakeLists.txt`.\n- The project should be automatically detected; If so, check `BUILD_IBR_MY_PROJECT` (`MY_PROJECT` being your project name folder) in CMake and re-generate.\n\n@section all_sec Main project configuration\n\nThis `CMakeLists.txt` is the one in the root of your project. It is registering the subdirectories of your project against the main CMake.\\n\nIt will also provide you with an additional project wide install target.\\n\n\n@code\nset(SIBR_PROJECT \"my_project\") # Please replace my_project with your project folder name\nproject(sibr_${SIBR_PROJECT}_all)\n\n# Update this with the folders included in your project\nadd_subdirectory(apps)\nadd_subdirectory(preprocess)\nadd_subdirectory(renderer)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/${SIBR_PROJECT}\")\n@endcode\n\n@section listing_sec Listing app & preprocesses projects\n\nThis `CMakeLists.txt` is registering the CMake projects in subdirectories against the main CMake of your SIBR project, putting them together in a custom named group.\\n\nThey will appear as multiple solutions in a subdirectory in Visual Studio for instance.\\n\nUseful to group the `apps/` or `preprocess/` executables of a project.\\n\n\\n\nYou can use it as sample structure for `apps/CMakeLists.txt` and `preprocess/CMakeLists.txt`.\n\n@code\nproject(sibr_my_apps_group)\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# Please rename this project at your convenience\nadd_subdirectory(my_app_1/)\nadd_subdirectory(my_app_2/)\n#...\n@endcode\n\n\n@section exe_sec App and preprocess projects\n\nThis example can be used for application and preprocess executables.\\n\nThe parts to modify are the project name, the linked libraries and the folder property.\\n\nPut the `CMakeLists.txt` in your application project directory.\n\n@code\nset(SIBR_PROJECT \"my_project\") # Please replace my_project with your project folder name\nproject(sibr_${SIBR_PROJECT}_app)\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# Please rename this project at your convenience\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} ${SOURCES})\n\n# Define dependencies\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n  \t${OpenCV_LIBRARIES}\n\tsibr_system\n\n\t# you can add your internal or external dependencies here (sibr_renderer, sibr_view, sibr_graphics, sibr_assets,...)\n)\n\n# Define location in solution.\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n@endcode\n\n@section scripts_sec Scripts projects\n\nYou can also add scripts projects. Scripts are bundled in the install/scripts folder, which ensures you have access to utility functions and SIBR binaries.\\n\n\n@code\nset(SIBR_PROJECT \"my_project\") # Please replace my_project with your project folder name\nproject(sibr_${SIBR_PROJECT}_scripts)\t\n\nfile(GLOB_RECURSE SCRIPTS \"*.py\") #add any scripts files / wildcards here\n\nadd_custom_target(${PROJECT_NAME} ALL)\n\ninclude(install_runtime)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/preprocess\")\nibr_install_rsc(${PROJECT_NAME} TYPE \"scripts\" FILES ${SCRIPTS} RELATIVE) # you can use FOLDER option if you want the scripts to be stored in a specific folder\n@endcode\n\n@section lib_sec Library project\n\nThis example can be used for libraries, in `renderer\\CMakeLists.txt`.\\n\nThe parts to modify are the project name, the linked libraries, the export/import flag for Windows libraries, and the folder property. \\n\nPut the `CMakeLists.txt` in your library project directory.\\n\nThis example also supports displaying shaders and copying them to the bin/resources common directory.\n\n@code\nset(SIBR_PROJECT \"my_project\")\nproject(sibr_${SIBR_PROJECT})\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# Please rename this project at your convenience\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\" \"shaders/*.fp\" \"shaders/*.vp\" \"shaders/*.gp\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\n# Redefine sources and all the files to display in the IDE.\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\"  \"shaders/*.fp\" \"shaders/*.vp\" \"shaders/*.gp\")\n\n# Declare library.\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\n# Define dependencies.\ninclude_directories(${Boost_INCLUDE_DIRS} .)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\n\t# you can add your internal or external dependencies here (sibr_renderer, sibr_view, sibr_graphics, sibr_assets,...)\n)\n\n# Define export/import flag.\nadd_definitions( -DSIBR_MY_LIBRARY_EXPORTS -DBOOST_ALL_DYN_LINK  )\t\t\t\t\t\t\t\t\t\t\t# Please refactor it with your library export/import flag from Config.hpp\n\n# Define location in solution.\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/renderer\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS \"${SHADERS}\"\t\t\t\t## You can also add scripts and resources with the corresponding keyword (SCRIPTS, RESOURCES)\n\tRSC_FOLDER \"${SIBR_PROJECT}\"\t\t## Resources will be stored in this subfolder in their respective resource folder (scripts, shaders, resources)\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n@endcode\n\nTo handle export/import of library methods properly on Windows, you also need a `Config.hpp` file in your library directory.\n\n@code\n#ifndef __SIBR_MY_LIBRARY_CONFIG_HPP__\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n# define __SIBR_MY_LIBRARY_CONFIG_HPP__\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n\n# include <core/system/Config.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_MY_LIBRARY_EXPORT\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n#      ifdef SIBR_MY_LIBRARY_EXPORTS\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n/* We are building this library */\n#        define SIBR_MY_LIBRARY_EXPORT __declspec(dllexport)\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n#      else\n/* We are using this library */\n#        define SIBR_MY_LIBRARY_EXPORT __declspec(dllimport)\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_MY_LIBRARY_EXPORT\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n# endif\n\n#endif  //__SIBR_MY_LIBRARY_CONFIG_HPP__\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Please refactor it with your library name\n\n@endcode\n */\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How to setup your own project/Creating_your_library.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page create_library Creating your library\n\n@ingroup setup_project\n\n@section ge_setup_library General setup\n\nThe content of your library would basically be up to what you will need in this specific project.\\n\nHowever, we do implement core patterns that could be useful to create your view and handle the rendering of your scene.\n\n@section library_views Views\n\nbasic example of a View:\n\nsee:\n- https://gitlab.inria.fr/mbenadel/sibr_simple/-/blob/master/renderer/SimpleView.hpp\n- https://gitlab.inria.fr/mbenadel/sibr_simple/-/blob/master/renderer/SimpleView.cpp\n\n@section library_renderers Renderers\n\nbasic example of a Renderer:\n\nsee:\n- https://gitlab.inria.fr/mbenadel/sibr_simple/-/blob/master/renderer/SimpleRenderer.hpp\n- https://gitlab.inria.fr/mbenadel/sibr_simple/-/blob/master/renderer/SimpleRenderer.cpp\n\n@section library_shaders Shaders\n\nShaders copy to binary folder is handled by the library CMake.\\n\nYou can put your shaders files in `renderer/shaders` with the following extensions:\n\n- `.vert`\n- `.frag`\n- `.geom`\n- `.vp`\n- `.fp`\n- `.gp`\n\nIf unchanged, those shaders should be copied to `install/bin/shaders_rsc`.\\n\nTo extend this behavior, please update `renderer/CMakeLists.txt`.\n\n */\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How to setup your own project/Documenting_a_project.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n /*!\n@page document_project Documenting projects\n\n@ingroup setup_project\n\n@section gendoc Writing\nEach class created in one of the core SIBR modules (system, assets,...) should be properly documented.\nPlease try to document as many methods as possible (especially public ones). Don't forget to <b>add each class to its module</b>.\nYou will find below an example of a commented class.\n\n@verbatim\n /* Represents a general view.\n    @ingroup sibr_view\n */\n class SIBR_MY_LIBRARY_EXPORT MyView {\n \tpublic:\n\n \t\t/* Loads everything.\n \t\t\t@param flags the options to use.\n \t\t\t@return a boolean denoting the success of the operation.\n \t\t*/\n \t\tbool load(const int flags);\n\n \t\tMyView();\n \t\t~MyView();\n\n \tprivate:\n\n \t\t/* Performs complex operations.\n \t\t\t@param val the value to use.\n \t\t*/\n \t\tvoid performComplexOps(const float val);\n\n \t\tint _flags;\t///< configuration flags\n \t\tsibr::Vector2i _size; ///< The size of the view.\n }\n@endverbatim\n\n\nIf you need to create a new module and want it to appear in the Modules doxygen listing, you will need to create a `sibr_mymodule.dox` file in your module directory, with the following content:\n\n@verbatim\n/*!\n\t@defgroup sibr_mymodule\n\n\t@brief This is my module.\n\n\tThis is a longer description of my module. It's mine.\n*/\n@endverbatim\n\nYou can also write general .dox pages to give more details on a process or a project.\\n\nPlease add them to your project `documentation/` folder.\\n\nHere is an example of a dox file content:\\n\n\n@verbatim\n/*!\n@page yourPageReference Your Page Name\n\nThis is a Page.\\n\n\nYou can add ref to pages like this : @ref anotherPage\\n\nOr add a link to a subpage like this : @subpage yetAnotherPage\\n\n*/\n@endverbatim\n\nYou can automatically link them as subpages in <a href=\"projects.html\">docs/pages/Projects.dox</a> by providing a `<my_project>_doc.cmake` file in your project `documentation/` folder.\\n\nHere you can see an example:\\n\n\n@verbatim\n/*!\nset(PROJECT_PAGE \"yourPageReference\")\nset(PROJECT_LINK \"https://the.link.to.your.source.code.for.instance\")\nset(PROJECT_DESCRIPTION \"A short description\")\nset(PROJECT_TYPE \"OTHER\") # this could be either SAMPLES, TOOLBOX, OURS or OTHERS. If not affiliated to SIBR, you might want to use OTHERS or TOOLBOX\n*/\n@endverbatim\n\n@section compileDoc Generating\n\nTo generate the documentation, enable the BUILD_DOCUMENTATION flag in cmake and build the DOCUMENTATION target in Visual Studio. The generated output html pages will be accessible from <a href=\"../index.html\">intall/docs/index.html</a>.\n\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How to setup your own project/Project_structure.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page project_structure Project Structure\n\n@ingroup setup_project\n\n@section gen_struct General structure\n\n- Projects should follow the following hierarchy:\n\n@verbatim\t\n\tapps/\n\t\tCMakeLists.txt\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# for listing apps to compile\n\t\tmy_app_1/\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# one of your apps, named at your convenience\n\t\t\tCMakeLists.txt\n\t\t\tmain.cpp\n\t\tmy_app_2/\n\t\t\tCMakeLists.txt\n\t\t\tmain.cpp\n\tpreprocess/\n\t\tCMakeLists.txt\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# for listing preprocesses to compile\n\t\tmy_preprocess_1/\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# one of your preprocesses, named at your convenience\n\t\t\tCMakeLists.txt\n\t\t\tmain.cpp\n\t\tmy_preprocess_2/\n\t\t\tCMakeLists.txt\n\t\t\tmain.cpp\n\trenderer/\n\t\tCMakeLists.txt\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# for compiling your library\n\t\tmy_library_code.cpp\n\tdocumentation/\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# you can add optional documentation pages in this folder\n\t\tmy_doc.dox\n\tCMakeLists.txt\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t# project-wide configuration\n@endverbatim\n\n- `renderer/`: contains your library code and configuration\n- `preprocess/`: contains your preprocesses listed by directory, and the configuration CMake file to list them\n- `apps/`: contains your apps listed by directory, and the configuration CMake file to list them\n- `documentation/`: contains additional doxygen documentation\n\n*/\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How_to_generate_your_dataset.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page howto_generate_dataset Dataset Structure and Generation\n\nMost *Projects* take as input a *multi-view dataset*, i.e., a set of images taken using a camera (phone, DSLR, videocamera such as GoPro etc). In almost all cases we assume that a Structure-from-Motion (SfM) algorithm has been run on the input images to generate *calibrated cameras*, and most often a second Multi-View Stereo (MVS) step has been run, to generate a reconstructed 3D mesh. Optionally, we may also use dense depth maps that come from MVS.\n\nIn **SIBR** we have a ``native'' dataset format, described below, which was used traditionally for some of the original *Projects*. For most recent projects we have used the commercial tool RealityCapture for SfM/MVS, since it tends to produce the best overall reconstruction quality. In some recent projects (e.g., DeepBlending, or SemanticCars, see @ref projects), we have used *Colmap*, since the dense per-view depth maps are very useful.\n\nAs a result, **SIBR** supports two types of dataset natively:\n\n @li Native **SIBR** datasets, created using the tools described below, from RealityCapture or Colmap.\nWe provide pre-processing documentation following which you can create SIBR compatible datasets from output of these two SfM/MVS systems:\n  - @subpage HowToCapreal\n  - @subpage HowToColmap\n\n @li Native Colmap datasets: we simply create a metadata file the first time the dataset is read.\n\nDifferent *Projects* often add additional information to their datasets, typically via multiple pre-processing utilities, some general, some specific to the *Project*.\nPython scripts designed to process and prepare datasets are provided with each project along with instructions on how to use them in the corresponding documentation.\n\n@section howto_generate_dataset_sibr_format Basic SIBR Dataset Structure and Generation\n\nThere are two main ways to generate a dataset for **SIBR**. The first is to use *colmap* (see @ref HowToColmap) to to SfM/MVS, and the second is using the commercial RealityCapture package (see @ref HowToCapreal).\n\nAfter running <code>colmap</code> **SIBR** can read the dataset natively. In this case the dataset will just contain the <code>colmap</code> directory:\n```\n\\dataset\\\n\\dataset\\colmap\\\n\\dataset\\colmap\\sparse\n\\dataset\\colmap\\dataset.db\n\\dataset\\colmap\\stereo\n\\dataset\\colmap\\stereo\\images\n\\dataset\\colmap\\stereo\\sparse\n\n```\nSIBR expects camera calibration to be in <code>colmap\\stereo\\sparse</code>, and the MVS mesh in <code>colmap\\stereo\\meshed-delaunay.ply</code>.\nNote that in this case, the images *do not have the same size*. Some IBR algorithms (e.g., ULR) can handle this, but others require that the input images all have the same size; this was historically the case for [Chaurasia 13], and **SIBR** handles this natively.\n\nSpecifically, we run the <code>colmap2sibr</code> script to generate an **SIBR** dataset.\nThe basic structure of this **SIBR** dataset is shown below, generated from colmap with texture:\n\\n\n\n```\n\\dataset\\\n\\dataset\\colmap\\\n\\dataset\\sibr_cm\\scene_metadata.txt\n\\dataset\\sibr_cm\\cameras\n\\dataset\\sibr_cm\\meshes\n\\dataset\\sibr_cm\\images\n\\dataset\\sibr_cm\\cameras\\bundle.out\n\\dataset\\sibr_cm\\cameras\\list_images.txt\n\\dataset\\sibr_cm\\images\\{img00000000.jpg,...,img000000NN.jpg}\n\\dataset\\sibr_cm\\meshes\\recon.{ply,obj}\n\\dataset\\capreal\\\n\\dataset\\capreal\\mesh.ply\n\\dataset\\capreal\\texture.png\n```\n\n\\n\n\nThe native **SIBR** directory structure in <code>sibr_cm</code> is as follows. \nThe <code>colmap</code> directory contains the colmap reconstruction, with the calibration, depthmap and mesh data.\n\nThe <code>capreal</code> directory contains the textured mesh generated by the conversion pipeline.\n\nIn the <code>sibr_cm</code> subdirectory:\n\n@li The *cameras* directory contains the calibrated cameras, using the *Bundler* format by default (\\ref subsecDataSetFormatsBundle).\n\n@li The *images* directory that contains the undistorted images from the reconstruction and the list images file (\\ref subsecDataSetFormatsListImg)\n\n@li The *meshes* directory that contains a *recon.ply* file that is the 3D reconstruction of the scene \n\\n\n\n\nTo generate a dataset after following the procedure for RealityCapture (@ref HowToCapreal), you need to perform the following steps:\n\n@li Generate and build the solution to generate executables for preprocessing applications (unless you already have the binary distribution).\n@li Go to `install\\scripts`\n@li Run the python script using \n```\npython ibr_preprocess_rc_to_sibr.py -i original_dataset_path -o original_dataset_path\\sibr_rc --bin path_to_sibr_bin \n```\n@li Specifying the binaries directory is optional. While compiling cmake automatically generates settings file which is parsed by the script to set bin directory.\n@li The script calls the distordCrop, cropFromCenter, and clipping_planes app executables; make sure they are up to date.\n    - They crop the images to remove the black borders; the --ratio parameter to distordCrop (currently 0.2 (/.5)) sets the percentage of border which can be removed. Images that have larger black borders are removed from the dataset.\n    - They also copy the meshes (ply and obj if available) and modifies the bundle.out and list_images.txt to the new values of resolution, and removes the images excluded (the numbers can be found in Dataset\\\\SibrData\\\\raw\\\\excluded_images.txt)\n    - Next they compute the clipping planes for each camera corresponding to input images and stores them in a clipping_planes.txt file.\n    - Finally, they parse all data to create a scene_metadata.txt file which holds information of the images, clipping planes etc. and is used to create the scene. This file can be manually extended.\n@li *[Recommended]* If you do not want to create a copy of the dataset, you can only specify the input directory with -i option. The dataset will be generated within the input directory itself.\n\\n\n\nThe **SIBR** dataset will have the following structure, similar to the case above, but with <code>sibr_rc</code> instead of <code>sibr_cm</code>.\n\n```\n\\dataset\\\n\\dataset\\sibr_rc\\scene_metadata.txt\n\\dataset\\sibr_rc\\cameras\n\\dataset\\sibr_rc\\meshes\n\\dataset\\sibr_rc\\images\n\\dataset\\sibr_rc\\cameras\\bundle.out\n\\dataset\\sibr_rc\\cameras\\list_images.txt\n\\dataset\\sibr_rc\\images\\{img000.jpg,...,img0NN.jpg}\n\\dataset\\sibr_rc\\meshes\\recon.ply\n\\dataset\\sibr_rc\\meshes\\recon.ply\n\\dataset\\capreal\\\n\\dataset\\capreal\\textured.obj\n\\dataset\\capreal\\textured.mtl\n\\dataset\\capreal\\textured_u1_v1.png\n```\n\\n\n \\subsection subsecInputDatasetFilesFormats Dataset Files and Formats\n\\n\n The following sections contain documentation of the various files used in the dataset. Some are inherited from other SfM/MVS solutions.\n\\subsection subsecMetadataFile The scene_metadata.txt file\n\nContains the list of images and their resolution as well as the near and far planes for each images.\n\\n\n \\subsection subsecDataSetFormatsBundle The bundle.out file\n\\n\n  Content: [the bundler documentation](http://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html) explain all what it contain \\n\n\t Description: [the output of the bundler](http://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html#S6) \\n\n Bundle file format is in plain text :\\n\n ~~~~~~~~~~~~~{.txt}\n # Bundle file v0.3\n <num_cameras> <num_points>   [two integers]\n <camera1>\n <camera2>\n   ...\n <cameraN>\n <point1>\n <point2>\n    ...\n <pointM>\n ~~~~~~~~~~~~~\n Where \\<camera\\> contain :\n ~~~~~~~~~~~~~{.txt}\n <f> <k1> <k2>   [the focal length, followed by two radial distortion coeffs]\n <R>             [a 3x3 matrix representing the camera rotation]\n <t>             [a 3-vector describing the camera translation]\n ~~~~~~~~~~~~~\n And where \\<point\\> contain :\n ~~~~~~~~~~~~~{.txt}\n <position>      [a 3-vector describing the 3D position of the point]\n <color>         [a 3-vector describing the RGB color of the point]\n <view list>     [a list of views the point is visible in]\n ~~~~~~~~~~~~~\n\n\n \\subsection subsecDataSetFormatsListImg The list_images.txt file\n  Content: It a list of all input images sorted by order it was taken (renamed) with their resolution \\n\n  Description: If you have [ImageMagick](http://www.imagemagick.org/script/command-line-options.php#format_identify_) you can do : `identify -format \"%f %w %h\\n\" *.jpg` \\n\n ~~~~~~~~~~~~~{.txt}\n <%8d.jpg> <width> <height>\n ~~~~~~~~~~~~~\n Example:\n ~~~~~~~~~~~~~{.txt}\n 00000000.jpg 2256 1504\n 00000001.jpg 2256 1504\n 00000002.jpg 2256 1504\n ...\n 00000026.jpg 2256 1504\n 00000027.jpg 2256 1504\n ~~~~~~~~~~~~~\n */\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/How_to_setup_your_own_project.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page howto_setup_project How to setup your own project\n\n- @subpage project_structure\n- @subpage configure_project\n- @subpage create_library\n- @subpage document_project\n\n */"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/Paths_and_cameras.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n/*!\n@page howto_cameras_paths  Cameras, Paths and Dataset Alignment\n\n@section cameras_section Cameras\n\nCalibrated cameras come from the SfM method used. We support bundler <code>.out</code> (http://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html), Blender <code>.lookat</code>, Colmap <code>cameras.txt/images.txt</code> and to a certain extent VisualSFM <code>.nvm</code> files.\nEach format has different constraints, and they are not always compatible. Internally, we convert to an <code>sibr::InputCamera</code> data structure.\n\n\n@section paths_section Paths\n\nWe can read paths, i.e., a sequence of cameras, in any of the above formats in the interactive viewers for most renderers, typically in the main \"View\" panel, and the \"Load path\" button. The path can be played either by interpolated between the views (\"Play\" button), or just playing the exact cameras (\"Play (No Interp)\"). We also have an internal <code>.path</code> binary path format.\nYou can define key cameras in the same menu using the \"Add key\" button.\n\nPaths can be played by most renderers by running the renderer in offscreen mode:\n```\nSIBR_renderer_app.exe --offscreen --pathFile path.(out|lookat|tst|path) [--outPath optionalOutputPath --noExit]\n```\n\nBy default, the application exits when this operation is performed. Rendering the same path is the easiest way to compare different algorithms, and works for most of the *Projects* provided.\n\n\n@section align_section Aligning datasets and transforming paths\n\nWe provide the <code>alignMeshes</code> tools to align two different reconstructions of the same multi-view dataset, see also the dataset tools page (@ref sibr_projects_dataset_tools).\nIf you need to align your dataset <code>dataset2Align</code> (e.g., a colmap reconstruction) to a reference dataset <code>refDataset</code> (e.g., a RealityCapture reconstruction) you can use the alignMeshes command (in <code>install\\bin</code>).\n*Important note: both datasets must have the same (or a subset of the same) images, calibrated cameras and a fully reconstructed mesh*\n```\nalignMeshes_rwdi.exe --path2Align dataset2Align --pathRef refDataset --out outputPath\nalignMeshes.exe --path2Align dataset2Align --pathRef refDataset --out outputPath\n```\nThis will align the two datasets, and write the aligned mesh in <code>outputPath</code> as well as the file <code>transform.txt</code> that contains the transformation matrix of the <code>dataset2Align</code> to <code>refDataset</code>.\n\nPlease note that alignMeshes may not manage to completely align meshes: please lalways compare the aligned mesh with the target (e.g., using meshlab). If alignmeshes fails, an alternative is to use a manual tool such as CloudCompare or meshlab.\n\nYou can then transform a camera path defined in the first dataset to the a path in the reference dataset:\n\n```\ncameraConverter_rwdi.exe --input inputPath.(out|lookat|path|tst) --output outputPath.(out|lookat|path|tst)\ncameraConverter.exe --input inputPath.(out|lookat|path|tst) --output outputPath.(out|lookat|path|tst)\n```\n\n */\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/docs/pages/Tutorials/Useful_sibr_elements.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page howto_sibr_useful_objects Useful systems in SIBR\n\n@section multiviewmanager Using the windowing system\n\nSIBR provides tools to display multiple rendering algorithms in different subviews that can be resized and hidden on screen. This is handled by the `MultiViewManager`. once instantiated and tied to a system window, the multi view manager can keep track of multiple views, ensuring that their content is updated and that they receive the correct user inputs. Views can be hidden, resized, their content captured as a screenshot.\n\nTwo modes can be used, mainly for legacy reasons:\n\n- IBR subviews, that have to implement onRenderIBR() (which was used to support anaglyph rendering previously)\n- basic subviews, that have to implement onRender()\n\nIt is possible to associate an interactive user camera to a view, so that the user can move around in the displayed content. Two views can use the same handler, for synchronized motion.\n\nBasic example:\n\n@code\nWindow window(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs);\nMultiViewManager manager(window, false);\n\n// Register a first view, with an interactive user camera\nMyView::Ptr\tmyView(new MyView());\nInteractiveCameraHandler::Ptr userCam(new InteractiveCameraHandler());\nmanager.addIBRSubView(\"My view\", myView);\nmanager.addCameraForView(\"My view\", userCam);\n\n// Register another view, which already handles the user motions by itself\nDebugView::Ptr dbgView(new DebugView());\nmanager.addSubView(\"Debug view\", dbgView, usedResolution);\n\nwhile (window.isOpened()) {\n\n\tInput::poll();\n\twindow.makeContextCurrent();\n\tif (Input::global().key().isPressed(Key::Escape)) {\n\t\twindow.close();\n\t}\n\n\tmanager.onUpdate(Input::global());\n\tmanager.onRender(window);\n\n\twindow.swapBuffer();\n}\n@endcode\n\n@image html multiviewmanager.png\n\n@section interactiveHandler Interactive user camera\n\nUser interaction in a view are handled by an `InteractiveCameraHandler`, that supports many interaction modes such as a FPS camera or a trackball. \nIt can also snaps to predefined viewpoints or interpolate between them. You can adjust the field of view and frustum planes.    \nYou can toggle between modes in the GUI or by pressing Y.\n\n- FPS : move around with WASD, down/up with Q/E, rotate with IJKL and roll with U/O.\n- Trackball : rotate around a central point with the left click in the center of the view, roll with left click towards the view edges. Pan with righ click in the center region, move forward/backward with right click towards the view edges, or by scrolling. Ctrl+left click redefines the center of the trackball, this is very useful to focus on a specific region of the scene and zoom in. \n- Orbit: rotate around using the numeric keypad.\n\n@subsection recordPaths Recording and replaying camera paths\n\nThe handler can also records and playback paths. Once the `Record` button is pressed, all user camera motions are recorded. When pressing `Save path`, they will be saved to a file on disk. Types such as `.lookat`, `.path` and `.bundle` are supported, which is useful for comparison with other frameworks. A `.path` can be reloaded using the `Load path` button, and will start playing automatically.\n\n@subsection recordVideos Generating videos\n\nIt is possible to record videos and dump images following a path in a view. In the camera handler GUI, you can check if you want to record videos or frames. If checked, the next start a path starts playing, each frame will be recorded and saved on disk.\n\n- for video recording, check the corresponding box, start playing the path. At the end, in the view manager, select \"Capture > Export video\" and select the output destination (supported export format: h264 with `.mp4` extension).\n- for frames recording, hen checking the box you will have to select an output directory. once the path starts to play, frames are going to be saved in the directory.\n\n@section debugview Visualizing debug geometry\n\nIt can be useful to visualize information such as the scene geometry, location of the cameras, rays, voxel-like structures in a view with an interactive camera. SIBR provides the `MultiMeshManager` for this prupose. It can be used to display multiple meshes, vertices, lines,... At runtime, a list of all elements is displayed and many attributes can be toggled. The same attributes can be edited from the code using chaining. Everything can be updated on the fly based on the objects names. \nFor instance:\n\n@code\ndebugView.addMeshAsLines(\"BBox\", bbox).setColor({1.0f, 0.0f, 1.0f}).setDepthTest(false).setColormode(USER_DEFINED);\n// Later in the code\ndebugView.addMeshAsLines(\"BBox\", anotherBbox); // will replace the previous mesh\n// Later again\ndebugView.getMeshData(\"BBox\").setAlpha(0.5f); // dim the box\n@endcode\n\nBecause the MultiMeshManager conforms to the ViewBase interface, it can be added to the view manager as any other view. It comes with its own interactive camera.\n\n@note The `SceneDebugView` that can be used to visualize an IBR dataset including the geometry, cameras and images, is built on top of `MultiMeshManager`, with extra code generating geometry for the frusta and the image quads.\n\n@image html multimeshmanager.png\n\n@section commandlineargs Command line arguments\n\nTo simplify the definition and parsing of arguments, SIBR provide tools to easily define and populate arguments. The system is based on `Arg<T>` and `RequiredArg<T>`, that can be used to define arguments anywhere. It is recommended to group them in a structure, especially as some existing structures can be reused to define command arguments (windowing options, dataset path, etc., see for instance `WindowAppArgs` or `BasicDatasetArgs`). \n\nBefore anything, you have to make sure the raw input arguments have been parsed by calling `CommandLineArgs::parseMainArgs(argc, argv);` in your main. Any argument instantiated afterwards will then be able to fetch its user-provided value (if it exists). Default values and help messages can be provided when declaring arguments. Required arguments will raise an error when used if the user did not provide a value.\n\nArguments will convert to their contained type when using them, it is also possible to access their value using `get()`. Arguments values can also be set directly in the code.\n\n@code\nstruct MyArgs : virtual WindowAppArgs, BasicDatasetArgs {\n\tRequiredArg<int> iterations = {\"itcount\", 5, \"Number of smoothing iterations\"};\n\tArg<std::string> logPath = {\"log\", \"\", \"Path to log file\"};\n\tArg<bool> showResult = {\"show\", \"Display results in a popup window\"};\n}\n\nint main(int argc, char** argv){\n\tCommandLineArgs::parseMainArgs(argc, argv);\n\tMyArgs args;\n\tconst std::string logFile = args.logPath.get() + \"_test.log\";\n\tThing(args.iterations, logFile);\n\tif(args.showResult){\n\t\t//...\n\t}\n}\n@endcode\n\n\n@section uniformsystem The uniform system\n\nWhen using OpenGL shaders, values are often passed from the CPU using uniforms. In the past, SIBR required the developer to maintain both a GLParameter object and the corresponding variable on the CPU to store the value and expose it. It is now recommended to use GLUniform<T>, that wraps a CPU value while allowing you to update the GPU uniform easily. The GLUniform will automatically converts to its contained type in most cases ; if a reference to the CPU value is needed, you can use get().\n\n@code\n// In the header, as members of a renderer for instance\nGLShader _shader;\nGLuniform<float> _val = 0.5;\n\n// At construction\n_shader.init(\"My Shader\", \"vertex shader content\", \"fragment shader content\");\n_val.init(_shader, \"alpha\"); // link the uniform to the shader, specify the name in the shader code\n\n// At some point in the code\n_val += 0.3f; // Mofidy the CPU value\n\n// In the render loop\n_shader.begin();\n_val.send(); // Send to the GPU\n...\n@endcode\n\n\n */"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n################################################################################\n# This CMakeLists.txt manages which projects should be built and add their     #\n# dependencies.                                                                #\n################################################################################\nset(SIBR_FOLDER \"core\")\nset_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER \"\")\n\noption(BUILD_SIBR \"Build core libs of SIBR (sibr_system, sibr_graphics, sibr_view, sibr_assets, ...)\" ON)\n\n#https://stackoverflow.com/questions/7787823/cmake-how-to-get-the-name-of-all-subdirectories-of-a-directory\nMACRO(SUBDIRLIST result curdir)\n  FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)\n  SET(dirlist \"\")\n  foreach(child ${children})\n    IF(IS_DIRECTORY ${curdir}/${child})\n      LIST(APPEND dirlist ${child})\n    ENDIF()\n  endforeach()\n  SET(${result} ${dirlist})\nENDMACRO()\n\nset(SIBR_PROJECTS_FOLDER           \"${CMAKE_CURRENT_SOURCE_DIR}/projects\")\nSUBDIRLIST(SUBDIRS ${SIBR_PROJECTS_FOLDER})\n\nlist(APPEND PROJECT_SUBFOLDERS \"apps\" \"preprocess\" \"renderer\" \"scripts\" \"library\")\n\n# Moving ulr to the top of the list\nlist(PREPEND SUBDIRS \"dataset_tools\" \"ulr\" \"basic\")\nlist(REMOVE_DUPLICATES SUBDIRS)\n\n## DEPS ##\ninclude(include_once)\n\nmessage(STATUS \"\\n\\n****************** Handling core dependencies ******************\")\n\ninclude_once(dependencies)\t\t## Map/bind 3rdParty/external dependencies packages to cmake\n\nmessage(STATUS \"****************************************************************\\n\\n\")\n\nforeach(subdir ${SUBDIRS})\n  set(${subdir}_ROOT_DIR \"${SIBR_PROJECTS_FOLDER}/${subdir}\")\n  set(PROJECT_NAME \"BUILD_IBR_${subdir}\")\n  string(TOUPPER ${PROJECT_NAME} PROJECT_NAME)\n  if(${${PROJECT_NAME}})\n    foreach(PROJECT_SUBFOLDER ${PROJECT_SUBFOLDERS})\n      if(EXISTS \"${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}/cmake/Modules\")\n        list(APPEND CMAKE_MODULE_PATH ${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}/cmake/Modules)\n      endif()\n\n      if(EXISTS \"${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}/cmake/dependencies.cmake\")\n        message(STATUS \"********* Handling ${subdir} ${PROJECT_SUBFOLDER} dependencies *********\")\n        include_once(\"${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}/cmake/dependencies.cmake\")\n\n        message(STATUS \"****************************************************************\\n\\n\")\n      endif()\n    endforeach()\n  endif()\nendforeach()\n\nWin3rdPartyGlobalCacheAction()\n\ninclude_directories(.)\n\nif (BUILD_SIBR)\n  add_subdirectory(core/system)\n  add_subdirectory(core/graphics)\n  add_subdirectory(core/renderer)\n  add_subdirectory(core/raycaster)\n  add_subdirectory(core/view)\n  add_subdirectory(core/scene)\n  add_subdirectory(core/assets)\n  add_subdirectory(core/imgproc)\n  add_subdirectory(core/video)\nendif()\n\nset(PROJECTS_ON_AT_FIRST_BUILD \"basic\" \"gaussianviewer\" \"remote\")\n\nforeach(subdir ${SUBDIRS})\n  message(STATUS \"Adding ${subdir} project\")\n  set(PROJECT_NAME \"BUILD_IBR_${subdir}\")\n  string(TOUPPER ${PROJECT_NAME} PROJECT_NAME)\n\n  if(NOT (DEFINED ${PROJECT_NAME}))\n    foreach(PROJECT_SUBFOLDER ${PROJECT_SUBFOLDERS})\n      if(EXISTS \"${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}/CMakeLists.txt\")\n        if(subdir IN_LIST PROJECTS_ON_AT_FIRST_BUILD)\n          option(${PROJECT_NAME} \"Build project \\\"${subdir}\\\"\" ON)\n        else()\n          option(${PROJECT_NAME} \"Build project \\\"${subdir}\\\"\" OFF)\n        endif()\n        break()\n      endif()\n    endforeach()\n  endif()\n\n  message(STATUS \"${PROJECT_NAME} is ${${PROJECT_NAME}}\")\n\n  if(${${PROJECT_NAME}})\n    if(EXISTS \"${${subdir}_ROOT_DIR}/CMakeLists.txt\")\n      add_subdirectory(\"${${subdir}_ROOT_DIR}\")\n    else()\n      foreach(PROJECT_SUBFOLDER ${PROJECT_SUBFOLDERS})\n        if(EXISTS \"${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}/CMakeLists.txt\")\n          add_subdirectory(\"${${subdir}_ROOT_DIR}/${PROJECT_SUBFOLDER}\")\n        endif()\n      endforeach()\n    endif()\n\n    if(EXISTS \"${${subdir}_ROOT_DIR}/documentation/\" AND BUILD_DOCUMENTATION)\n      unset(PROJECT_PAGE)\n      unset(PROJECT_LINK)\n      unset(PROJECT_DESCRIPTION)\n      unset(PROJECT_TYPE)\n      include(\"${${subdir}_ROOT_DIR}/documentation/${subdir}_doc.cmake\" OPTIONAL)\n\n      if(NOT DEFINED PROJECT_PAGE)\n        set(PROJECT_PAGE \"${subdir}Page\")\n      endif()\n\n      if(NOT DEFINED PROJECT_TYPE)\n        set(PROJECT_TYPE \"OTHERS\")\n      endif()\n\n      set(PROJECT_SUBPAGE_REF \"  - @subpage ${PROJECT_PAGE}\")\n      set(PROJECT_REF_REF \"  - @ref ${PROJECT_PAGE}\")\n\n      if(DEFINED PROJECT_LINK)\n        string(APPEND PROJECT_SUBPAGE_REF \" (${PROJECT_LINK})\")\n        string(APPEND PROJECT_REF_REF \" (${PROJECT_LINK})\")\n      endif()\n\n      if(DEFINED PROJECT_DESCRIPTION)\n        string(APPEND PROJECT_SUBPAGE_REF \": ${PROJECT_DESCRIPTION}\")\n        string(APPEND PROJECT_REF_REF \" (${PROJECT_DESCRIPTION})\")\n      endif()\n\n      string(APPEND SIBR_PROJECTS_${PROJECT_TYPE}_SUBPAGE_REF_LOCAL \"${PROJECT_SUBPAGE_REF}\\n\")\n      string(APPEND SIBR_PROJECTS_${PROJECT_TYPE}_REF_REF_LOCAL \"${PROJECT_REF_REF}\\n\")\n\n      if(EXISTS \"${${subdir}_ROOT_DIR}/documentation/img\")\n        set(DOXY_APP_SPECIFIC_IMG_PATH_LOCAL \"${DOXY_APP_SPECIFIC_IMG_PATH_LOCAL} ${${subdir}_ROOT_DIR}/documentation/img\")\n      endif()\n\n      if(EXISTS \"${${subdir}_ROOT_DIR}/LICENSE.md\")\n        set(DOXY_DOC_EXCLUDE_PATTERNS_DIRS_LOCAL \"${DOXY_DOC_EXCLUDE_PATTERNS_DIRS_LOCAL} ${${subdir}_ROOT_DIR}/LICENSE.md\")\n      endif()\n    endif()\n  else()\n    set(DOXY_DOC_EXCLUDE_PATTERNS_DIRS_LOCAL \"${DOXY_DOC_EXCLUDE_PATTERNS_DIRS_LOCAL} ${${subdir}_ROOT_DIR}\")\n  endif()\nendforeach()\n  \nset(SIBR_PROJECTS_SAMPLES_SUBPAGE_REF \"${SIBR_PROJECTS_SAMPLES_SUBPAGE_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_OURS_SUBPAGE_REF \"${SIBR_PROJECTS_OURS_SUBPAGE_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF \"${SIBR_PROJECTS_TOOLBOX_SUBPAGE_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_OTHERS_SUBPAGE_REF \"${SIBR_PROJECTS_OTHERS_SUBPAGE_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_SAMPLES_REF_REF \"${SIBR_PROJECTS_SAMPLES_REF_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_OURS_REF_REF \"${SIBR_PROJECTS_OURS_REF_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_TOOLBOX_REF_REF \"${SIBR_PROJECTS_TOOLBOX_REF_REF_LOCAL}\" PARENT_SCOPE)\nset(SIBR_PROJECTS_OTHERS_REF_REF \"${SIBR_PROJECTS_OTHERS_REF_REF_LOCAL}\" PARENT_SCOPE)\nset(DOXY_APP_SPECIFIC_IMG_PATH \"${DOXY_APP_SPECIFIC_IMG_PATH_LOCAL}\" PARENT_SCOPE)\nset(DOXY_DOC_EXCLUDE_PATTERNS_DIRS \"${DOXY_DOC_EXCLUDE_PATTERNS_DIRS_LOCAL}\" PARENT_SCOPE)\n\nif (BUILD_IBR_TFGL_INTEROP)\n  add_subdirectory(projects/tfgl_interop/renderer/custom_ops)\nendif()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/ActiveImageFile.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n# include <boost/filesystem.hpp>\n# include <fstream>\n# include \"core/assets/ActiveImageFile.hpp\"\n\nnamespace sibr\n{\n\tbool ActiveImageFile::load( const std::string& filename, bool verbose )\n\t{\n\n\t\tstd::fstream\tfile(filename, std::ios::in);\n\t\tif(_numImages == 0 )\n\t\t\tSIBR_WRG << \"No Images Loaded while loading '\"<<filename<<\"'\"<<std::endl;\n\t\t\n\n\t\t// always create the array size of cameras so it can be queried.\n\t\t_active.resize(_numImages);\t\n\t\tfor(int i=0; i < _numImages; i++)\n\t\t\t_active[i]=false;\n\n\t\tif (file && !boost::filesystem::is_empty(filename))\n\t\t{\n\t\t\twhile (file.eof() == false)\n\t\t\t{\n\t\t\t\tint imageId;\n\t\t\t\tfile >>  imageId;\n\t\t\t\t_active[imageId] = true;\n\t\t\t}\n\n\t\t\tif( verbose )\n\t\t\t\tSIBR_FLOG << \"'\"<< filename <<\"' successfully loaded.\" << std::endl;\n\t\t\telse\n\t\t\t\tstd::cerr<< \".\" ;\n\t\t\treturn true;\n\t\t}\n\t \telse {\n\t\t\tfor(int i=0; i < _numImages; i++)\n\t\t\t\t_active[i]=true;\n\t\t\tif( verbose )\n\t\t\t\tSIBR_WRG << \"file not found: '\"<<filename<<\"'\"<<std::endl;\n\t\t}\n\t\treturn false;\n\t}\n\n\tbool ActiveImageFile::load( const std::string& filename, int numImage, bool verbose )\n\t{\n\t\t_numImages = numImage;\n\t\treturn load(filename, verbose);\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/ActiveImageFile.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n\r\n# include \"core/graphics/Image.hpp\"\r\n# include \"core/assets/Config.hpp\"\r\n# include \"core/assets/IFileLoader.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\t/** Represent a active_images.txt file use to select a subset of a scene images.\r\n\t\\ingroup sibr_assets\r\n\t*/\r\n\tclass SIBR_ASSETS_EXPORT ActiveImageFile : public IFileLoader\r\n\t{\r\n\r\n\tpublic:\r\n\r\n\t\t/**\r\n\t\tSet the number of images contained in the associated scene.\r\n\t\t\\param n the number of images.\r\n\t\t*/\r\n\t\tvoid setNumImages(int n) {\t_numImages = n; }\r\n\r\n\t\t/**\r\n\t\tLoad an active cameras listing from a file on disk.\r\n\t\t\\param filename the path to the file.\r\n\t\t\\param verbose output additional informations to the standard output.\r\n\t\t\\return a boolean indicating if the loading was successful or not.\r\n\t\t*/\r\n\t\tbool load( const std::string& filename, bool verbose = true ) override;\r\n\t\t\r\n\t\t/**\r\n\t\tLoad an active cameras listing from a file on disk, expecting numImage images in total.\r\n\t\tThe active images file does not indicate the total number of cameras in the scene, thus the additional parameter.\r\n\t\t\\param filename the path to the file.\r\n\t\t\\param numImages the total number of images in the associated scene.\r\n\t\t\\param verbose output additional informations to the standard output.\r\n\t\t\\return a boolean indicating if the load was successful or not.\r\n\t\t*/\r\n\t\tbool load( const std::string& filename, int numImages, bool verbose = true );\r\n\t\t\r\n\t\t/**\r\n\t\tReturn a reference to a boolean vector indicating, for each picture of \r\n\t\tthe associated scene, if it is active or not.\r\n\t\t\\return the boolean vector described above.\r\n\t\t*/\r\n\t\tconst std::vector<bool>&\tactive( void ) const { return _active; }\r\n\r\n\r\n\tprivate:\r\n\t\tstd::vector<bool> _active; ///< Flags denoting which images are active.\r\n\t\tint _numImages = 0; ///< Number of images.\r\n\r\n\t};\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nproject(sibr_assets)\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" )\r\nsource_group(\"Source Files\" FILES ${SOURCES})\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\r\n\r\n\r\n## Specify target rules\r\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\r\n\r\ninclude_directories(\r\n    ${Boost_INCLUDE_DIRS}\r\n    ${xatlas_INCLUDE_DIRS}\r\n)\r\ntarget_link_libraries(${PROJECT_NAME}\r\n    ${Boost_LIBRARIES}\r\n    ${ASSIMP_LIBRARIES}\r\n    ${GLEW_LIBRARIES}\r\n    ${OPENGL_LIBRARIES}\r\n    ${OpenCV_LIBRARIES}\r\n\tOpenMP::OpenMP_CXX\r\n    sibr_graphics\r\n    xatlas\r\n)\r\n\r\nadd_definitions(-DSIBR_ASSETS_EXPORTS -DBOOST_ALL_DYN_LINK)\r\n\r\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\r\n\r\n## High level macro to install in an homogen way all our ibr targets\r\ninclude(install_runtime)\r\nibr_install_target(${PROJECT_NAME}\r\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\r\n)\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/CameraRecorder.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include \"core/assets/CameraRecorder.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include <opencv2/imgcodecs.hpp>\n\nnamespace sibr\n{\n\tvoid\tCameraRecorder::use(Camera& cam)\n\t{\n\t\tif (_recording) {\n\t\t\t_cameras.push_back(cam);\n\t\t} \n\t\telse if (_playing && _pos < _cameras.size() ) {\n\t\t\tconst float znear = cam.znear();\n\t\t\tconst float zfar = cam.zfar();\n\t\t\t\n\t\t\tif( !_playNoInterp ) {\n\t\t\t\t//std::cout << _playing << std::endl;\n\t\t\t\t// If we reach the last frame of the interpolation b/w two cameras, skip to next camera.\n\t\t\t\tif (_interp > (1.0f - _speed))\n\t\t\t\t{\n\t\t\t\t\t_interp = 0.0f;\n\t\t\t\t\t_pos++;\n\t\t\t\t}\n\t\t\t\t// Interpolate between the two closest cameras.\n\t\t\t\tconst float k = std::min(std::max(_interp, 0.0f), 1.0f);\n\t\t\t\t\n\t\t\t\tsibr::Camera & camStart = _cameras[std::min(int(_pos), int(_cameras.size()) - 1)];\n\t\t\t\tsibr::Camera & camNext = _cameras[std::min(int(_pos) + 1, int(_cameras.size())-1)];\n\n\t\t\t\tcam = sibr::Camera::interpolate(camStart, camNext, k);\n\n\t\t\t\t_interp += _speed;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcam = _cameras[_pos];\n\t\t\t\t_pos++;\n\t\t\t\tif (_pos == _cameras.size())\n\t\t\t\t\t_playNoInterp = false;\n\t\t\t\t\n\t\t\t}\n\n\t\t\t// Preserve the znear and zfar.\n\t\t\tcam.znear(znear);\n\t\t\tcam.zfar(zfar);\n\n\t\t\tif (_saving) {\n\t\t\t\t// 获取当前时间的毫秒数\n\t\t\t\t// auto now = std::chrono::system_clock::now();\n\t\t\t\t// auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();\n\n\t\t\t\tif(_savingPath != _lastPath) {\n\t\t\t\t\t_imgid = -1;\n\t\t\t\t}\n\t\t\t\t    auto now = std::chrono::system_clock::now();\n\t\t\t\t\tstd::time_t now_time = std::chrono::system_clock::to_time_t(now);\n\t\t\t\t\t\n\t\t\t\t\t// 转换为结构化时间（tm）\n\t\t\t\t\tstd::tm* local_time = std::localtime(&now_time);\n\t\t\t\t\t\n\t\t\t\t\t// 格式化时间\n\t\t\t\t\tstd::ostringstream oss;\n\t\t\t\t\toss << std::put_time(local_time, \"%Y%m%d_%H%M%S_\");\n\t\t\t\t// 将毫秒数转换为字符串\n\t\t\t\t// std::ostringstream ssTime;\n\t\t\t\t// ssTime << ms;\n\t\t\t\t\n\t\t\t\t// 设置保存路径\n\t\t\t\tif (_imgid <0){\n\t\t\t\t\tcam.setSavePath(\"\");\n\t\t\t\t}\n\t\t\t\telse{\n\t\t\t\t\tcam.setSavePath(_savingPath + \"/\" + oss.str() + std::to_string(_imgid) + \".png\");\n\t\t\t\t}\n\t\t\t\t_imgid += 1;\n\n\t\t\t\t// std::ostringstream ssZeroPad;\n\t\t\t\t// ssZeroPad << std::setw(8) << std::setfill('0') << (_pos);\n\t\t\t\t// cam.setSavePath(_savingPath + \"/\" + ssZeroPad.str() + \".png\");\n\t\t\t\t// std::cout << \"Saving frame as: \" << cam.savePath() << std::endl;\n\t\t\t}\n\t\t\tif (_savingVideo) {\n\t\t\t\tcam.setDebugVideo(true);\n\t\t\t}\n\t\t\tif (_pos >= _cameras.size())\n\t\t\t{\n\t\t\t\t_lastPath = _savingPath;\n\t\t\t\t// sleep(3);\n\t\t\t\tstop();\n\t\t\t\tSIBR_LOG << \"[CameraRecorder] - Playback Finished\" << std::endl;\n\t\t\t}\n\t\t} \n\t\telse {\n\t\t\t//std::cout << _playing << std::endl;\n\t\t\tcam.setSavePath(\"\");\n\t\t\tcam.setDebugVideo(false);\n\t\t}\n\t}\n\n\tvoid\tCameraRecorder::playback(void)\n\t{\n\t\tstop();\n\t\t_playing = true;\n\t\tSIBR_LOG << \"[CameraRecorder] - Playing\" << std::endl;\n\t}\n\n\tvoid\tCameraRecorder::record(void)\n\t{\n\t\tstop();\n\t\t_recording = true;\n\t\tSIBR_LOG << \"[CameraRecorder] - Recording\" << std::endl;\n\t}\n\n\tvoid sibr::CameraRecorder::saving(std::string savePath)\n\t{\n\t\t_saving = true;\n\t\t_savingPath = savePath;\n\t\tSIBR_LOG << \"[CameraRecorder] - Recording\" << std::endl;\n\t}\n\n\tvoid CameraRecorder::savingVideo(bool saveVideo)\n\t{\n\t\t_savingVideo = saveVideo;\n\t}\n\n\tvoid sibr::CameraRecorder::stopSaving(void)\n\t{\n\t\t_saving = false;\n\t\t_savingPath = \"\";\n\t}\n\n\tvoid\tCameraRecorder::stop(void)\n\t{\n\t\t_recording = _playing = false;\n\t\t_pos = 0;\n\t\t_interp = 0.0f;\n\t}\n\n\tvoid\tCameraRecorder::reset(void)\n\t{\n\t\tstop();\n\t\t_cameras.clear();\n\t}\n\n\tbool\tCameraRecorder::load(const std::string& filename)\n\t{\n\t\treset();\n\n\t\tsibr::ByteStream stream;\n\n\t\tif (stream.load(filename) == false)\n\t\t\treturn false;\n\n\t\tint32 num = 0;\n\n\t\tstd::cout << \" CameraRecorder::load \" << num << std::endl;\n\n\t\tstream >> num;\n\t\twhile (num > 0)\n\t\t{\n\t\t\tCamera cam;\n\t\t\tstream >> cam;\n\t\t\t_cameras.emplace_back(std::move(cam));\n\t\t\t--num;\n\t\t}\n\t\tSIBR_LOG << \"[CameraRecorder] - Loaded from \" << filename << std::endl;\n\t\treturn stream;\n\t}\n\n\tvoid\tCameraRecorder::save(const std::string& filename)\n\t{\n\t\tsibr::ByteStream stream;\n\t\tint32 num = (int32)_cameras.size();\n\t\tstream << num;\n\t\tfor (const Camera& cam : _cameras)\n\t\t\tstream << cam;\n\n\t\tstream.saveToFile(filename);\n\t\tSIBR_LOG << \"[CameraRecorder] - Saved \" << num << \" cameras to \" << filename << std::endl;\n\t}\n\n\tbool CameraRecorder::safeLoad(const std::string & filename, int w, int h)\n\t{\n\t\tPath path = Path(filename);\n\n\t\tif (path.extension().string() == \".out\") {\n\t\t\tloadBundle(filename, w, h);\n\t\t\treturn true;\n\t\t} else if (path.extension().string() == \".path\") {\n\t\t\treturn load(filename);\n\t\t} \n\t\tSIBR_WRG << \"Unable to load camera path\" << std::endl;\n\t\treturn false;\n\t}\n\n\tvoid CameraRecorder::loadBundle(const std::string & filePath, int w, int h)\n\t{\n\t\tconst std::string bundlerFile = filePath;\n\t\tSIBR_LOG << \"Loading bundle path.\" << std::endl;\n\n\t\t// check bundler file\n\t\tstd::ifstream bundle_file(bundlerFile);\n\t\tSIBR_ASSERT(bundle_file.is_open());\n\n\t\t// read number of images\n\t\tstd::string line;\n\t\tgetline(bundle_file, line);\t// ignore first line - contains version\n\t\tint numImages = 0;\n\t\tbundle_file >> numImages;\t// read first value (number of images)\n\t\tgetline(bundle_file, line);\t// ignore the rest of the line\n\n\t\tstd::vector<InputCamera::Ptr> cameras(numImages);\n\t\t//  Parse bundle.out file for camera calibration parameters\n\t\tfor (int i = 0; i < numImages; i++) {\n\t\t\n\t\t\tMatrix4f m;\n\t\t\tbundle_file >> m(0) >> m(1) >> m(2) >> m(3) >> m(4);\n\t\t\tbundle_file >> m(5) >> m(6) >> m(7) >> m(8) >> m(9);\n\t\t\tbundle_file >> m(10) >> m(11) >> m(12) >> m(13) >> m(14);\n\n\t\t\tcameras[i] = InputCamera::Ptr(new InputCamera(i, w, h, m, true));\n\n\t\t\tcameras[i]->znear(0.2f); cameras[i]->zfar(250.f);\n\n\t\t}\n\n\t\tfor (const InputCamera::Ptr cam : cameras)\n\t\t{\n\t\t\t_cameras.push_back(*cam);\n\t\t}\n\n\t}\n\n\tvoid CameraRecorder::loadColmap(const std::string &filePath, int w, int h)\n\t{\n\t\tSIBR_LOG << \"Loading colmap path.\" << std::endl;\n\t\tboost::filesystem::path colmapDir ( filePath );\n\n\t\tSIBR_LOG << \"DEBUG: colmap path dir \" << colmapDir.parent_path().string() << std::endl;\n\n\t\tstd::vector<InputCamera::Ptr> path = InputCamera::loadColmap(colmapDir.parent_path().string(), float(0.01), 1000, false );\n\t\tfor (const InputCamera::Ptr cam : path)\n\t\t{\n\t\t\t_cameras.push_back(*cam);\n\t\t}\n\t}\n\n\t// for http server\n\tvoid CameraRecorder::loadColmapFromHttp(const std::string& cameraParams, const std::string& imageParams, std::string pathParams)\n\t{\n\t\tSIBR_LOG << \"Loading colmap from http.\" << std::endl;\n\t\tstd::cout << \"test\" << std::endl;\n\t\tstd::vector<InputCamera::Ptr> path = InputCamera::loadColmapFromHttp(cameraParams, imageParams, float(0.01), 1000, false );\n\t\t_cameras.clear();\n\t\tfor (const InputCamera::Ptr cam : path)\n\t\t{\n\t\t\t_cameras.push_back(*cam);\n\t\t}\n\t\t_saving = true;\n\t\t_savingPath = pathParams;\n\t\t_playNoInterp = true;\n\t\tplayback();\n\n\t\t\n\t}\n\n\tvoid CameraRecorder::loadLookat(const std::string &filePath, int w, int h)\n\t{\n\t\tSIBR_LOG << \"Loading lookat path.\" << std::endl;\n\t\tstd::vector<InputCamera::Ptr> path = InputCamera::loadLookat(filePath, std::vector<Vector2u>{Vector2u(w, h)});\n\t\tfor (const InputCamera::Ptr cam : path)\n\t\t{\n\t\t\t_cameras.push_back(*cam);\n\t\t}\n\t}\n\n\tvoid CameraRecorder::saveAsBundle(const std::string & filePath, const int height, const int step)\n\t{\n\n\t\tstd::ofstream out(filePath.c_str(), std::ios::binary);\n\t\tif (!out.good()) {\n\t\t\tSIBR_LOG << \"ERROR: cannot write to the file '\" << filePath << \"'\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tif(_cameras.empty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst int size = static_cast<int>(_cameras.size() / step);\n\t\t\n\t\tout << \"# Bundle file v0.3\\n\";\n\t\tout << size << \" \" << 0 << \"\\n\";\n\n\t\tfor (int i = 0; i < _cameras.size(); i += step) {\n\n\t\t\tconst sibr::Camera cam = _cameras[i];\n\t\t\tsibr::Quaternionf q = cam.rotation();\n\t\t\tsibr::Matrix3f m1 = q.toRotationMatrix();\n\t\t\tsibr::Vector3f pos = -m1.transpose()  * cam.position();\n\n\t\t\tfloat m[15];\n\t\t\tm[0] = 0.5f*height / tan(cam.fovy() / 2.f); m[1] = 0.0f; m[2] = 0.0f;\n\t\t\tm[3] = m1(0, 0), m[4] = m1(1, 0), m[5] = m1(2, 0);\n\t\t\tm[6] = m1(0, 1), m[7] = m1(1, 1), m[8] = m1(2, 1);\n\t\t\tm[9] = m1(0, 2), m[10] = m1(1, 2), m[11] = m1(2, 2);\n\t\t\tm[12] = pos.x(), m[13] = pos.y(), m[14] = pos.z();\n\n\t\t\tout << m[0] << \" \" << m[1] << \" \" << m[2] << std::endl;\n\t\t\tout << m[3] << \" \" << m[4] << \" \" << m[5] << std::endl;\n\t\t\tout << m[6] << \" \" << m[7] << \" \" << m[8] << std::endl;\n\t\t\tout << m[9] << \" \" << m[10] << \" \" << m[11] << std::endl;\n\t\t\tout << m[12] << \" \" << m[13] << \" \" << m[14] << std::endl;\n\t\t}\n\t\tout << std::endl;\n\t\tout.close();\n\n\t\tSIBR_LOG << \"[CameraRecorder] - Saved \" << _cameras.size() << \" cameras to \" << filePath << \" (using fovy \" <<_cameras[0].fovy() << \").\" << std::endl;\n\t\t\n\t}\n\n\tvoid CameraRecorder::saveAsColmap(const std::string& filePath, const int height, const int width)\n\t{\n\n\t\tstd::string basepath = parentDirectory(filePath);\n\t\tstd::cout << basepath << std::endl;\n\t\tstd::string images_filepath = basepath + \"/images.txt\";\n\t\tstd::string cameras_filepath = basepath + \"/cameras.txt\";\n\n\t\tstd::ofstream out_images(images_filepath.c_str(), std::ios::binary);\n\t\tstd::ofstream out_cameras(cameras_filepath.c_str(), std::ios::binary);\n\n\t\tif (!out_images.good()) {\n\t\t\tSIBR_LOG << \"ERROR: cannot write to the file '\" << filePath << \"'\" << std::endl;\n\t\t\treturn;\n\t\t}\n\t\tif (!out_cameras.good()) {\n\t\t\tSIBR_LOG << \"ERROR: cannot write to the file '\" << filePath << \"'\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tif (_cameras.empty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst int size = static_cast<int>(_cameras.size());\n\t\t\n\t\tsibr::Matrix3f converter;\n\t\tconverter << 1, 0, 0,\n\t\t\t0, -1, 0,\n\t\t\t0, 0, -1;\n\n\n\n\t\tout_images << \"# Image list with two lines of data per image:\" << std::endl;\n\t\tout_images << \"#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME\" << std::endl;\n\t\tout_images << \"#   POINTS2D[] as (X, Y, POINT3D_ID)\" << std::endl;\n\t\tfor (int i = 0; i < _cameras.size(); i++) {\n\t\t\tsibr::Matrix3f tmp = _cameras[i].rotation().toRotationMatrix() * converter;\n\t\t\tsibr::Matrix3f Qinv = tmp.transpose();\n\t\t\tsibr::Quaternionf q = quatFromMatrix(Qinv);\n\t\t\tsibr::Vector3f t = converter * _cameras[i].rotation().toRotationMatrix().transpose() * (-_cameras[i].position());\n\t\t\n\n\t\t\tout_images << i << \" \" << -_cameras[i].rotation().x() << \" \" << -_cameras[i].rotation().w() << \" \" << -_cameras[i].rotation().z() << \" \" << _cameras[i].rotation().y() << \" \" <<\n\t\t\t\t_cameras[i].view()(0, 3) << \" \" << -_cameras[i].view()(1, 3) << \" \" << -_cameras[i].view()(2, 3) << \" \" << i << \" \" << \"00000000.png\" << std::endl << std::endl;\n\t\t\t\n\t\t\tfloat focal = 0.5f * height / tan(_cameras[i].fovy() / 2.f);\n\t\t\t//float focal = 1.0f / (tan(0.5f * cam.fovy()) * 2.0f / float(height));\n\t\t\tout_cameras << i << \" \" << \"PINHOLE\" << \" \" << width << \" \" << height << \" \" << focal << \" \" << focal << \" \" << width / 2.0 << \" \" << height / 2.0 << std::endl;\n\t\t}\n\n\t\tout_images << std::endl;\n\t\tout_images.close();\n\t\tout_cameras << std::endl;\n\t\tout_cameras.close();\n\t\tSIBR_LOG << \"[CameraRecorder] - Saved \" << _cameras.size() << \" cameras to \" << filePath << \" (using fovy \" << _cameras[0].fovy() << \").\" << std::endl;\n\n\t}\n\n\n\tvoid CameraRecorder::saveAsFRIBRBundle(const std::string & dirPath, const int width, const int height)\n\t{\n\t\tconst std::string bundlepath = dirPath + \"/path.rd.out\";\n\t\tconst std::string listpath = dirPath + \"/list.txt\";\n\t\tconst std::string imagesDir = dirPath + \"/visualize/\";\n\t\tsibr::makeDirectory(dirPath);\n\t\tsibr::makeDirectory(imagesDir);\n\t\tstd::ofstream out(bundlepath);\n\t\tstd::ofstream outList(listpath);\n\t\tif (out.good() && outList.good()) {\n\t\t\tconst int size = static_cast<int>(_cameras.size() / 1);\n\t\t\tout << \"# Bundle file v0.3\\n\";\n\t\t\tout << size << \" \" << 0 << \"\\n\";\n\t\t\tsibr::Matrix3f converter;\n\t\t\tconverter << 1, 0, 0,\n\t\t\t\t0, -1, 0,\n\t\t\t\t0, 0, -1;\n\t\t\tsibr::Matrix3f from_cv;\n\t\t\tfrom_cv << 1, 0, 0,\n\t\t\t\t0, -1, 0,\n\t\t\t\t0, 0, -1;\n\t\t\tfor (int i = 0; i < _cameras.size(); ++i) {\n\n\t\t\t\tconst sibr::Camera cam = _cameras[i];\n\n\t\t\t\tsibr::Matrix3f orientation = cam.rotation().toRotationMatrix();\n\t\t\t\tsibr::Vector3f position = cam.position();\n\t\t\t\tsibr::Matrix3f rotation_cv = converter.transpose() * orientation.transpose() * converter;\n\t\t\t\tsibr::Matrix3f rotation_bundler = from_cv * rotation_cv;\n\t\t\t\tsibr::Vector3f position_cv = converter.transpose() * position;\n\t\t\t\tsibr::Vector3f translation_cv = -(rotation_cv * position_cv);\n\t\t\t\tsibr::Vector3f pos = from_cv * translation_cv;\n\n\t\t\t\tsibr::Matrix3f m1 = rotation_bundler.transpose();\n\t\t\t\tfloat m[15];\n\t\t\t\tm[0] = 0.5f*height / tan(cam.fovy() / 2.f); m[1] = 0.0f; m[2] = 0.0f;\n\t\t\t\tm[3] = m1(0, 0), m[4] = m1(1, 0), m[5] = m1(2, 0);\n\t\t\t\tm[6] = m1(0, 1), m[7] = m1(1, 1), m[8] = m1(2, 1);\n\t\t\t\tm[9] = m1(0, 2), m[10] = m1(1, 2), m[11] = m1(2, 2);\n\t\t\t\tm[12] = pos.x(), m[13] = pos.y(), m[14] = pos.z();\n\t\t\t\tout << m[0] << \" \" << m[1] << \" \" << m[2] << std::endl;\n\t\t\t\tout << m[3] << \" \" << m[4] << \" \" << m[5] << std::endl;\n\t\t\t\tout << m[6] << \" \" << m[7] << \" \" << m[8] << std::endl;\n\t\t\t\tout << m[9] << \" \" << m[10] << \" \" << m[11] << std::endl;\n\t\t\t\tout << m[12] << \" \" << m[13] << \" \" << m[14] << std::endl;\n\n\t\t\t\tconst std::string imageName = sibr::intToString<8>(i) + \".jpg\";\n\t\t\t\toutList << \"visualize/\" + imageName << \" 0 \" << m[0] << std::endl;\n\n\t\t\t\tcv::Mat3b dummy(height, width);\n\t\t\t\tcv::imwrite(imagesDir + imageName, dummy);\n\t\t\t}\n\t\t\tout << std::endl;\n\t\t\tout.close();\n\t\t\toutList.close();\n\n\t\t\tSIBR_LOG << \"[CameraRecorder] - Saved \" << _cameras.size() << \" cameras to \" << dirPath << \".\" << std::endl;\n\t\t}\n\t}\n\n\tvoid CameraRecorder::saveAsLookAt(const std::string & filePath) const\n\t{\n\t\tInputCamera::saveAsLookat(_cameras, filePath);\n\t}\n\n\t// offline path rendering\n\tbool CameraRecorder::loadPath(const std::string& pathFileName, int w, int h) {\n\t\t_savingPath = parentDirectory(pathFileName);\n\t\tif (!fileExists(pathFileName)) {\n\t\t\tSIBR_WRG << \"Camera path \" << pathFileName << \" doesnt exist. \" << std::endl;\n\t\t\treturn false;\n\t\t}\n\t\t_ow = w, _oh = h;\n\t\tif (boost::filesystem::extension(pathFileName) == \".out\")\n\t\t\tloadBundle(pathFileName, w, h);\n\t\telse if (boost::filesystem::extension(pathFileName) == \".lookat\")\n\t\t\tloadLookat(pathFileName, w, h);\n\t\telse if (boost::filesystem::extension(pathFileName) == \".txt\")\n\t\t\tloadColmap(pathFileName, w, h);\n\t\telse\n\t\t\tload(pathFileName);\n\t\treturn true;\n\t}\n\n\tvoid CameraRecorder::recordOfflinePath(const std::string& outPathDir, ViewBase::Ptr view, const std::string& prefix) {\n\t\tsibr::ImageRGBA32F::Ptr outImage;\n\t\toutImage.reset(new ImageRGBA32F(_ow, _oh));\n\t\tstd::string outpathd = outPathDir;\n\n\t\tsibr::RenderTargetRGBA32F::Ptr outFrame;\n\t\toutFrame.reset(new RenderTargetRGBA32F(_ow, _oh));\n\t\tstd::string outFileName;\n\n\t\tboost::filesystem::path dstFolder;\n\n\t\toutpathd = outPathDir;\n\n\t\tif (outPathDir == \"pathOutput\" && _savingPath != \"\") { // default to path parent, saved by loadPath\n\t\t\toutpathd = _savingPath + \"/\" + \"pathOutput\";\n\t\t\tdstFolder = outpathd;\n\t\t\tif (!directoryExists(outpathd) && !boost::filesystem::create_directories(dstFolder))\n\t\t\t\tSIBR_ERR << \"Error creating directory \" << dstFolder << std::endl;\n\t\t}\n\n        if( prefix != \"\" ) \n    \t\tdstFolder = outpathd = outpathd + \"/\" + prefix;\n        else \n            dstFolder = outpathd ;\n\n\t\tif (!directoryExists(outpathd) && !boost::filesystem::create_directories(dstFolder))\n\t\t\tSIBR_ERR << \"Error creating directory \" << dstFolder << std::endl;\n\n\t\tstd::cout << \"Rendering path with \" << _cameras.size() << \" cameras to \" << outpathd << std::endl;\n\n\t\tfor (int i = 0; i < _cameras.size(); ++i) {\n\t\t\toutFrame->clear();\n\t\t\tstd::ostringstream ssZeroPad;\n\t\t\tssZeroPad << std::setw(8) << std::setfill('0') << i;\n\t\t\toutFileName = outpathd + \"/\" +  ssZeroPad.str() + \".png\";\n\t\t\tstd::cout << outFileName << \" \" << std::endl;\n\t\t\tview->onRenderIBR(*outFrame, _cameras[i]);\n\t\t\toutFrame->readBack(*outImage);\n\t\t\toutImage->save(outFileName, false);\n\t\t}\n\t\tstd::cout << std::endl;\n\n\t\tstd::cout << \"Done rendering path. \" << std::endl;\n\n\t}\n\n\tvoid CameraRecorder::saveImage(const std::string& outPathDir, const Camera& cam, int w, int h) {\n\t\tsibr::ImageRGBA32F::Ptr outImage;\n\t\t_ow = w, _oh = h;\n\t\toutImage.reset(new ImageRGBA32F(_ow, _oh));\n\t\tstd::string outpathd = outPathDir;\n\n\t\tsibr::RenderTargetRGBA32F::Ptr outFrame;\n\t\toutFrame.reset(new RenderTargetRGBA32F(_ow, _oh));\n\t\tstd::string outFileName;\n\n\t\tboost::filesystem::path dstFolder;\n\n\t\toutpathd = outPathDir;\n\n\t\tif (outPathDir == \"\") { // default to path parent, saved by loadPath\n\t\t\toutpathd = _dsPath + \"/\" + \"pathOutput\";\n\t\t\tdstFolder = outpathd;\n\t\t\tif (!directoryExists(outpathd) && !boost::filesystem::create_directories(dstFolder))\n\t\t\t\tSIBR_ERR << \"Error creating directory \" << dstFolder << std::endl;\n\t\t}\n\n\t\tdstFolder = outpathd;\n\n\t\tif (!directoryExists(outpathd) && !boost::filesystem::create_directories(dstFolder))\n\t\t\tSIBR_ERR << \"Error creating directory \" << dstFolder << std::endl;\n\n\t\tstd::cout << \"Saving current camera to \" << outpathd << std::endl;\n\n\t\toutFrame->clear();\n\t\tstd::ostringstream ssZeroPad;\n\t\tstatic int i = 0;\n\t\tssZeroPad << std::setw(8) << std::setfill('0') << i++;\n\t\toutFileName = outpathd + \"/\" + ssZeroPad.str() + \".png\";\n\t\tstd::cout << \"outFileName\" << outFileName << \" \" << std::endl;\n\t\t_view->onRenderIBR(*outFrame, cam);\n\t\toutFrame->readBack(*outImage);\n\t\toutImage->save(outFileName, false);\n\t\tstd::cout << std::endl;\n\t\tstd::cout << \"Done saving image. \" << std::endl;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/CameraRecorder.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <vector>\r\n# include \"core/assets/Config.hpp\"\r\n# include \"core/graphics/Camera.hpp\"\r\n# include \"core/view/ViewBase.hpp\"\r\n\r\n\r\n# define SIBR_CAMERARECORDER_DEFAULTFILE \"camera-record.bytes\"\r\n\r\nnamespace sibr\r\n{\r\n\t/** This class handles the recording and replay of a stream of cameras.\r\n\t\\ingroup sibr_assets\r\n\t*/\r\n\tclass SIBR_ASSETS_EXPORT CameraRecorder\r\n\t{\r\n\tpublic:\r\n\r\n\t\t/**\r\n\t\tDefault constructor.\r\n\t\t*/\r\n\t\tCameraRecorder(void) :\r\n\t\t\t_pos(0), _recording(false), _playing(false), _saving(false), _savingPath(\"\"), _savingVideo(false), _savingVideoPath(\"\"), _speed(1.0f), _interp(0.0f), _playNoInterp(false) {\r\n\t\t\t\t//load();\r\n\t\t\t\t_imgid = 0;\r\n\t\t}\r\n\t\t/**\r\n\t\tDefault destructor.\r\n\t\t*/\r\n\t\t~CameraRecorder( void ) { \r\n\t\t}\r\n\r\n\t\t/**\r\n\t\tThis method supports two modes: if the recorder is currently recording, \r\n\t\tthe camera argument will be saved into the recording; else if the recorder \r\n\t\tis playing, the camera argument will be set to the current replaid camera.\r\n\t\t\\param cam A reference to the camera to record/update.\r\n\t\t*/\r\n\t\tvoid\tuse( Camera& cam );\r\n\r\n\t\t/**\r\n\t\tStart playing the recorded camera stream from the beginning, at a rate of one step for each \"use\" call.\r\n\t\t*/\r\n\t\tvoid\tplayback( void );\r\n\t\t\r\n\t\t/**\r\n\t\tStart recording a new camera stream, at a rate of one camera saved for each \"use\" call.\r\n\t\t*/\r\n\t\tvoid\trecord( void );\r\n\r\n\t\t/**\r\n\t\tStart asking the renderer to save the frames, at a rate of one image saved for each \"use\" call.\r\n\t\t*/\r\n\t\tvoid\tsaving(std::string savePath);\r\n\r\n\t\t/**\r\n\t\tToggle the save flag for video frames when replaying.\r\n\t\t\\param saveVideo the new flag status\r\n\t\t*/\r\n\t\tvoid\tsavingVideo(bool saveVideo);\r\n\r\n\t\t/**\r\n\t\tStop saving.\r\n\t\t*/\r\n\t\tvoid\tstopSaving(void);\r\n\r\n\t\t/**\r\n\t\tStop playing/recording.\r\n\t\t*/\r\n\t\tvoid\tstop( void );\r\n\r\n\t\t/**\r\n\t\tClear the current recording.\r\n\t\t*/\r\n\t\tvoid\treset( void );\r\n\r\n\t\t/**\r\n\t\tSet value of play no interpolation\r\n\t\t*/\r\n\t\tvoid\tplayNoInterpolation( bool val ) { _playNoInterp = val; }\r\n\r\n\t\t/**\r\n\t\tLoad a recorded camera stream from a given file.\r\n\t\t\\param filename Optional path to the file to load from. By default will try to \r\n\t\t\t\tload SIBR_CAMERARECORDER_DEFAULTFILE from the current directory.\r\n\t\t\\return a boolean denoting the loading success/failure.\r\n\t\t*/\r\n\t\tbool\tload( const std::string& filename=SIBR_CAMERARECORDER_DEFAULTFILE );\r\n\r\n\t\t/**\r\n\t\tSave the current recording stream to a given file.\r\n\t\t\\param filename Optional path to the file to write to. By default will try to\r\n\t\twrite to SIBR_CAMERARECORDER_DEFAULTFILE in the current directory.\r\n\t\t*/\r\n\t\tvoid\tsave( const std::string& filename=SIBR_CAMERARECORDER_DEFAULTFILE );\r\n\r\n\t\t/** Load recorded path based on file extension.\r\n\t\t *\\param filename the file to load\r\n\t\t *\\param w resoltuion width\r\n\t\t *\\param h resolution height\r\n\t\t *\\return a success boolean\r\n\t\t *\\note w and h are needed when loading a Bundle.\r\n\t\t */\r\n\t\tbool safeLoad(const std::string& filename, int w = 1920, int h = 1080);\r\n\r\n\t\t/**\r\n\t\tLoad a recording stream saved as a bundle file (useful for path from FRIBR).\r\n\t\t\\param filePath Path to the bundle file to write to.\r\n\t\t\\param w the image width to use for Fov computation\r\n\t\t\\param h the image height\r\n\t\t*/\r\n\t\tvoid\tloadBundle(const std::string & filePath, int w = 1920, int h = 1080);\r\n\r\n\t\t/**\r\n\t\t *Load a camera path generated by the camera editor / bledner plugin\r\n\t\t *\\param filePath Path to the .lookat file.\r\n\t\t *\\param w Width of the cameras to create.\r\n\t\t *\\param h Height of the cameras to create.\r\n\t\t */\r\n\t\tvoid loadLookat(const std::string &filePath, int w = 1920, int h = 1080);\r\n\r\n\t\t/**\r\n\t\t *Load a camera path generated by colmap requires filename images.txt for now TODO GD; fix this\r\n\t\t *\\param filePath Path to the images.txt file; assumes that a cameras.txt is \"next to\" it; \r\n\t\t *\\param w Width of the cameras to create.\r\n\t\t *\\param h Height of the cameras to create.\r\n\t\t */\r\n\t\tvoid loadColmap(const std::string &filePath, int w = 1920, int h = 1080);\r\n\t\tvoid loadColmapFromHttp(const std::string& cameraParams, const std::string& imageParams, const std::string pathParams);\r\n\r\n\r\n\t\t/**\r\n\t\tSave the current recording stream as a bundle file.\r\n\t\t\\param filePath Path to the bundle file to write to. \r\n\t\t\\param height the height in pixels of the camera. Used to compute focal length in mm as expected by bundle.\r\n\t\t\\param step set to a value greater than 1 to save every \"step\" camera in the recording stream. \r\n\t\t*/\r\n\t\tvoid\tsaveAsBundle(const std::string & filePath, const int height, const int step = 1);\r\n\t\tvoid    saveAsColmap(const std::string& filePath, const int height, const int width);\r\n\r\n\t\t/**\r\n\t\tSave the current recording stream as a bundle file and a series of empty images for FRIBR compatibility.\r\n\t\t\\param dirPath Path to the directory to export to.\r\n\t\t\\param width the width in pixels of the camera. \r\n\t\t\\param height the height in pixels of the camera.\r\n\t\t*/\r\n\t\tvoid\tsaveAsFRIBRBundle(const std::string & dirPath, const int width, const int height);\r\n\r\n\t\t/**\r\n\t\tSave the current recording stream as a lookat file.\r\n\t\t\\param filePath Path to the lookat file to write to.\r\n\t\t*/\r\n\t\tvoid saveAsLookAt(const std::string& filePath) const;\r\n\r\n\t\t/**\r\n\t\t\\return a boolean denoting if the recorder is currently playing.\r\n\t\t*/\r\n\t\tbool isPlaying() const { return _playing; }\r\n\r\n\t\t/**\r\n\t\t\\return a boolean denoting if the recorder is currently recording.\r\n\t\t*/\r\n\t\tbool isRecording() const { return _recording;  }\r\n\r\n\t\t/**\r\n\t\t\\return a boolean denoting if the recorder is currently asking frames to be saved.\r\n\t\t*/\r\n\t\tbool isSaving() const { return _saving; }\r\n\r\n\t\t/**\r\n\t\t\\return A reference to the current stream of recorded cameras.\r\n\t\t*/\r\n\t\tstd::vector<Camera>& cams() { return _cameras;  }\r\n\t\t\r\n\t\t/**\r\n\t\tUpdates the cameras from a vector, usefull to play already loaded path.\r\n\t\t*/\r\n\t\tvoid cams(std::vector<Camera>& cams) { _cameras = cams; }\r\n\r\n\r\n\t\t/**\r\n\t\tLoad a path, based on file extension name. Cameras loaded into _cameras\r\n\t\t*/\r\n\t\tbool loadPath(const std::string& pathFileName, int w, int h);\r\n\r\n\t\t/**\r\n\t\tPlay path for offline rendering using abstract View interface\r\n\t\t*/\r\n\t\tvoid recordOfflinePath(const std::string& outPathDir, ViewBase::Ptr view, const std::string& prefix);\r\n\r\n\t\t/**\r\n\t\tSave an image\r\n\t\t*/\r\n\t\tvoid setViewPath(ViewBase::Ptr view, const std::string& dataset_path) { _view = view; _dsPath = dataset_path;  };\r\n\r\n\t\tvoid saveImage(const std::string& outPathDir, const Camera& cam, int w, int h);\r\n\r\n\t\t/**\r\n\t\t * \\return the interpolation speed\r\n\t\t*/\r\n\t\tfloat & speed() { return _speed; }\r\n\r\n\tprivate:\r\n\t\tstd::string\t\t\t\t_dsPath; // path to dataset\r\n\t\tViewBase::Ptr\t\t\t_view; // view to save images\r\n\t\tuint\t\t\t\t\t_pos; ///< Current camera ID for replay.\r\n\t\tstd::vector<Camera>\t\t_cameras; ///< List of recorded cameras.\r\n\t\tbool\t\t\t\t\t_recording; ///< Are we currently recording.\r\n\t\tbool\t\t\t\t\t_playing; ///< Are we currently playing.\r\n\t\tbool\t\t\t\t\t_saving; ///< Are we saving the path as images.\r\n\t\tstd::string\t\t\t\t_savingPath; ///< Destination base path for saved images.\r\n\t\tstd::string             _lastPath; ///< Last path loaded\r\n\t\tbool\t\t\t\t\t_savingVideo; ///< Are we saving the path as video.\r\n\t\tstd::string\t\t\t\t_savingVideoPath; ///< Destination base path for saved video.\r\n\t\tfloat\t\t\t\t\t_speed; ///< Playback speed.\r\n\t\tfloat\t\t\t\t\t_interp; ///< Current interpoaltion factor.\r\n\t\tbool\t\t\t\t\t_playNoInterp; ///< Just play the cameras, make sure focal is preserved\r\n\t\tint\t\t\t\t\t\t_ow, _oh; ///< offline output path resolution\r\n\t\tint \t\t\t\t    _imgid; ///< image count\r\n\t};\r\n\r\n\t///// DEFINITIONS /////\r\n\r\n\r\n\r\n} // namespace sibr\r\n \r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/Config.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include <iomanip>\r\n\r\n\r\n#ifdef SIBR_OS_WINDOWS\r\n//// Export Macro (used for creating DLLs) ////\r\n# ifdef SIBR_STATIC_DEFINE\r\n#   define SIBR_EXPORT\r\n#   define SIBR_NO_EXPORT\r\n# else\r\n#   ifndef SIBR_ASSETS_EXPORT\r\n#     ifdef SIBR_ASSETS_EXPORTS\r\n         /* We are building this library */\r\n#       define SIBR_ASSETS_EXPORT __declspec(dllexport)\r\n#     else\r\n         /* We are using this library */\r\n#       define SIBR_ASSETS_EXPORT __declspec(dllimport)\r\n#     endif\r\n#   endif\r\n#   ifndef SIBR_NO_EXPORT\r\n#     define SIBR_NO_EXPORT\r\n#   endif\r\n# endif\r\n# else\r\n#  define SIBR_ASSETS_EXPORT\r\n# endif\r\n\r\nnamespace sibr\r\n{\r\n\t/**\r\n\t* Utility that converts an integer id to a string using\r\n\t* the \"most used\" format.\r\n\t*       \\param id the id to convert (fi 7)\r\n\t*       \\return the corresponding string (fi \"0000007\")\r\n\t* \\ingroup sibr_assets\r\n\t*/\r\n\tinline std::string\t\timageIdToString( int id ) {\r\n\t\tstd::ostringstream oss;\r\n\t\toss << std::setfill('0') << std::setw(8) << id;\r\n\t\treturn oss.str();\r\n\t}\r\n\r\n\t/** Generate a string representation of an integer, padded with zeros.\r\n\t * \\param id the integer\r\n\t * \\return the padded string\r\n\t * \\note The template int value determines the padding count.\r\n\t * \\ingroup sibr_assets\r\n\t * */\r\n\ttemplate<unsigned int N> std::string intToString(int id) {\r\n\t\tstd::ostringstream oss;\r\n\t\toss << std::setfill('0') << std::setw(N) << id;\r\n\t\treturn oss.str();\r\n\t}\r\n\r\n\t/**\r\n\t* Get the default path and filename used for the proxy\r\n\t* mesh.\r\n\t* \\param datasetPath the base path\r\n\t* \\return the mesh path\r\n\t* \\ingroup sibr_assets\r\n\t*/\r\n\tinline std::string\t\tgetProxyFilename( const std::string& datasetPath ) {\r\n\t\treturn datasetPath + \"/pmvs/models/pmvs_recon.ply\";\r\n\t}\r\n\r\n\t/**\r\n\t * Loading status for streaming.\r\n\t* \\todo Rename the following status into: NotLoaded, CPULoading, CPUReady, GPUReady, Failure.\r\n\t* \\ingroup sibr_assets\r\n\t*/\r\n\tnamespace LoadingStatus\r\n\t{\r\n\t\tenum\tEnum\r\n\t\t{\r\n\t\t\tNotLoaded = 0,\r\n\t\t\tInProgress,\r\n\t\t\tCPUReady,\r\n\t\t\tSuccessful,\r\n\t\t\tFailure,\r\n\r\n\t\t\tCount\r\n\t\t};\r\n\t} // namespace LoadingStatus\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/IFileLoader.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/assets/Config.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\t/** General file loading interface.\r\n\t\\ingroup sibr_assets\r\n\t*/\r\n\tclass SIBR_ASSETS_EXPORT IFileLoader\r\n\t{\r\n\tpublic:\r\n\r\n\t\t/** Destructor. */\r\n\t\tvirtual ~IFileLoader( void ) { }\r\n\r\n\t\t/** Load the file content from disk.\r\n\t\t\\param filename path to the file\r\n\t\t\\param verbose display information\r\n\t\t\\return a boolean denoting success\r\n\t\t*/\r\n\t\tvirtual bool load( const std::string& filename, bool verbose = true ) = 0;\r\n\t};\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/ImageListFile.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n# include <boost/filesystem.hpp>\r\n# include <fstream>\r\n# include \"core/assets/ImageListFile.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\tbool ImageListFile::load( const std::string& filename, bool verbose )\r\n\t{\r\n\r\n\t\tstd::fstream\tfile(filename, std::ios::in);\r\n\r\n\t\t_infos.clear();\r\n\t\tif (file)\r\n\t\t{\r\n\t\t\twhile (file.eof() == false)\r\n\t\t\t{\r\n\t\t\t\tInfos i;\r\n\t\t\t\tfile >> i.filename >> i.width >> i.height;\r\n\t\t\t\tif (i.filename.size())\r\n\t\t\t\t\t_infos.emplace_back(std::move(i));\r\n\t\t\t}\r\n\r\n\t\t\t// store basename\r\n\t\t\tboost::filesystem::path path(filename);\r\n\t\t\t_basename = path.parent_path().string();\r\n\r\n\t\t\tif( verbose )\r\n\t\t\t\tSIBR_FLOG << \"'\"<< filename <<\"' successfully loaded.\" << std::endl;\r\n\t\t\t\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse\r\n\t\t\tSIBR_WRG << \"file not found: '\"<<filename<<\"'\"<<std::endl;\r\n\t\treturn false;\r\n\t}\r\n\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/ImageListFile.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n\n# include \"core/graphics/Image.hpp\"\n# include \"core/assets/Config.hpp\"\n# include \"core/assets/IFileLoader.hpp\"\n# include \"core/assets/ActiveImageFile.hpp\"\n\nnamespace sibr\n{\n\t/** Represents a list of input images.\n\t\\ingroup sibr_assets\n\t*/\n\tclass SIBR_ASSETS_EXPORT ImageListFile : public IFileLoader\n\t{\n\tpublic:\n\n\t\t/** Image infos. */\n\t\tstruct Infos\n\t\t{\n\t\t\tstd::string\t\tfilename; ///< image filename.\n\t\t\tuint\t\t\tcamId; ///< Associated camera ID.\n\t\t\tuint\t\t\twidth; ///< Image width.\n\t\t\tuint\t\t\theight; ///< Image height.\n\t\t};\n\n\tpublic:\n\n\t\t/** Load the list file from disk.\n\t\t\\param filename path to the list file\n\t\t\\param verbose display information\n\t\t\\return a boolean denoting success\n\t\t*/\n\t\tbool load( const std::string& filename , bool verbose = true);\n\n\t\t/** Images information.\n\t\t * \\return each image infos.\n\t\t */\n\t\tconst std::vector<Infos>&\tinfos( void ) const { return _infos; }\n\n\t\t/** Image absename.\n\t\t *\\return the basename\n\t\t **/\n\t\tconst std::string&\t\t\tbasename( void ) const { return _basename; }\n\n\t\t/** Load images.\n\t\t\t\\return the loaded images\n\t\t*/\n\t\ttemplate <class TImage>\n\t\tstd::vector<TImage>\t\t\tloadImages( void ) const;\n\n\t\t/** Load images, applying an active images file filter. \n\t\t\t\\param ac the active list file\n\t\t\t\\return the loaded images\n\t\t\t\\note Non-active images are present but empty.\n\t\t*/\n\t\ttemplate <class TImage>\n\t\tstd::vector<TImage>\t\t\tloadImages( const ActiveImageFile& ac) const;\n\t\t\n\n\tprivate:\n\t\tstd::vector<Infos>\t\t_infos; ///< Image infos.\n\t\tstd::string\t\t\t\t_basename; ///< Root name.\n\n\t};\n\n\t///// DEFINITIONS /////\n\n\n\ttemplate <class TImage>\n\tstd::vector<TImage>\t\t\tImageListFile::loadImages( const ActiveImageFile& ac ) const {\n\t\tstd::vector<TImage> out;\n\n\t\tSIBR_LOG << \"[ImageListFile] loading images\";\n\t\tout.resize(_infos.size());\n\t\tif (_infos.empty() == false)\n\t\t{\n\t\t\t#pragma omp parallel for\n\t\t\tfor (int i = 0; i < _infos.size(); ++i)\n\t\t\t\tif( ac.active()[i] )\n\t\t\t\t\tout[i].load(_basename + \"/\" + _infos.at(i).filename, false);\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"cannot load images (ImageListFile is empty. Did you use ImageListFile::load(...) before ?\";\n\n\t\tstd::cout << std::endl;\n\t\treturn out;\n\t}\n\n\ttemplate <class TImage>\n\tstd::vector<TImage>\t\t\tImageListFile::loadImages( void ) const {\n\t\tstd::vector<TImage> out;\n\n\t\tstd::cerr << \"[ImageListFile] loading images\";\n\t\tout.resize(_infos.size());\n\t\tif (_infos.empty() == false)\n\t\t{\n\t\t\t#pragma omp parallel for\n\t\t\tfor (int i = 0; i < _infos.size(); ++i)\n\t\t\t\tout[i].load(_basename + \"/\" + _infos.at(i).filename, false);\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"cannot load images (ImageListFile is empty. Did you use ImageListFile::load(...) before ?\";\n\n\t\treturn out;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/InputCamera.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"core/assets/ActiveImageFile.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include <boost/algorithm/string.hpp>\n#include <map>\n#include \"core/system/String.hpp\"\n#include \"picojson/picojson.hpp\"\n\n\n// Colmap binary stuff\n#include \"colmapheader.h\"\ntypedef uint32_t image_t;\ntypedef uint32_t camera_t;\ntypedef uint64_t point3D_t;\ntypedef uint32_t point2D_t;\n\n#define SIBR_INPUTCAMERA_BINARYFILE_VERSION 10\n#define IBRVIEW_TOPVIEW_SAVEVERSION \"version002\"\n#define FOCAL_X_UNDEFINED -1\n\nnamespace sibr\n{\n\tInputCamera::InputCamera(float f, float k1, float k2, int w, int h, int id) :\n\t\t_focal(f), _k1(k1), _k2(k2), _w(w), _h(h), _id(id), _active(true), _name(\"\"), _focalx(FOCAL_X_UNDEFINED)\n\t{\n\t\t// Update fov and aspect ratio.\n\t\tfloat fov = 2.0f * atan(0.5f * h / f);\n\t\tfloat aspect = float(w) / float(h);\n\n\t\tCamera::aspect(aspect);\n\t\tCamera::fovy(fov);\n\n\t\t_id = id;\n\t}\n\n\tInputCamera::InputCamera(float fy, float fx, float k1, float k2, int w, int h, int id) :\n\t\t_focal(fy), _k1(k1), _k2(k2), _w(w), _h(h), _id(id), _active(true), _name(\"\"), _focalx(fx)\n\t{\n\t\t// Update fov and aspect ratio.\n\t\tfloat fovY = 2.0f * atan(0.5f * h / fy);\n\t\tfloat fovX = 2.0f * atan(0.5f * w / fx);\n\n\t\tCamera::aspect(tan(fovX / 2) / tan(fovY / 2));\n\t\tCamera::fovy(fovY);\n\n\t\t_id = id;\n\t}\n\n\n\tInputCamera::InputCamera(int id, int w, int h, sibr::Matrix4f m, bool active) :\n\t\t_active(active)\n\t{\n\t\tVector3f t;\n\t\tfloat r[9];\n\n\t\tfor (int i = 0; i < 9; i++)  r[i] = m(3 + i);\n\t\tfor (int i = 0; i < 3; i++)  t[i] = m(12 + i);\n\n\t\t_w = w;\n\t\t_h = h;\n\n\t\t_focal = m(0);\n\t\t_focalx = FOCAL_X_UNDEFINED;\n\t\t_k1 = m(1);\n\t\t_k2 = m(2);\n\n\t\tfloat fov = 2.0f * atan(0.5f * h / m(0));\n\t\tfloat aspect = float(w) / float(h);\n\n\t\tsibr::Matrix3f\t\tmatRotation;\n\t\tmatRotation <<\n\t\t\tr[0], r[1], r[2],\n\t\t\tr[3], r[4], r[5],\n\t\t\tr[6], r[7], r[8]\n\t\t\t;\n\n\t\tCamera::aspect(aspect);\n\t\tCamera::fovy(fov);\n\n\t\t// http://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html#S6\n\t\t// Do pos = -R' * t\n\t\tconst sibr::Matrix3f orientation = matRotation.transpose();\n\t\tsibr::Vector3f position = -orientation * t;\n\t\tCamera::position(position);\n\t\tCamera::rotation(Quaternionf(orientation));\n\n\n\t\tCamera::principalPoint({ 0.5f, 0.5f });\n\n\n\t\t_id = id;\n\t\t_name = \"\";\n\t}\n\n\n\n\tInputCamera::InputCamera(int id, int w, int h, sibr::Vector3f& position, sibr::Matrix3f& orientation, float focal, float k1, float k2, bool active) :\n\t\t_active(active)\n\t{\n\n\n\t\t_w = w;\n\t\t_h = h;\n\n\t\t_focal = focal;\n\t\t_focalx = FOCAL_X_UNDEFINED;\n\t\t_k1 = k1;\n\t\t_k2 = k2;\n\n\t\tfloat fov = 2.0f * atan(0.5f * h / _focal);\n\t\tfloat aspect = float(w) / float(h);\n\n\n\n\t\tCamera::aspect(aspect);\n\t\tCamera::fovy(fov);\n\n\t\t// http://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html#S6\n\t\t// Do pos = -R' * t\n\n\t\tCamera::position(position);\n\t\tCamera::rotation(Quaternionf(orientation));\n\n\t\t_id = id;\n\t\t_name = \"\";\n\t}\n\n\tInputCamera::InputCamera(const Camera& c, int w, int h) : Camera(c) {\n\t\t_focal = 1.0f / (tan(0.5f * fovy()) * 2.0f / float(h));\n\t\t_focalx = FOCAL_X_UNDEFINED;\n\t\t_k1 = _k2 = 0;\n\t\t_w = w;\n\t\t_h = h;\n\t\t_id = 0;\n\t\t_name = \"\";\n\t\t_active = true;\n\t\taspect(float(_w) / float(_h));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tvoid InputCamera::size(uint w, uint h) { _w = w; _h = h; }\n\tuint InputCamera::w(void)  const { return _w; }\n\tuint InputCamera::h(void)  const { return _h; }\n\tbool InputCamera::isActive(void)  const { return _active; }\n\n\t/* compatibility for preprocess (depth) */\n\n\n\tVector3f InputCamera::projectScreen(const Vector3f& pt) const {\n\t\tVector3f proj_pt = project(pt);\n\t\tVector3f screen_pt((proj_pt[0] + 1.f) * _w / 2.0f, (1.f - proj_pt[1]) * _h / 2.0f, proj_pt[2] * 0.5f + 0.5f);\n\n\t\treturn screen_pt;\n\t}\n\n\tfloat InputCamera::focal() const { return _focal; };\n\tfloat InputCamera::focalx() const { return _focalx; };\n\tfloat InputCamera::k1() const { return _k1; };\n\tfloat InputCamera::k2() const { return _k2; };\n\n\tInputCamera InputCamera::resizedH(int h) const {\n\n\t\tint w = int(_aspect * h);\n\n\t\tfloat sibr_focal = h * _focal / _h;\n\t\tfloat k1 = _k1;\n\t\tfloat k2 = _k2;\n\t\tint id = _id;\n\n\t\tsibr::Matrix4f m;\n\n\t\tsibr::InputCamera cam(sibr_focal, k1, k2, w, h, id);\n\n\t\tcam.rotation(rotation());\n\t\tcam.position(position());\n\n\t\tcam.znear(znear());\n\t\tcam.zfar(zfar());\n\t\tcam.name(name());\n\n\t\treturn cam;\n\t}\n\n\tInputCamera InputCamera::resizedW(int w) const {\n\n\t\tint h = int(float(w) / _aspect);\n\n\t\tfloat sibr_focal = h * _focal / _h;\n\t\tfloat k1 = _k1;\n\t\tfloat k2 = _k2;\n\t\tint id = _id;\n\n\t\tsibr::Matrix4f m;\n\n\t\tsibr::InputCamera cam(sibr_focal, k1, k2, w, h, id);\n\n\t\tcam.rotation(rotation());\n\t\tcam.position(position());\n\n\t\tcam.znear(znear());\n\t\tcam.zfar(zfar());\n\t\tcam.name(name());\n\n\t\treturn cam;\n\t}\n\n\n\n\tstd::vector<InputCamera::Ptr> InputCamera::load(const std::string& datasetPath, float zNear, float zFar, const std::string& bundleName, const std::string& listName)\n\t{\n\t\tconst std::string bundlerFile = datasetPath + \"/cameras/\" + bundleName;\n\t\tconst std::string listFile = datasetPath + \"/images/\" + listName;\n\t\tconst std::string clipFile = datasetPath + \"/clipping_planes.txt\";\n\n\t\t// Loading clipping planes if they are available.\n\t\tSIBR_LOG << \"Loading clipping planes from \" << clipFile << std::endl;\n\n\t\tstruct Z {\n\t\t\tZ() {}\n\t\t\tZ(float f, float n) : far(f), near(n) {}\n\t\t\tfloat far;\n\t\t\tfloat near;\n\t\t};\n\t\tstd::vector<Z> nearsFars;\n\n\t\t{ // Load znear & zfar for unprojecting depth samples\n\n\t\t\tfloat z;\n\t\t\tstd::ifstream zfile(clipFile);\n\t\t\t// During preprocessing clipping planes are not yet defined\n\t\t\t// the preprocess utility \"depth\" defines this\n\t\t\t// SIBR_ASSERT(zfile.is_open());\n\t\t\tif (zfile.is_open()) {\n\t\t\t\tint num_z_values = 0;\n\t\t\t\twhile (zfile >> z) {\n\t\t\t\t\tif (num_z_values % 2 == 0) {\n\t\t\t\t\t\tnearsFars.push_back(Z());\n\t\t\t\t\t\tnearsFars[nearsFars.size() - 1].near = z;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tnearsFars[nearsFars.size() - 1].far = z;\n\t\t\t\t\t}\n\t\t\t\t\t++num_z_values;\n\t\t\t\t}\n\n\t\t\t\tif (num_z_values > 0 && num_z_values % 2 != 0) {\n\t\t\t\t\tnearsFars.resize(nearsFars.size() - 1);\n\t\t\t\t}\n\n\t\t\t\tif (nearsFars.size() == 0) {\n\t\t\t\t\tSIBR_WRG << \" Could not extract at leat 2 clipping planes from '\" << clipFile << \"' .\" << std::endl;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tSIBR_WRG << \"Cannot open '\" << clipFile << \"' (not clipping plane loaded).\" << std::endl;\n\t\t\t}\n\n\t\t}\n\n\t\t// Load cameras and images infos.\n\t\tSIBR_LOG << \"Loading input cameras.\" << std::endl;\n\t\tauto cameras = InputCamera::loadBundle(bundlerFile, zNear, zFar, listFile);\n\n\t\tif (!nearsFars.empty()) {\n\t\t\tfor (int cid = 0; cid < cameras.size(); ++cid) {\n\t\t\t\tconst int zid = std::min(cid, int(nearsFars.size()) - 1);\n\t\t\t\tcameras[cid]->znear(nearsFars[zid].near);\n\t\t\t\tcameras[cid]->zfar(nearsFars[zid].far);\n\t\t\t}\n\t\t}\n\n\t\t// Load active images\n\t\tActiveImageFile activeImageFile;\n\t\tactiveImageFile.setNumImages((int)cameras.size());\n\t\t// load active image file and set (in)active\n\t\tif (activeImageFile.load(datasetPath + \"/active_images.txt\", false)) {\n\t\t\tfor (int i = 0; i < (int)cameras.size(); i++) {\n\t\t\t\tif (!activeImageFile.active()[i])\n\t\t\t\t\tcameras[i]->setActive(false);\n\t\t\t}\n\t\t}\n\n\t\t// Load excluded images\n\t\tActiveImageFile excludeImageFile;\n\t\texcludeImageFile.setNumImages((int)cameras.size());\n\t\t// load exclude image file and set *in*active\n\t\tif (excludeImageFile.load(datasetPath + \"/exclude_images.txt\", false)) {\n\t\t\tfor (int i = 0; i < (int)cameras.size(); i++) {\n\t\t\t\t// Attn (GD): invert the meaning of active for exclude:\n\t\t\t\t// only file numbers explicitly in exclude_images are set\n\t\t\t\t// to active, and these are the only ones we set to *inactive*\n\t\t\t\t// should really create a separate class or have a flag \"invert\"\n\t\t\t\tif (excludeImageFile.active()[i])\n\t\t\t\t\tcameras[i]->setActive(false);\n\t\t\t}\n\t\t}\n\t\treturn cameras;\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadNVM(const std::string& nvmPath, float zNear, float zFar, std::vector<sibr::Vector2u> wh)\n\t{\n\t\tstd::ifstream in(nvmPath);\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tif (in.is_open())\n\t\t{\n\t\t\tint rotation_parameter_num = 4;\n\t\t\tbool format_r9t = false;\n\t\t\tstd::string token;\n\t\t\tif (in.peek() == 'N')\n\t\t\t{\n\t\t\t\tin >> token; //file header\n\t\t\t\tif (strstr(token.c_str(), \"R9T\"))\n\t\t\t\t{\n\t\t\t\t\trotation_parameter_num = 9;    //rotation as 3x3 matrix\n\t\t\t\t\tformat_r9t = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint ncam = 0, npoint = 0, nproj = 0;\n\t\t\t// read # of cameras\n\t\t\tin >> ncam;  if (ncam <= 1) return std::vector<InputCamera::Ptr>();\n\n\t\t\t//read the camera parameters\n\n\t\t\tstd::function<Eigen::Matrix3f(const double[9])> matrix = [](const double q[9])\n\t\t\t{\n\n\t\t\t\tEigen::Matrix3f m;\n\t\t\t\tdouble qq = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);\n\t\t\t\tdouble qw, qx, qy, qz;\n\t\t\t\tif (qq > 0)\n\t\t\t\t{\n\t\t\t\t\tqw = q[0] / qq;\n\t\t\t\t\tqx = q[1] / qq;\n\t\t\t\t\tqy = q[2] / qq;\n\t\t\t\t\tqz = q[3] / qq;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tqw = 1;\n\t\t\t\t\tqx = qy = qz = 0;\n\t\t\t\t}\n\t\t\t\tm(0, 0) = float(qw * qw + qx * qx - qz * qz - qy * qy);\n\t\t\t\tm(0, 1) = float(2 * qx * qy - 2 * qz * qw);\n\t\t\t\tm(0, 2) = float(2 * qy * qw + 2 * qz * qx);\n\t\t\t\tm(1, 0) = float(2 * qx * qy + 2 * qw * qz);\n\t\t\t\tm(1, 1) = float(qy * qy + qw * qw - qz * qz - qx * qx);\n\t\t\t\tm(1, 2) = float(2 * qz * qy - 2 * qx * qw);\n\t\t\t\tm(2, 0) = float(2 * qx * qz - 2 * qy * qw);\n\t\t\t\tm(2, 1) = float(2 * qy * qz + 2 * qw * qx);\n\t\t\t\tm(2, 2) = float(qz * qz + qw * qw - qy * qy - qx * qx);\n\n\t\t\t\treturn m;\n\t\t\t};\n\n\t\t\tfor (int i = 0; i < ncam; ++i)\n\t\t\t{\n\t\t\t\tdouble f, q[9], c[3], d[2];\n\t\t\t\tin >> token >> f;\n\t\t\t\tfor (int j = 0; j < rotation_parameter_num; ++j) in >> q[j];\n\t\t\t\tin >> c[0] >> c[1] >> c[2] >> d[0] >> d[1];\n\n\t\t\t\tstd::string     image_path = sibr::parentDirectory(nvmPath) + \"/\" + token;\n\t\t\t\tsibr::Vector2i\tresolution = sibr::IImage::imageResolution(image_path);\n\n\t\t\t\tif (resolution.x() < 0 || resolution.y() < 0)\n\t\t\t\t{\n\t\t\t\t\tstd::cerr << \"Could not get resolution for input image: \" << image_path << std::endl;\n\t\t\t\t\treturn std::vector<InputCamera::Ptr>();\n\t\t\t\t}\n\n\t\t\t\tint wIm = 1, hIm = 1;\n\t\t\t\tif (ncam == wh.size()) {\n\t\t\t\t\twIm = wh[i].x();\n\t\t\t\t\thIm = wh[i].y();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\twIm = resolution.x();\n\t\t\t\t\thIm = resolution.y();\n\t\t\t\t}\n\n\t\t\t\t//camera_data[i].SetFocalLength(f);\n\t\t\t\tcameras.emplace_back(new InputCamera((float)f, (float)d[0], (float)d[1], wIm, hIm, i));\n\n\t\t\t\tfloat fov = 2.0f * atan(0.5f * hIm / (float)f);\n\t\t\t\tfloat aspect = float(wIm) / float(hIm);\n\t\t\t\tcameras[i]->aspect(aspect);\n\t\t\t\tcameras[i]->fovy(fov);\n\n\t\t\t\t//translation\n\t\t\t\tVector3f posCam((float)c[0], (float)c[1], (float)c[2]);\n\n\t\t\t\tif (format_r9t)\n\t\t\t\t{\n\n\t\t\t\t\tstd::cout << \" WARNING THIS PART OF THE CODE WAS NEVER TESTED. IT IS SUPPOSED NOT TO WORK PROPERLY\" << std::endl;\n\t\t\t\t\tEigen::Matrix3f\t\tmatRotation;\n\t\t\t\t\tmatRotation <<\n\t\t\t\t\t\tfloat(q[0]), float(q[1]), float(q[2]),\n\t\t\t\t\t\tfloat(q[3]), float(q[4]), float(q[5]),\n\t\t\t\t\t\tfloat(q[6]), float(q[7]), float(q[8])\n\t\t\t\t\t\t;\n\t\t\t\t\tmatRotation.transposeInPlace();\n\n\n\t\t\t\t\tcameras[i]->position(posCam);\n\t\t\t\t\tcameras[i]->rotation(Quaternionf(matRotation));\n\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\n\t\t\t\t\tEigen::Matrix3f converter;\n\t\t\t\t\tconverter <<\n\t\t\t\t\t\t1, 0, 0,\n\t\t\t\t\t\t0, -1, 0,\n\t\t\t\t\t\t0, 0, -1;\n\t\t\t\t\t//older format for compability\n\t\t\t\t\tQuaternionf quat((float)q[0], (float)q[1], (float)q[2], (float)q[3]);\n\t\t\t\t\tEigen::Matrix3f\tmatRotation = converter.transpose() * quat.toRotationMatrix();\n\t\t\t\t\tmatRotation.transposeInPlace();\n\n\t\t\t\t\tcameras[i]->position(posCam);\n\t\t\t\t\tcameras[i]->rotation(Quaternionf(matRotation));\n\n\t\t\t\t}\n\t\t\t\t//camera_data[i].SetNormalizedMeasurementDistortion(d[0]);\n\t\t\t\tcameras[i]->name(token);\n\t\t\t}\n\t\t\tstd::cout << ncam << \" cameras; \" << npoint << \" 3D points; \" << nproj << \" projections\\n\";\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \"Cannot open '\" << nvmPath << std::endl;\n\t\t}\n\n\t\treturn cameras;\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadLookat(const std::string& lookatPath, const std::vector<sibr::Vector2u>& wh, float znear, float zfar)\n\t{\n\n\t\tstd::ifstream in(lookatPath);\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tif (in.is_open())\n\t\t{\n\t\t\tint i = 0;\n\t\t\tfor (std::string line; safeGetline(in, line); i++)\n\t\t\t{\n\t\t\t\tint w = 1024, h = 768;\n\t\t\t\tif (wh.size() > 0) {\n\t\t\t\t\tint whI = std::min(i, (int)wh.size() - 1);\n\t\t\t\t\tw = wh[whI].x();\n\t\t\t\t\th = wh[whI].y();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tstd::cout << \"Warning default image size of 1024*768 is supposed for camera\" << std::endl;\n\t\t\t\t}\n\n\t\t\t\tbool use_fovx = false;\n\t\t\t\tstd::string camName = line.substr(0, line.find(\" \"));\n\t\t\t\tsize_t originPos = line.find(\"-D origin=\") + 10;\n\t\t\t\tsize_t targetPos = line.find(\"-D target=\") + 10;\n\t\t\t\tsize_t upPos = line.find(\"-D up=\") + 6;\n\t\t\t\tsize_t fovPos = line.find(\"-D fovy=\") + 8;\n\t\t\t\tint delta_fov = 9;\n\t\t\t\tif (fovPos < 8) {\n\t\t\t\t\tstd::cout << \"Warning: Fovy not found, backing to Fovx mode\" << std::endl;\n\t\t\t\t\tfovPos = line.find(\"-D fov=\") + 7;\n\t\t\t\t\tuse_fovx = true;\n\t\t\t\t\tdelta_fov = 8;\n\t\t\t\t}\n\t\t\t\tsize_t clipPos = line.find(\"-D clip=\") + 8;\n\t\t\t\tsize_t aspectPos = line.find(\"-D aspect=\") + 10;\n\t\t\t\tsize_t endPos = line.size();\n\n\t\t\t\tstd::string originStr = line.substr(originPos, targetPos - originPos - 11);\n\t\t\t\tstd::string targetStr = line.substr(targetPos, upPos - targetPos - 7);\n\t\t\t\tstd::string upStr = line.substr(upPos, fovPos - upPos - delta_fov);\n\t\t\t\tstd::string fovStr = line.substr(fovPos, clipPos - fovPos - 9);\n\t\t\t\tstd::string clipStr = line.substr(clipPos, endPos - clipPos);\n\n\t\t\t\tstd::vector<std::string> vecVal;\n\t\t\t\tboost::split(vecVal, originStr, [](char c) {return c == ','; });\n\t\t\t\tVector3f Eye(std::strtof(vecVal[0].c_str(), 0), std::strtof(vecVal[1].c_str(), 0), std::strtof(vecVal[2].c_str(), 0));\n\t\t\t\tboost::split(vecVal, targetStr, [](char c) {return c == ','; });\n\t\t\t\tVector3f At(std::strtof(vecVal[0].c_str(), 0), std::strtof(vecVal[1].c_str(), 0), std::strtof(vecVal[2].c_str(), 0));\n\n\t\t\t\tboost::split(vecVal, upStr, [](char c) {return c == ','; });\n\t\t\t\tVector3f Up(std::strtof(vecVal[0].c_str(), 0), std::strtof(vecVal[1].c_str(), 0), std::strtof(vecVal[2].c_str(), 0));\n\n\t\t\t\tfloat fov = std::strtof(fovStr.c_str(), 0);\n\n\t\t\t\tboost::split(vecVal, clipStr, [](char c) {return c == ','; });\n\t\t\t\tVector2f clip(std::strtof(vecVal[0].c_str(), 0), std::strtof(vecVal[1].c_str(), 0));\n\n\t\t\t\tVector3f zAxis((Eye - At).normalized());\n\t\t\t\tVector3f xAxis((Up.cross(zAxis)).normalized());\n\t\t\t\tVector3f yAxis(zAxis.cross(xAxis));\n\n\t\t\t\tVector3f transl(-Eye.dot(xAxis), -Eye.dot(yAxis), -Eye.dot(zAxis));\n\n\t\t\t\tEigen::Matrix3f rotation;\n\t\t\t\trotation << xAxis, yAxis, zAxis;\n\t\t\t\trotation.transposeInPlace();\n\n\t\t\t\tEigen::Matrix4f mLook;\n\t\t\t\tmLook.setZero();\n\t\t\t\tmLook.block<3, 3>(0, 0) = rotation;\n\t\t\t\tmLook.block<3, 1>(0, 3) = transl;\n\t\t\t\tmLook(3, 3) = 1;\n\n\t\t\t\tfloat fovRad = fov * float(M_PI) / 180;\n\t\t\t\tfloat sibr_focal = 0.5f * h / tan(fovRad / 2.0f); //Lookat file contain the vertical field of view now\n\t\t\t\tif (use_fovx) {\n\t\t\t\t\tsibr_focal = 0.5f * w / tan(fovRad / 2.0f); //Lookat file contain the vertical field of view now\n\t\t\t\t}\n\n\t\t\t\tEigen::Matrix4f r(mLook);\n\t\t\t\t/*float m[15] = {\n\t\t\t\t\tsibr_focal,0,0,\n\t\t\t\t\tr(0,0),r(0,1),r(0,2),\n\t\t\t\t\tr(1,0),r(1,1),r(1,2),\n\t\t\t\t\tr(2,0),r(2,1),r(2,2),\n\t\t\t\t\tr(0,3),r(1,3),r(2,3)\n\t\t\t\t};\n\t\t\t\t*/\n\t\t\t\tEigen::Matrix4f m;\n\t\t\t\tm(0) = sibr_focal;  m(1) = 0; m(2) = 0;\n\t\t\t\tm(3) = r(0, 0); m(4) = r(0, 1); m(5) = r(0, 2);\n\t\t\t\tm(6) = r(1, 0); m(7) = r(1, 1); m(8) = r(1, 2);\n\t\t\t\tm(9) = r(2, 0); m(10) = r(2, 1); m(11) = r(2, 2);\n\t\t\t\tm(12) = r(0, 3); m(13) = r(1, 3); m(14) = r(2, 3);\n\n\n\t\t\t\tbool isActive = true;\n\n\t\t\t\tcameras.emplace_back(new InputCamera((int)cameras.size(), w, h, m, isActive));\n\n\t\t\t\tif (znear > 0) {\n\t\t\t\t\tcameras[i]->znear(znear);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcameras[i]->znear(clip.x());\n\t\t\t\t}\n\t\t\t\tif (zfar > 0) {\n\t\t\t\t\tcameras[i]->zfar(zfar);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcameras[i]->zfar(clip.y());\n\t\t\t\t}\n\t\t\t\tcameras[i]->name(camName);\n\t\t\t}\n\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \"Cannot open '\" << lookatPath << std::endl;\n\t\t}\n\n\t\treturn cameras;\n\n\t}\n\n\tstd::string InputCamera::lookatString() const {\n\t\tstd::string infos = std::string(\" -D origin=\") +\n\t\t\tstd::to_string(position()[0]) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(position()[1]) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(position()[2]) +\n\t\t\tstd::string(\" -D target=\") +\n\t\t\tstd::to_string(position()[0] +\n\t\t\t\tdir()[0]) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(position()[1] +\n\t\t\t\tdir()[1]) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(position()[2] +\n\t\t\t\tdir()[2]) +\n\t\t\tstd::string(\" -D up=\") +\n\t\t\tstd::to_string(up()[0]) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(up()[1]) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(up()[2]) +\n\t\t\tstd::string(\" -D fovy=\") +\n\t\t\tstd::to_string(180 * fovy() / M_PI) +\n\t\t\tstd::string(\" -D clip=\") +\n\t\t\tstd::to_string(znear()) +\n\t\t\tstd::string(\",\") +\n\t\t\tstd::to_string(zfar()) +\n\t\t\tstd::string(\"\\n\");\n\t\treturn infos;\n\t}\n\n\tvoid InputCamera::saveAsLookat(const std::vector<InputCamera::Ptr>& cams, const std::string& fileName) {\n\n\t\tstd::ofstream fileRender(fileName, std::ios::out | std::ios::trunc);\n\t\tfor (const auto& cam : cams) {\n\n\t\t\tfileRender << cam->name() << cam->lookatString();\n\t\t}\n\n\t\tfileRender.close();\n\t}\n\n\tvoid InputCamera::saveImageSizes(const std::vector<InputCamera::Ptr>& cams, const std::string& fileName) {\n\n\t\tstd::ofstream fileRender(fileName, std::ios::out | std::ios::trunc);\n\t\tfor (const auto& cam : cams) {\n\n\t\t\tfileRender << cam->w() << \"x\" << cam->h() << \"\\n\";\n\t\t}\n\n\t\tfileRender.close();\n\t}\n\n\n\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadColmap(const std::string& colmapSparsePath, const float zNear, const float zFar, const int fovXfovYFlag)\n\t{\n\t\tconst std::string camerasListing = colmapSparsePath + \"/cameras.txt\";\n\t\tconst std::string imagesListing = colmapSparsePath + \"/images.txt\";\n\n\t\tconst std::string camerasListing2 = colmapSparsePath + \"/cameras.txt2\";\n\t\tconst std::string imagesListing2 = colmapSparsePath + \"/images.txt2\";\n\n\t\tstd::ifstream camerasFile(camerasListing);\n\t\tstd::ifstream imagesFile(imagesListing);\n\t\tstd::ofstream camerasFile2(camerasListing2);\n\t\tstd::ofstream imagesFile2(imagesListing2);\n\t\tif (!camerasFile.is_open()) {\n\t\t\tSIBR_ERR << \"Unable to load camera colmap file\" << std::endl;\n\t\t}\n\t\tif (!imagesFile.is_open()) {\n\t\t\tSIBR_WRG << \"Unable to load images colmap file\" << std::endl;\n\t\t}\n\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tstd::string line;\n\n\t\tstruct CameraParametersColmap {\n\t\t\tsize_t id;\n\t\t\tsize_t width;\n\t\t\tsize_t height;\n\t\t\tfloat  fx;\n\t\t\tfloat  fy;\n\t\t\tfloat  dx;\n\t\t\tfloat  dy;\n\t\t};\n\n\t\tstd::map<size_t, CameraParametersColmap> cameraParameters;\n\n\t\tstd::map<int, std::vector<std::string>> camidtokens;\n\n\t\twhile (safeGetline(camerasFile, line)) {\n\t\t\tif (line.empty() || line[0] == '#') {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tstd::vector<std::string> tokens = sibr::split(line, ' ');\n\t\t\tif (tokens.size() < 8) {\n\t\t\t\tSIBR_WRG << \"Unknown line.\" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (tokens[1] != \"PINHOLE\" && tokens[1] != \"OPENCV\") {\n\t\t\t\tSIBR_WRG << \"Unknown camera type.\" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tCameraParametersColmap params;\n\t\t\tparams.id = std::stol(tokens[0]);\n\t\t\tparams.width = std::stol(tokens[2]);\n\t\t\tparams.height = std::stol(tokens[3]);\n\t\t\tparams.fx = std::stof(tokens[4]);\n\t\t\tparams.fy = std::stof(tokens[5]);\n\t\t\tparams.dx = std::stof(tokens[6]);\n\t\t\tparams.dy = std::stof(tokens[7]);\n\n\t\t\tcameraParameters[params.id] = params;\n\n\t\t\tcamidtokens[params.id] = tokens;\n\t\t}\n\n\t\t// Now load the individual images and their extrinsic parameters\n\t\tsibr::Matrix3f converter;\n\t\tconverter << 1, 0, 0,\n\t\t\t0, -1, 0,\n\t\t\t0, 0, -1;\n\n\t\tint camid = 0;\n\t\tint valid = 0;\n\t\twhile (safeGetline(imagesFile, line)) {\n\t\t\tif (line.empty() || line[0] == '#') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstd::vector<std::string> tokens = sibr::split(line, ' ');\n\t\t\tif (tokens.size() < 10) {\n\t\t\t\tSIBR_WRG << \"Unknown line.\" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tuint\t\tcId = std::stoi(tokens[0]) - 1;\n\t\t\tfloat       qw = std::stof(tokens[1]);\n\t\t\tfloat       qx = std::stof(tokens[2]);\n\t\t\tfloat       qy = std::stof(tokens[3]);\n\t\t\tfloat       qz = std::stof(tokens[4]);\n\t\t\tfloat       tx = std::stof(tokens[5]);\n\t\t\tfloat       ty = std::stof(tokens[6]);\n\t\t\tfloat       tz = std::stof(tokens[7]);\n\t\t\tsize_t      id = std::stol(tokens[8]);\n\n\t\t\tstd::string imageName = tokens[9];\n\n\t\t\tif (cameraParameters.find(id) == cameraParameters.end())\n\t\t\t{\n\t\t\t\tSIBR_ERR << \"Could not find intrinsics for image: \"\n\t\t\t\t\t<< tokens[9] << std::endl;\n\t\t\t}\n\t\t\tconst CameraParametersColmap& camParams = cameraParameters[id];\n\n\t\t\tconst sibr::Quaternionf quat(qw, qx, qy, qz);\n\t\t\tconst sibr::Matrix3f orientation = quat.toRotationMatrix().transpose() * converter;\n\t\t\tsibr::Vector3f translation(tx, ty, tz);\n\n\t\t\tsibr::Vector3f position = -(orientation * converter * translation);\n\n\t\t\tsibr::InputCamera::Ptr camera;\n\t\t\tif (fovXfovYFlag) {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(camParams.fy, camParams.fx, 0.0f, 0.0f, int(camParams.width), int(camParams.height), int(cId)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(camParams.fy, 0.0f, 0.0f, int(camParams.width), int(camParams.height), int(cId)));\n\t\t\t}\n\n\t\t\tcamera->name(imageName);\n\t\t\tcamera->position(position);\n\t\t\tcamera->rotation(sibr::Quaternionf(orientation));\n\t\t\tcamera->znear(zNear);\n\t\t\tcamera->zfar(zFar);\n\n\t\t\tif (camera->position().x() < 0)\n\t\t\t{\n\t\t\t\tcamerasFile2 << ++valid;\n\t\t\t\tfor (int i = 1; i < camidtokens[id].size(); i++)\n\t\t\t\t\tcamerasFile2 << \" \" << camidtokens[id][i];\n\t\t\t\tcamerasFile2 << \"\\n\\n\";\n\n\t\t\t\timagesFile2<< valid;\n\t\t\t\tfor (int i = 1; i < tokens.size() - 1; i++)\n\t\t\t\t\timagesFile2 << \" \" << tokens[i];\n\t\t\t\timagesFile2 << \" \" << valid << std::endl;\n\t\t\t\timagesFile2 << \"\\n\\n\";\n\t\t\t}\n\n\t\t\tcameras.push_back(camera);\n\n\t\t\t++camid;\n\t\t\t// Skip the observations.\n\t\t\tsafeGetline(imagesFile, line);\n\t\t}\n\n\n\t\treturn cameras;\n\t}\n\n\t// for http server\n\tstd::vector<InputCamera::Ptr> InputCamera::loadColmapFromHttp(const std::string& camera_str, const std::string& image_str, const float zNear, const float zFar, const int fovXfovYFlag)\n\t{\n\t\t// 使用传入的字符串模拟文件流\n\t\tstd::istringstream camerasFile(camera_str);\n\t\tstd::istringstream imagesFile(image_str);\n\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\t\tstd::string line;\n\n\t\tstruct CameraParametersColmap {\n\t\t\tsize_t id;\n\t\t\tsize_t width;\n\t\t\tsize_t height;\n\t\t\tfloat  fx;\n\t\t\tfloat  fy;\n\t\t\tfloat  dx;\n\t\t\tfloat  dy;\n\t\t};\n\n\t\tstd::map<size_t, CameraParametersColmap> cameraParameters;\n\t\tstd::map<int, std::vector<std::string>> camidtokens;\n\n\t\t// 解析传入的 camera_str（模拟 cameras.txt）\n\t\twhile (safeGetline(camerasFile, line)) {\n\t\t\tif (line.empty() || line[0] == '#') {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tstd::vector<std::string> tokens = sibr::split(line, ' ');\n\t\t\tif (tokens.size() < 8) {\n\t\t\t\tSIBR_WRG << \"Unknown line.\" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (tokens[1] != \"PINHOLE\" && tokens[1] != \"OPENCV\") {\n\t\t\t\tSIBR_WRG << \"Unknown camera type.\" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tCameraParametersColmap params;\n\t\t\tparams.id = std::stol(tokens[0]);\n\t\t\tparams.width = std::stol(tokens[2]);\n\t\t\tparams.height = std::stol(tokens[3]);\n\t\t\tparams.fx = std::stof(tokens[4]);\n\t\t\tparams.fy = std::stof(tokens[5]);\n\t\t\tparams.dx = std::stof(tokens[6]);\n\t\t\tparams.dy = std::stof(tokens[7]);\n\n\t\t\tcameraParameters[params.id] = params;\n\t\t\tcamidtokens[params.id] = tokens;\n\t\t}\n\n\t\t// 解析传入的 image_str（模拟 images.txt）\n\t\tsibr::Matrix3f converter;\n\t\tconverter << 1, 0, 0,\n\t\t\t\t\t0, -1, 0,\n\t\t\t\t\t0, 0, -1;\n\n\t\tint camid = 0;\n\t\tint valid = 0;\n\n\t\twhile (safeGetline(imagesFile, line)) {\n\t\t\tif (line.empty() || line[0] == '#') {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tstd::vector<std::string> tokens = sibr::split(line, ' ');\n\t\t\tif (tokens.size() < 10) {\n\t\t\t\tSIBR_WRG << \"Unknown line.\" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tuint cId = std::stoi(tokens[0]) - 1;\n\t\t\tfloat qw = std::stof(tokens[1]);\n\t\t\tfloat qx = std::stof(tokens[2]);\n\t\t\tfloat qy = std::stof(tokens[3]);\n\t\t\tfloat qz = std::stof(tokens[4]);\n\t\t\tfloat tx = std::stof(tokens[5]);\n\t\t\tfloat ty = std::stof(tokens[6]);\n\t\t\tfloat tz = std::stof(tokens[7]);\n\t\t\tsize_t id = std::stol(tokens[8]);\n\t\t\tstd::string imageName = tokens[9];\n\n\t\t\tif (cameraParameters.find(id) == cameraParameters.end()) {\n\t\t\t\tSIBR_ERR << \"Could not find intrinsics for image: \" << tokens[9] << std::endl;\n\t\t\t}\n\t\t\tconst CameraParametersColmap& camParams = cameraParameters[id];\n\n\t\t\tconst sibr::Quaternionf quat(qw, qx, qy, qz);\n\t\t\tconst sibr::Matrix3f orientation = quat.toRotationMatrix().transpose() * converter;\n\t\t\tsibr::Vector3f translation(tx, ty, tz);\n\n\t\t\tsibr::Vector3f position = -(orientation * converter * translation);\n\n\t\t\tsibr::InputCamera::Ptr camera;\n\t\t\tif (fovXfovYFlag) {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(camParams.fy, camParams.fx, 0.0f, 0.0f, int(camParams.width), int(camParams.height), int(cId)));\n\t\t\t} else {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(camParams.fy, 0.0f, 0.0f, int(camParams.width), int(camParams.height), int(cId)));\n\t\t\t}\n\n\t\t\tcamera->name(imageName);\n\t\t\tcamera->position(position);\n\t\t\tcamera->rotation(sibr::Quaternionf(orientation));\n\t\t\tcamera->znear(zNear);\n\t\t\tcamera->zfar(zFar);\n\n\t\t\tcameras.push_back(camera);\n\t\t\t++camid;\n\n\t\t\t// 跳过观察部分\n\t\t\tsafeGetline(imagesFile, line);\n\t\t}\n\n\t\treturn cameras;\n\t}\n\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadBundle(const std::string& bundlerPath, float zNear, float zFar, const std::string& listImagePath, bool path)\n\t{\n\t\tSIBR_LOG << \"Loading input cameras.\" << std::endl;\n\n\t\t// check bundler file\n\t\tstd::ifstream bundle_file(bundlerPath);\n\t\tif (!bundle_file.is_open()) {\n\t\t\tSIBR_ERR << \"Unable to load bundle file at path \\\"\" << bundlerPath << \"\\\".\" << std::endl;\n\t\t\treturn {};\n\t\t}\n\n\t\tconst std::string listImages = listImagePath.empty() ? (bundlerPath + \"/../list_images.txt\") : listImagePath;\n\t\tstd::ifstream list_images(listImages);\n\t\tif (!list_images.is_open()) {\n\t\t\tSIBR_ERR << \"Unable to load list_images file at path \\\"\" << listImages << \"\\\".\" << std::endl;\n\t\t\treturn {};\n\t\t}\n\n\t\t// read number of images\n\t\tstd::string line;\n\t\tgetline(bundle_file, line);\t// ignore first line - contains version\n\t\tint numImages = 0;\n\t\tbundle_file >> numImages;\t// read first value (number of images)\n\t\tgetline(bundle_file, line);\t// ignore the rest of the line\n\n\t\t\t\t\t\t\t\t\t// Read all filenames\n\t\tstruct ImgInfos\n\t\t{\n\t\t\tstd::string name;\n\t\t\tint id;\n\t\t\tint w, h;\n\t\t};\n\t\tstd::vector<ImgInfos>\timgInfos;\n\t\t{\n\t\t\tImgInfos\t\t\t\tinfos;\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tlist_images >> infos.name;\n\t\t\t\tif (infos.name.empty()) break;\n\t\t\t\tlist_images >> infos.w >> infos.h;\n\t\t\t\tinfos.name.erase(infos.name.find_last_of(\".\"), std::string::npos);\n\t\t\t\tinfos.id = atoi(infos.name.c_str());\n\t\t\t\timgInfos.push_back(infos);\n\t\t\t\tinfos.name.clear();\n\t\t\t}\n\t\t}\n\n\t\tImgInfos infoPrevImage;\n\t\tbool shortListImages = false;\n\t\t// check if list images has the same number of cameras as path, else assume we read the dataset list_images.txt\n\t\tif (path && imgInfos.size() != numImages)\n\t\t\tshortListImages = true;\n\n\n\n\n\t\tstd::vector<InputCamera::Ptr> cameras(numImages);\n\t\t//  Parse bundle.out file for camera calibration parameters\n\t\tfor (int i = 0, infosId = 0; i < numImages; i++) {\n\n\t\t\tImgInfos infos;\n\t\t\tstd::string camName;\n\n\t\t\tif (!shortListImages) {\n\t\t\t\tinfoPrevImage = infos = imgInfos[infosId];\n\t\t\t\tcamName = infos.name;\n\t\t\t\tif (infosId > imgInfos.size())\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// hack; use info of last available image, but (always) change name\n\t\t\t\tif( i < imgInfos.size())\n\t\t\t\t\tinfoPrevImage = infos = imgInfos[infosId];\n\t\t\t\telse \n\t\t\t\t\tinfos = infoPrevImage;\n\n\t\t\t\tstd::stringstream ss;\n\t\t\t\tss << std::setw(10) << std::setfill('0') << i;\n\t\t\t\tstd::string s = ss.str();\n\t\t\t\tcamName = std::string(\"path_camera\") + s;\n\t\t\t}\n\n\t\t\tMatrix4f m; // bundler params\n\n\t\t\tbundle_file >> m(0) >> m(1) >> m(2) >> m(3) >> m(4);\n\t\t\tbundle_file >> m(5) >> m(6) >> m(7) >> m(8) >> m(9);\n\t\t\tbundle_file >> m(10) >> m(11) >> m(12) >> m(13) >> m(14);\n\n\t\t\tcameras[infosId] = InputCamera::Ptr(new InputCamera(infosId, infos.w, infos.h, m, true));\n\t\t\tcameras[infosId]->name(camName);\n\t\t\tcameras[infosId]->znear(zNear); cameras[infosId]->zfar(zFar);\n\n\t\t\t++infosId;\n\t\t}\n\n\t\treturn cameras;\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadBundleFRIBR(const std::string& bundlerPath, float zNear, float zFar, const std::string& listImagePath)\n\t{\n\t\tSIBR_LOG << \"Loading input cameras.\" << std::endl;\n\n\t\t// check bundler file\n\t\tstd::ifstream bundle_file(bundlerPath);\n\t\tif (!bundle_file.is_open()) {\n\t\t\tSIBR_ERR << \"Unable to load bundle file at path \\\"\" << bundlerPath << \"\\\".\" << std::endl;\n\t\t\treturn {};\n\t\t}\n\n\n\t\t// read number of images\n\t\tstd::string line;\n\t\tgetline(bundle_file, line);\t// ignore first line - contains version\n\t\tint numImages = 0;\n\t\tbundle_file >> numImages;\t// read first value (number of images)\n\t\tgetline(bundle_file, line);\t// ignore the rest of the line\n\n\t\tstd::vector<InputCamera::Ptr> cameras(numImages);\n\n\t\tEigen::Matrix3f to_cv, converter;\n\t\tto_cv << 1.0f, 0.0f, 0.0f,\n\t\t\t0.0f, -1.0f, 0.0f,\n\t\t\t0.0f, 0.0f, -1.0f;\n\t\tconverter <<\n\t\t\t1, 0, 0,\n\t\t\t0, -1, 0,\n\t\t\t0, 0, -1;\n\t\t//  Parse bundle.out file for camera calibration parameters\n\t\tfor (int i = 0; i < numImages; i++) {\n\n\t\t\tfloat f, k1, k2;\n\t\t\tbundle_file >> f >> k1 >> k2;\n\n\t\t\tfloat r00, r01, r02;\n\t\t\tfloat r10, r11, r12;\n\t\t\tfloat r20, r21, r22;\n\t\t\tbundle_file >> r00 >> r01 >> r02\n\t\t\t\t>> r10 >> r11 >> r12\n\t\t\t\t>> r20 >> r21 >> r22;\n\n\t\t\tEigen::Matrix3f rotation;\n\t\t\trotation(0, 0) = r00;\n\t\t\trotation(0, 1) = r01;\n\t\t\trotation(0, 2) = r02;\n\t\t\trotation(1, 0) = r10;\n\t\t\trotation(1, 1) = r11;\n\t\t\trotation(1, 2) = r12;\n\t\t\trotation(2, 0) = r20;\n\t\t\trotation(2, 1) = r21;\n\t\t\trotation(2, 2) = r22;\n\n\t\t\tsibr::Matrix3f orientation = (to_cv * rotation).transpose();\n\n\t\t\tfloat tx, ty, tz;\n\t\t\tbundle_file >> tx >> ty >> tz;\n\t\t\tsibr::Vector3f position = -orientation * (to_cv * Eigen::Vector3f(tx, ty, tz));\n\n\t\t\tstd::stringstream pad_stream;\n\t\t\tpad_stream << std::setfill('0') << std::setw(10) << i - 2 << \".png\";\n\t\t\tstd::string     image_path = sibr::parentDirectory(bundlerPath) + \"/\" + listImagePath + pad_stream.str();\n\n\t\t\tsibr::Vector2u resolution(2, 2);\n\t\t\tsibr::ImageRGB temp;\n\t\t\tif (!temp.load(image_path)) {\n\n\t\t\t\tpad_stream.str(\"\");\n\t\t\t\tpad_stream << std::setfill('0') << std::setw(8) << i << \".jpg\";\n\t\t\t\timage_path = sibr::parentDirectory(bundlerPath) + \"/\" + listImagePath + pad_stream.str();\n\t\t\t\ttemp.load(image_path);\n\t\t\t}\n\t\t\tresolution = temp.size();\n\n\t\t\tif (resolution.x() < 0 || resolution.y() < 0)\n\t\t\t{\n\t\t\t\tstd::cerr << \"Could not get resolution for calibrated camera: \" << image_path << std::endl;\n\t\t\t\treturn {};\n\t\t\t}\n\n\t\t\tfloat dx = resolution.x() * 0.5f;\n\t\t\tfloat dy = resolution.y() * 0.5f;\n\n\t\t\torientation = /*converter.transpose() **/ orientation * converter;\n\t\t\tposition = /*converter.transpose() **/ position;\n\n\t\t\tcameras[i] = InputCamera::Ptr(new InputCamera(i, resolution.x(), resolution.y(), position, orientation, f, k1, k2, true));\n\t\t\tcameras[i]->name(pad_stream.str());\n\t\t\tcameras[i]->znear(zNear); cameras[i]->zfar(zFar);\n\n\t\t}\n\n\t\treturn cameras;\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadMeshroom(const std::string& meshroomSFMPath, const float zNear, const float zFar)\n\t{\n\n\t\tstd::string file_path = meshroomSFMPath + \"/cameras.sfm\";\n\n\t\tstd::ifstream json_file(file_path, std::ios::in);\n\n\t\tif (!json_file)\n\t\t{\n\t\t\tstd::cerr << \"file loading failed: \" << file_path << std::endl;\n\t\t\treturn std::vector<InputCamera::Ptr>();\n\t\t}\n\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tpicojson::value v;\n\t\tpicojson::set_last_error(std::string());\n\t\tstd::string err = picojson::parse(v, json_file);\n\t\tif (!err.empty()) {\n\t\t\tpicojson::set_last_error(err);\n\t\t\tjson_file.setstate(std::ios::failbit);\n\t\t}\n\n\t\tpicojson::array& views = v.get(\"views\").get<picojson::array>();\n\t\tpicojson::array& intrinsincs = v.get(\"intrinsics\").get<picojson::array>();\n\t\tpicojson::array& poses = v.get(\"poses\").get<picojson::array>();\n\n\t\tint numCameras = int(poses.size());\n\t\t//meras.resize(numCameras);\n\n\t\tsibr::Matrix3f converter;\n\t\tconverter << 1.0f, 0, 0,\n\t\t\t0, -1, 0,\n\t\t\t0, 0, -1;\n\n\t\tsize_t pose_idx, view_idx, intrinsic_idx;\n\t\tstd::vector<std::string> splitS;\n\n\n\t\tfor (size_t i = 0; i < numCameras; ++i)\n\t\t{\n\n\t\t\tMatrix4f m;\n\t\t\t//std::vector<std::string> splitS;\n\n\t\t\tpose_idx = i;\n\t\t\tstd::string pose_id = poses[pose_idx].get(\"poseId\").get<std::string>();\n\n\t\t\tfor (size_t j = 0; j < views.size(); j++) {\n\t\t\t\tif (pose_id.compare(views[j].get(\"poseId\").get<std::string>()) == 0) {\n\t\t\t\t\tview_idx = j;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tstd::string intrinsics_id = views[view_idx].get(\"intrinsicId\").get<std::string>();\n\n\t\t\tfor (size_t k = 0; k < intrinsincs.size(); k++) {\n\t\t\t\tif (intrinsics_id.compare(intrinsincs[k].get(\"intrinsicId\").get<std::string>()) == 0) {\n\t\t\t\t\tintrinsic_idx = k;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tm(0) = std::stof(intrinsincs[intrinsic_idx].get(\"pxFocalLength\").get<std::string>());\n\t\t\tfloat dx = std::stof(intrinsincs[intrinsic_idx].get(\"principalPoint\").get<picojson::array>()[0].get<std::string>());\n\t\t\tfloat dy = std::stof(intrinsincs[intrinsic_idx].get(\"principalPoint\").get<picojson::array>()[1].get<std::string>());\n\n\t\t\t//std::stof(intrinsincs[intrinsic_idx].get(\"distortionParams\").get<picojson::array>()[0].get<std::string>());\n\t\t\tm(1) = dx;\n\t\t\t//std::stof(intrinsincs[intrinsic_idx].get(\"distortionParams\").get<picojson::array>()[1].get<std::string>());\n\t\t\tm(2) = dy;\n\n\t\t\tstd::string camName = pose_id + \".exr\";\n\t\t\tint width = std::stoi(views[view_idx].get(\"width\").get<std::string>());\n\t\t\tint height = std::stoi(views[view_idx].get(\"height\").get<std::string>());\n\n\t\t\tuint camId = uint(i);\n\n\t\t\tpicojson::array& center = poses[pose_idx].get(\"pose\").get(\"transform\").get(\"center\").get<picojson::array>();\n\t\t\tpicojson::array& rotation = poses[pose_idx].get(\"pose\").get(\"transform\").get(\"rotation\").get<picojson::array>();\n\n\t\t\tstd::vector<Eigen::Vector3f> rows;\n\t\t\tEigen::Vector3f row;\n\t\t\tEigen::Vector3f position(std::stof(center[0].get<std::string>()), std::stof(center[1].get<std::string>()), std::stof(center[2].get<std::string>()));\n\t\t\tEigen::Matrix3f orientation;\n\n\t\t\tfor (int ii = 0; ii < 3; ++ii) {\n\t\t\t\tfor (int jj = 0; jj < 3; ++jj)\n\t\t\t\t\trow(jj) = std::stof(rotation[jj + ii * 3].get<std::string>());\n\t\t\t\trows.push_back(row);\n\t\t\t}\n\n\t\t\torientation.row(0) = rows[0];\n\t\t\torientation.row(1) = rows[1];\n\t\t\torientation.row(2) = rows[2];\n\t\t\torientation = orientation * converter;\n\n\t\t\tfor (int ii = 0; ii < 9; ii++) {\n\t\t\t\tm(3 + ii) = orientation(ii);\n\t\t\t}\n\n\t\t\tconst sibr::Vector3f finTrans = -orientation.transpose() * position;\n\t\t\tfor (int ii = 0; ii < 3; ii++) {\n\t\t\t\tm(12 + ii) = finTrans[ii];\n\t\t\t}\n\n\t\t\tsibr::InputCamera::Ptr cam = std::make_shared<InputCamera>(camId, width, height, m, true);\n\t\t\tcam->name(camName);\n\t\t\tcam->znear(zNear);\n\t\t\tcam->zfar(zFar);\n\t\t\tcameras.push_back(cam);\n\n\t\t}\n\t\treturn cameras;\n\t}\n\n\tVector3f\t\t\tInputCamera::unprojectImgSpaceInvertY(const sibr::Vector2i& pixelPos, const float& depth) const\n\t{\n\t\tsibr::Vector2f pos2dGL(2.0f * ((pixelPos.cast<float>() + sibr::Vector2f(0.5, 0.5)).cwiseQuotient(sibr::Vector2f(w(), h()))) - sibr::Vector2f(1, 1));  //to [-1,1]\n\t\tpos2dGL.y() = -pos2dGL.y();\n\t\treturn unproject(sibr::Vector3f(pos2dGL.x(), pos2dGL.y(), depth));\n\t}\n\n\tVector3f\t\t\tInputCamera::projectImgSpaceInvertY(const Vector3f& point3d) const\n\t{\n\t\tsibr::Vector3f pos2dGL = project(point3d);\n\t\tpos2dGL.y() = -pos2dGL.y();\n\t\tsibr::Vector2f pos2dImg = (0.5f * (pos2dGL.xy() + sibr::Vector2f(1, 1))).cwiseProduct(sibr::Vector2f(w(), h()));\n\t\treturn sibr::Vector3f(pos2dImg.x(), pos2dImg.y(), pos2dGL.z());\n\t}\n\n\tbool\t\t\t\tInputCamera::loadFromBinary(const std::string& filename)\n\t{\n\t\tByteStream\tbytes;\n\n\t\tif (bytes.load(filename))\n\t\t{\n\t\t\tuint8\tversion;\n\t\t\tfloat\tfocal;\n\t\t\tfloat\tk1;\n\t\t\tfloat\tk2;\n\t\t\tuint16\tw;\n\t\t\tuint16\th;\n\t\t\tVector3f\tpos;\n\t\t\tQuaternionf\trot;\n\t\t\tfloat\t\tfov;\n\t\t\tfloat\t\taspect;\n\t\t\tfloat\t\tznear;\n\t\t\tfloat\t\tzfar;\n\n\t\t\tbytes\n\t\t\t\t>> version;\n\n\t\t\tif (version != SIBR_INPUTCAMERA_BINARYFILE_VERSION)\n\t\t\t{\n\t\t\t\t// Maybe the file format has been updated, or your binary file is not about InputCamera...\n\t\t\t\tSIBR_ERR << \"incorrect file format (version number does not correspond).\" << std::endl;\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbytes\n\t\t\t\t>> focal >> k1 >> k2 >> w >> h\n\t\t\t\t>> pos.x() >> pos.y() >> pos.z()\n\t\t\t\t>> rot.w() >> rot.x() >> rot.y() >> rot.z()\n\t\t\t\t>> fov >> aspect >> znear >> zfar\n\t\t\t\t;\n\n\t\t\t_focal = focal;\n\t\t\t_k1 = k1;\n\t\t\t_k2 = k2;\n\t\t\t_w = (uint)w;\n\t\t\t_h = (uint)h;\n\t\t\tCamera::position(pos);\n\t\t\tCamera::rotation(rot);\n\t\t\tCamera::fovy(fov);\n\t\t\tCamera::aspect(aspect);\n\t\t\tCamera::znear(znear);\n\t\t\tCamera::zfar(zfar);\n\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSIBR_WRG << \"cannot open file '\" << filename << \"'.\" << std::endl;\n\t\t}\n\t\treturn false;\n\t}\n\n\tvoid\t\t\t\tInputCamera::saveToBinary(const std::string& filename) const\n\t{\n\t\tByteStream\tbytes;\n\n\t\tuint8\tversion = SIBR_INPUTCAMERA_BINARYFILE_VERSION;\n\t\tfloat\tfocal = _focal;\n\t\tfloat\tk1 = _k1;\n\t\tfloat\tk2 = _k2;\n\t\tuint16\tw = (uint16)_w;\n\t\tuint16\th = (uint16)_h;\n\t\tVector3f\tpos = position();\n\t\tQuaternionf\trot = rotation();\n\t\tfloat\t\tfov = _fov;\n\t\tfloat\t\taspect = _aspect;\n\t\tfloat\t\tznear = _znear;\n\t\tfloat\t\tzfar = _zfar;\n\n\t\tbytes\n\t\t\t<< version\n\t\t\t<< focal << k1 << k2 << w << h\n\t\t\t<< pos.x() << pos.y() << pos.z()\n\t\t\t<< rot.w() << rot.x() << rot.y() << rot.z()\n\t\t\t<< fov << aspect << znear << zfar\n\t\t\t;\n\n\t\tbytes.saveToFile(filename);\n\t}\n\n\tvoid InputCamera::readFromFile(std::istream& infile)\n\t{\n\t\tstd::string version;\n\t\tinfile >> version;\n\t\tif (version != IBRVIEW_TOPVIEW_SAVEVERSION)\n\t\t{\n\t\t\tSIBR_WRG << \"Sorry but your TopView camera configuration \"\n\t\t\t\t\"is too old (we added new features since!)\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tVector3f v;\n\t\tinfile >> v.x() >> v.y() >> v.z();\n\t\tQuaternionf q;\n\t\tinfile >> q.x() >> q.y() >> q.z() >> q.w();\n\t\tset(v, q);\n\t}\n\n\tvoid InputCamera::writeToFile(std::ostream& outfile) const\n\t{\n\t\toutfile << IBRVIEW_TOPVIEW_SAVEVERSION \"\\n\";\n\t\tVector3f v = transform().position();\n\t\tQuaternionf q = transform().rotation();\n\t\toutfile << \" \" << v.x() << \" \" << v.y() << \" \" << v.z() << \" \";\n\t\toutfile << q.x() << \" \" << q.y() << \" \" << q.z() << \" \" << q.w();\n\t}\n\n\tstd::string InputCamera::toBundleString(bool negativeZ, bool recomputeFocal) const {\n\n\t\tstd::stringstream ss;\n\t\tss << std::setprecision(16);\n\t\tfloat focal;\n\t\tif( recomputeFocal )\n\t\t\tfocal = 0.5f * h() / tan(fovy() / 2.0f); // We cannot set the focal but we need to compute it\n\t\telse\n\t\t\tfocal = _focal;\n\n\t\tEigen::Matrix3f r = transform().rotation().toRotationMatrix();\n\t\tsibr::Vector3f t = -transform().rotation().toRotationMatrix().transpose() * position();\n\n\t\tss << focal << \" \" << k1() << \" \" << k2() << \"\\n\"; // The focal is set to zero in the loading module we use fov=2.0f * atan( 0.5f*h / focal) here\n\t\tif (!negativeZ) {\n\t\t\tss << r(0) << \" \" << r(1) << \" \" << r(2) << \"\\n\";\n\t\t\tss << r(3) << \" \" << r(4) << \" \" << r(5) << \"\\n\";\n\t\t\tss << r(6) << \" \" << r(7) << \" \" << r(8) << \"\\n\";\n\t\t\tss << t(0) << \" \" << t(1) << \" \" << t(2) << \"\\n\";\n\t\t}\n\t\telse {\n\t\t\tss << r(0) << \" \" << -r(2) << \" \" << r(1) << \"\\n\";\n\t\t\tss << r(3) << \" \" << -r(5) << \" \" << r(4) << \"\\n\";\n\t\t\tss << r(6) << \" \" << -r(8) << \" \" << r(7) << \"\\n\";\n\t\t\tss << t(0) << \" \" << t(1) << \" \" << t(2) << \"\\n\";\n\t\t}\n\n\t\treturn ss.str();\n\t}\n\n\tstd::vector<sibr::Vector2i> InputCamera::getImageCorners() const\n\t{\n\t\treturn { {0,0}, {_w - 1, 0}, {_w - 1,_h - 1}, {0, _h - 1} };\n\t}\n\n\tvoid InputCamera::saveAsBundle(const std::vector<InputCamera::Ptr>& cams, const std::string& fileName, bool negativeZ, const bool exportImages, bool oldFocal) {\n\n\t\tstd::ofstream outputBundleCam;\n\t\toutputBundleCam.open(fileName);\n\t\toutputBundleCam << \"# Bundle file v0.3\" << std::endl;\n\t\toutputBundleCam << cams.size() << \" \" << 0 << std::endl;\n\n\t\tfor (int c = 0; c < cams.size(); c++) {\n\t\t\tauto& camIm = cams[c];\n\t\t\toutputBundleCam << camIm->toBundleString(negativeZ, oldFocal);\n\t\t}\n\n\t\toutputBundleCam.close();\n\n\t\t// Export the images list and empty images (useful for fribr).\n\t\tif (exportImages) {\n\t\t\tstd::ofstream outList;\n\t\t\tconst std::string listpath = fileName + \"/../list_images.txt\";\n\t\t\tconst std::string imagesDir = fileName + \"/../visualize/\";\n\t\t\tsibr::makeDirectory(imagesDir);\n\n\t\t\toutList.open(listpath);\n\t\t\tif (outList.good()) {\n\t\t\t\tfor (int i = 0; i < cams.size(); ++i) {\n\t\t\t\t\tconst sibr::InputCamera::Ptr cam = cams[i];\n\t\t\t\t\tconst std::string imageName = cam->name().empty() ? sibr::intToString<8>(i) + \".jpg\" : cam->name();\n\t\t\t\t\toutList << \"visualize/\" << imageName << \" \" << cam->w() << \" \" << cam->h() << std::endl;\n\t\t\t\t\tcv::Mat3b dummy(cam->h(), cam->w());\n\t\t\t\t\tcv::imwrite(imagesDir + imageName, dummy);\n\t\t\t\t}\n\t\t\t\toutList.close();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tSIBR_WRG << \"Unable to export images list to path \\\"\" << listpath << \"\\\".\" << std::endl;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid InputCamera::saveAsLookat(const std::vector<sibr::Camera>& cams, const std::string& fileName)\n\t{\n\n\t\tstd::ofstream file(fileName, std::ios::out | std::ios::trunc);\n\t\tif (!file.is_open()) {\n\t\t\tSIBR_WRG << \"Unable to save to file at path \" << fileName << std::endl;\n\t\t\treturn;\n\t\t}\n\t\t// Get the padding count.\n\t\tconst int len = int(std::floor(std::log10(cams.size()))) + 1;\n\t\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\t\tconst auto& cam = cams[cid];\n\t\t\tstd::string id = std::to_string(cid);\n\t\t\tconst std::string pad = std::string(len - id.size(), '0');\n\n\t\t\tconst sibr::Vector3f& pos = cam.position();\n\t\t\tconst sibr::Vector3f& up = cam.up();\n\t\t\tconst sibr::Vector3f tgt = cam.position() + cam.dir();\n\n\n\t\t\tfile << \"Cam\" << pad << id;\n\t\t\tfile << \" -D origin=\" << pos[0] << \",\" << pos[1] << \",\" << pos[2];\n\t\t\tfile << \" -D target=\" << tgt[0] << \",\" << tgt[1] << \",\" << tgt[2];\n\t\t\tfile << \" -D up=\" << up[0] << \",\" << up[1] << \",\" << up[2];\n\t\t\tfile << \" -D fovy=\" << cam.fovy();\n\t\t\tfile << \" -D clip=\" << cam.znear() << \",\" << cam.zfar();\n\t\t\tfile << \"\\n\";\n\t\t}\n\n\t\tfile.close();\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadColmapBin(const std::string& colmapSparsePath, const float zNear, const float zFar, const int fovXfovYFlag)\n\t{\n\t\tconst std::string camerasListing = colmapSparsePath + \"/cameras.bin\";\n\t\tconst std::string imagesListing = colmapSparsePath + \"/images.bin\";\n\n\n  \t\tstd::ifstream camerasFile(camerasListing, std::ios::binary);\n\t\tstd::ifstream imagesFile(imagesListing, std::ios::binary);\n\n\t\tif (!camerasFile.is_open()) {\n\t\t\tSIBR_ERR << \"Unable to load camera colmap file\" << camerasListing << std::endl;\n\t\t}\n\t\tif (!imagesFile.is_open()) {\n\t\t\tSIBR_WRG << \"Unable to load images colmap file\" << imagesListing << std::endl;\n\t\t}\n\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tstd::string line;\n\n\t\tstruct CameraParametersColmap {\n\t\t\tsize_t id;\n\t\t\tsize_t width;\n\t\t\tsize_t height;\n\t\t\tfloat  fx;\n\t\t\tfloat  fy;\n\t\t\tfloat  dx;\n\t\t\tfloat  dy;\n\t\t};\n\n\t\tstd::map<size_t, CameraParametersColmap> cameraParameters;\n  \t\tconst size_t num_cameras = ReadBinaryLittleEndian<uint64_t>(&camerasFile);\n\n  \t\tfor (size_t i = 0; i < num_cameras ; ++i) {\n\n\t\t\tCameraParametersColmap params;\n\n\t\t\tparams.id = ReadBinaryLittleEndian<uint32_t>(&camerasFile);\n\t\t\tint model_id = ReadBinaryLittleEndian<int>(&camerasFile);\n\t\t\tparams.width = ReadBinaryLittleEndian<uint64_t>(&camerasFile);\n\t\t\tparams.height = ReadBinaryLittleEndian<uint64_t>(&camerasFile);\n\t\t\tstd::vector<double> Params(4);\n\n    \t\t\tReadBinaryLittleEndian<double>(&camerasFile, &Params);\n\t\t\tparams.fx = float(Params[0]);\n\t\t\tparams.fy = float(Params[1]);\n\t\t\tparams.dx = float(Params[2]);\n\t\t\tparams.dy = float(Params[3]);\n\t\t\tcameraParameters[params.id] = params;\n\t\t}\n\n\t\t// Now load the individual images and their extrinsic parameters\n\t\tsibr::Matrix3f converter;\n\t\tconverter << 1, 0, 0,\n\t\t\t0, -1, 0,\n\t\t\t0, 0, -1;\n\n  \t\tconst size_t num_reg_images = ReadBinaryLittleEndian<uint64_t>(&imagesFile);\n\t\tfor (size_t i = 0; i < num_reg_images; ++i) {\n\n\t\t\tuint\t    cId = ReadBinaryLittleEndian<image_t>(&imagesFile);\n\t\t\tfloat       qw = float(ReadBinaryLittleEndian<double>(&imagesFile));\n\t\t\tfloat       qx = float(ReadBinaryLittleEndian<double>(&imagesFile)) ;\n\t\t\tfloat       qy = float(ReadBinaryLittleEndian<double>(&imagesFile)) ;\n\t\t\tfloat       qz = float(ReadBinaryLittleEndian<double>(&imagesFile)) ;\n\t\t\tfloat       tx = float(ReadBinaryLittleEndian<double>(&imagesFile));\n\t\t\tfloat       ty = float(ReadBinaryLittleEndian<double>(&imagesFile));\n\t\t\tfloat       tz = float(ReadBinaryLittleEndian<double>(&imagesFile));\n\t\t\tsize_t      id = ReadBinaryLittleEndian<camera_t>(&imagesFile) ;\n\n\n\t\t\tif (cameraParameters.find(id) == cameraParameters.end())\n\t\t\t{\n\t\t\t\t/* code multi camera broken\n\t\t\t\tSIBR_ERR << \"Could not find intrinsics for image: \"\n\t\t\t\t\t<< id << std::endl;\n\t\t\t*/\n\t\t\t\tid = 1;\n\t\t\t}\n\t\t\tconst CameraParametersColmap& camParams = cameraParameters[id];\n\n\n\t\t\tconst sibr::Quaternionf quat(qw, qx, qy, qz);\n\t\t\tconst sibr::Matrix3f orientation = quat.toRotationMatrix().transpose() * converter;\n\t\t\tsibr::Vector3f translation(tx, ty, tz);\n\n\t\t\tsibr::Vector3f position = -(orientation * converter * translation);\n\n\t\t\tsibr::InputCamera::Ptr camera;\n\t\t\tif (fovXfovYFlag) {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(camParams.fy, camParams.fx, 0.0f, 0.0f, int(camParams.width), int(camParams.height), int(cId)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(camParams.fy, 0.0f, 0.0f, int(camParams.width), int(camParams.height), int(cId)));\n\t\t\t}\n\t\t\tstd::string image_name;\n\t\t\tchar name_char;\n\t\t\tdo {\n\t\t\t\timagesFile.read(&name_char, 1);\n\t\t\t\tif (name_char != '\\0') {\n\t\t\t\t\timage_name += name_char;\n\t\t\t\t}\n\t\t\t} while (name_char != '\\0');\n\n\t\t\tcamera->name(image_name);\n\t\t\tcamera->position(position);\n\t\t\tcamera->rotation(sibr::Quaternionf(orientation));\n\t\t\tcamera->znear(zNear);\n\t\t\tcamera->zfar(zFar);\n\t\t\tcameras.push_back(camera);\n\n\n    \t\t// ignore the 2d points\n    \t\tconst size_t num_points2D = ReadBinaryLittleEndian<uint64_t>(&imagesFile);\n\n    \t\t\tfor (size_t j = 0; j < num_points2D; ++j) {\n\t\t\t      const double x = ReadBinaryLittleEndian<double>(&imagesFile);\n\t\t\t      const double y = ReadBinaryLittleEndian<double>(&imagesFile);\n\t\t\t\t  point3D_t id = ReadBinaryLittleEndian<point3D_t>(&imagesFile);\n    \t\t\t}\n\t\t}\n\t\treturn cameras;\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadJSON(const std::string& jsonPath, const float zNear, const float zFar)\n\t{\n\t\tstd::ifstream json_file(jsonPath, std::ios::in);\n\n\t\tif (!json_file)\n\t\t{\n\t\t\tstd::cerr << \"file loading failed: \" << jsonPath << std::endl;\n\t\t\treturn std::vector<InputCamera::Ptr>();\n\t\t}\n\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tpicojson::value v;\n\t\tpicojson::set_last_error(std::string());\n\t\tstd::string err = picojson::parse(v, json_file);\n\t\tif (!err.empty()) {\n\t\t\tpicojson::set_last_error(err);\n\t\t\tjson_file.setstate(std::ios::failbit);\n\t\t}\n\n\t\tpicojson::array& frames = v.get<picojson::array>();\n\n\t\tfor (size_t i = 0; i < frames.size(); ++i)\n\t\t{\n\t\t\tint id = frames[i].get(\"id\").get<double>();\n\t\t\tstd::string imgname = frames[i].get(\"img_name\").get<std::string>();\n\t\t\tint width = frames[i].get(\"width\").get<double>();\n\t\t\tint height = frames[i].get(\"height\").get<double>();\n\t\t\tfloat fy = frames[i].get(\"fy\").get<double>();\n\t\t\tfloat fx = frames[i].get(\"fx\").get<double>();\n\n\t\t\tsibr::InputCamera::Ptr camera = std::make_shared<InputCamera>(InputCamera(fy, fx, 0.0f, 0.0f, width, height, id));\n\n\t\t\tpicojson::array& pos = frames[i].get(\"position\").get<picojson::array>();\n\t\t\tsibr::Vector3f position(pos[0].get<double>(), pos[1].get<double>(), pos[2].get<double>());\n\n\t\t\t//position.x() = 0;\n\t\t\t//position.y() = 0;\n\t\t\t//position.z() = 1;\n\n\t\t\tpicojson::array& rot = frames[i].get(\"rotation\").get<picojson::array>();\n\t\t\tsibr::Matrix3f orientation;\n\t\t\tfor (int i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tpicojson::array& row = rot[i].get<picojson::array>();\n\t\t\t\tfor (int j = 0; j < 3; j++)\n\t\t\t\t{\n\t\t\t\t\torientation(i, j) = row[j].get<double>();\n\t\t\t\t}\n\t\t\t}\n\t\t\torientation.col(1) = -orientation.col(1);\n\t\t\torientation.col(2) = -orientation.col(2);\n\t\t\t//orientation = sibr::Matrix3f::Identity();\n\n\t\t\tcamera->name(imgname);\n\t\t\tcamera->position(position);\n\t\t\tcamera->rotation(sibr::Quaternionf(orientation));\n\t\t\tcamera->znear(zNear);\n\t\t\tcamera->zfar(zFar);\n\t\t\tcameras.push_back(camera);\n\t\t}\n\t\treturn cameras;\n\t}\n\n\tstd::vector<InputCamera::Ptr> InputCamera::loadTransform(const std::string& transformPath, int w, int h, std::string extension, const float zNear, const float zFar, const int offset, const int fovXfovYFlag)\n\t{\n\t\tstd::ifstream json_file(transformPath, std::ios::in);\n\n\t\tif (!json_file)\n\t\t{\n\t\t\tstd::cerr << \"file loading failed: \" << transformPath << std::endl;\n\t\t\treturn std::vector<InputCamera::Ptr>();\n\t\t}\n\n\t\tstd::vector<InputCamera::Ptr> cameras;\n\n\t\tpicojson::value v;\n\t\tpicojson::set_last_error(std::string());\n\t\tstd::string err = picojson::parse(v, json_file);\n\t\tif (!err.empty()) {\n\t\t\tpicojson::set_last_error(err);\n\t\t\tjson_file.setstate(std::ios::failbit);\n\t\t}\n\n\t\tfloat fovx = v.get(\"camera_angle_x\").get<double>();\n\t\tpicojson::array& frames = v.get(\"frames\").get<picojson::array>();\n\n\t\tfor (int i = 0; i < frames.size(); i++)\n\t\t{\n\t\t\tstd::string imgname = frames[i].get(\"file_path\").get<std::string>() + \".\" + extension;\n\n\t\t\tauto mat = frames[i].get(\"transform_matrix\").get<picojson::array>();\n\n\t\t\tEigen::Matrix4f matrix;\n\t\t\tfor (int i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tauto row = mat[i].get<picojson::array>();\n\t\t\t\tfor (int j = 0; j < 4; j++)\n\t\t\t\t{\n\t\t\t\t\tmatrix(i, j) = row[j].get<double>();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tEigen::Matrix3f R = matrix.block<3, 3>(0, 0);\n\t\t\tEigen::Vector3f T(matrix(0, 3), matrix(1, 3), matrix(2, 3));\n\n\t\t\tfloat focalx = 0.5f * w / tan(fovx / 2.0f);\n\t\t\tfloat focaly = (((float)h)/w) * focalx;\n\n\t\t\tsibr::InputCamera::Ptr camera;\n\t\t\tif (fovXfovYFlag) {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(focaly, focalx, 0.0f, 0.0f, int(w), int(h), i + offset));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcamera = std::make_shared<InputCamera>(InputCamera(focalx, 0.0f, 0.0f, int(w), int(h), i + offset));\n\t\t\t}\n\n\t\t\tcamera->name(imgname);\n\t\t\tcamera->position(T);\n\t\t\tcamera->rotation(sibr::Quaternionf(R));\n\t\t\tcamera->znear(zNear);\n\t\t\tcamera->zfar(zFar);\n\t\t\tcameras.push_back(camera);\n\t\t}\n\t\treturn cameras;\n\t}\n\n\n} \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/InputCamera.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/graphics/Config.hpp\"\n#include \"core/graphics/Camera.hpp\"\n#include \"core/assets/Config.hpp\"\n\nnamespace sibr\n{\n\t/** Input camera parameters. Inherits all basic camera functionality from Camera\n\t*  and adds functions for depth samples from multi-view stereo.\n\t*\n\t* \\sa Camera, NovelCamera\n\t* \\ingroup sibr_assets\n\t*/\n\tclass SIBR_ASSETS_EXPORT InputCamera : public Camera \n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<InputCamera> Ptr;\n\n\t\t/** Near/far plane representation. */\n\t\tstruct Z {\n\n\t\t\t/** Constructor. */\n\t\t\tZ() {}\n\n\t\t\t/** Constructor.\n\t\t\t * \\warning Ordering of the values is swapped.\n\t\t\t * \\param f far plane\n\t\t\t * \\param n near plane\n\t\t\t */\n\t\t\tZ(float f, float n) : far(f), near(n) {}\n\n\t\t\tfloat far = 0.0f; ///< Far plane.\n\t\t\tfloat near = 0.0f; ///< Near plane.\n\t\t};\n\n\t\t/** Default constructor. */\n\t\tInputCamera() :\n\t\t\t_focal(0.f), _k1(0.f), _k2(0.f), _w(0), _h(0), _id(0), _active(true)\n\t\t{ }\n\n\t\t/** Partial constructor\n\t\t* \\param f focal length in mm\n\t\t* \\param k1 first distortion parameter\n\t\t* \\param k2 second distortion parameter\n\t\t* \\param w  width of input image\n\t\t* \\param h  height of input image\n\t\t* \\param id ID of input image\n\t\t*/\n\t\tInputCamera(float f, float k1, float k2, int w, int h, int id);\n\t\tInputCamera(float fy, float fx, float k1, float k2, int w, int h, int id);\n\n\t\t/** Constructor, initialize the input camera.\n\t\t* \\param id ID of input image\n\t\t* \\param w  width of input image\n\t\t* \\param h  height of input image\n\t\t* \\param position camera position\n\t\t* \\param rotation camera rotation\n\t\t* \\param focal focal length in mm\n\t\t* \\param k1 first distortion parameter\n\t\t* \\param k2 second distortion parameter\n\t\t* \\param active  input image active or not\n\t\t*/\n\t\tInputCamera(int id, int w, int h, sibr::Vector3f & position, sibr::Matrix3f & rotation, float focal, float k1, float k2, bool active);\n\n\t\t/** Constructor, initialize the input camera.\n\t\t* \\param id ID of input image\n\t\t* \\param w  width of input image\n\t\t* \\param h  height of input image\n\t\t* \\param m  camera parameters resad from Bundler output file\n\t\t* \\param active  input image active or not\n\t\t* \\param fovFromFocal: if true, compute fov from focal else use \"standard sibr\" convention\n\t\t* \\sa Bundler: http://phototour.cs.washington.edu/bundler/\n\t\t* \\deprecated Avoid using this legacy constructor.\n\t\t*/\n\t\tInputCamera(int id, int w, int h, sibr::Matrix4f m, bool active);\n\n\t\t/** Constructor from a basic Camera.\n\t\t * \\param c camera\n\t\t * \\param w image width\n\t\t * \\param h image height\n\t\t */\n\t\tInputCamera(const Camera& c, int w, int h);\n\n\t\t/** Copy constructor. */\n\t\tInputCamera(const InputCamera&) = default;\n\n\t\t/** Move constructor. */\n\t\tInputCamera(InputCamera&&) = default;\n\n\t\t/** Copy operator. */\n\t\tInputCamera&\toperator =(const InputCamera&) = default;\n\n\t\t/** Move operator. */\n\t\tInputCamera&\toperator =(InputCamera&&) = default;\n\n\t\t/** Input image width\n\t\t* \\return width of input image\n\t\t*/\n\t\tuint w(void) const;\n\n\t\t/** Input image height\n\t\t* \\return height of input image\n\t\t*/\n\t\tuint h(void) const;\n\n\t\t/** Check if the input camera active or inactive,\n\t\t* camera is completely ignored if set to inactive.\n\t\t* \\return true if active, false otherwise\n\t\t*/\n\t\tbool isActive(void) const;\n\n\t\t/** Set camera active status\n\t\t *\\param active if true, camera is in use\n\t     */\n\t\tvoid setActive(bool active) { _active = active ; }\n\n\t\t/** \\return the image name */\n\t\tinline const std::string&\tname(void) const { return _name; }\n\n\t\t/** Set camera name \n\t\t * \\param s the new name\n\t\t */\n\t\tinline void\t\t\t\t\tname( const std::string& s ) { _name = s; }\n\n\t\t/** Update image dimensions. Calls \\a update() after changing image width and height\n\t\t* \\param w image width\n\t\t* \\param h image height\n\t\t*/\n\t\tvoid size( uint w, uint h );\n\n\t\t/** \\return the camera id */\n\t\tuint id() const { return _id; }\n\n\t\t/** Project a world space point into screen space.\n\t\t *\\param pt 3d world point\n\t\t *\\return screen space position and depth, in (0,w)x(0,h)x(0,1)\n\t\t */\n\t\tVector3f projectScreen( const Vector3f& pt ) const;\n\n\t\t/** \\return the focal length */\n\t\tfloat focal() const;\n\n\t\t/** \\return the focal length x */\n\t\tfloat focalx() const;\n\n\t\t/** set the focal length ; to be used with caution; focal is usually inferred from the fov*/\n\t\tvoid setFocal(float focal) { _focal = focal; }\n\n\t\t/** \\return the k1 distorsion parameter */\n\t\tfloat k1() const;\n\n\t\t/** \\return the k2 distorsion parameter */\n\t\tfloat k2() const;\n\n\t\t/** Back-project pixel coordinates and depth.\n\t\t* \\param pixelPos pixel coordinates p[0],p[1] in [0,w-1]x[0,h-1] \n\t\t* \\param depth d in [-1,1]\n\t\t* \\returns 3D world point\n\t\t*/\n\t\tVector3f\t\t\tunprojectImgSpaceInvertY( const sibr::Vector2i & pixelPos, const float & depth ) const;\n\n\t\t/** Project 3D point using perspective projection.\n\t\t* \\param point3d 3D point\n\t\t* \\returns pixel coordinates in [0,w-1]x[0,h-1] and depth d in [-1,1]\n\t\t*/\n\t\tVector3f\t\t\tprojectImgSpaceInvertY( const Vector3f& point3d  ) const;\n\n\t\t/** Load from internal binary representation.\n\t\t * \\param filename file path\n\t\t * \\return success boolean\n\t\t */\n\t\tbool\t\t\t\tloadFromBinary( const std::string& filename );\n\n\t\t/** Save to disk using internal binary representation.\n\t\t * \\param filename file path\n\t\t */\n\t\tvoid\t\t\t\tsaveToBinary( const std::string& filename ) const;\n\n\t\t/** Save a file in the IBR TopView format.\n\t\t* \\param outfile the destination file\n\t\t*/\n\t\tvoid\t\t\t\twriteToFile(std::ostream& outfile) const;\n\n\t\t/** Load a file in the IBR TopView format.\n\t\t* \\param infile the input file\n\t\t*/\n\t\tvoid\t\t\t\treadFromFile(std::istream& infile);\n\n\t\t/** Conver to Bundle string.\n\t\t * \\param negativeZ should the Z axis be flipped\n\t\t * \\recomputeFocal recompute the focal or just set\n\t\t * \\return a string that can be used to create a bundle file from this camera\n\t\t*/\n\t\tstd::string toBundleString(bool negativeZ = false, bool recomputeFocal = true) const;\n\n\n\t\t/** \\return A vector of four Vector2i corresponding to the pixels at the camera corners\n\t\t*/\n\t\tstd::vector<sibr::Vector2i> getImageCorners() const;\n\n\t\t/** Return a new camera resized to the specified height\n\t\t*/\n\t\tsibr::InputCamera resizedH(int h) const;\n\t\t/** Return a new camera resized to the specified height\n\t\t*/\n\t\tsibr::InputCamera resizedW(int w) const;\n\n\t\t/** Return the lookat string of the camera\n\t\t*/\n\t\tstd::string lookatString() const;\n\t\t/** save a vector of cameras as lookat\n\t\t*/\n\t\tstatic void saveAsLookat(const std::vector<InputCamera::Ptr> & cams, const std::string & fileName);\n\t\t/** save a vector of cameras sizes to a file to be read by mitsuba rendering script\n\t\t*/\n\t\tstatic void saveImageSizes(const std::vector<InputCamera::Ptr> & cams, const std::string & fileName);\n\n\n\t\t/** Save a vector of cameras as a bundle file.\n\t\t *\\param cams the cameras\n\t\t * \\param fileName output bundle file path\n\t\t * \\param negativeZ should the Z axis be flipped\n\t\t * \\param exportImages should empty images with the proper dimensions be saved in a visualize subdirectory\n\t\t * \\param oldFocal: recompute focal, else assign that of camera TODO: fix this\n\t\t*/\n\t\tstatic void saveAsBundle(const std::vector<InputCamera::Ptr> & cams, const std::string & fileName, bool negativeZ = false, bool exportImages = false, bool recomputeFocal=true);\n\n\t\t/** Save a vector of cameras as a lookat file.\n\t\t *\\param cams the cameras\n\t\t * \\param fileName output lookat file path\n\t\t*/\n\t\tstatic void saveAsLookat(const std::vector<sibr::Camera> & cams, const std::string & fileName);\n\n\t\t/** Load cameras from a bundler file.\n\t\t *\\param datasetPath path to the root of the dataset, should contain bundle.out, list_images.txt and optionally clipping_planes.txt \n\t\t * \\param zNear default near-plane value to use if the clipping_planes.txt file doesn't exist\n\t\t * \\param zFar default far-plane value to use if the clipping_planes.txt file doesn't exist\n\t\t * \\param bundleName name of the bundle file\n\t\t * \\param listName name of the list images file\n\t\t * \\returns the loaded cameras\n\t\t */\n\t\tstatic std::vector<InputCamera::Ptr> load( const std::string& datasetPath, float zNear = 0.01f, float zFar = 1000.0f, const std::string & bundleName = \"bundle.out\", const std::string & listName = \"list_images.txt\");\n\n\t\t/** Load cameras from a NVM file.\n\t\t*\\param nvmPath path to the NVM file\n\t\t* \\param zNear default near-plane value to use\n\t\t* \\param zFar default far-plane value to use.\n\t\t* \\param wh will contain the sizes of each camera image\n\t\t* \\returns the loaded cameras\n\t\t*/\n\t\tstatic std::vector<InputCamera::Ptr> loadNVM(const std::string& nvmPath, float zNear = 0.01f, float zFar = 1000.0f, std::vector<sibr::Vector2u> wh = std::vector<sibr::Vector2u>());\n\n\t\t/** Load cameras from a .lookat file generated by our Blender plugin.\n\t\t* \\param lookatPath path to the lookAt file\n\t\t* \\param wh indicative size of each camera image\n\t\t* \\param zNear default near-plane value to use\n\t\t* \\param zFar default far-plane value to use.\n\t\t* \\returns the loaded cameras\n\t\t*/\n\t\tstatic std::vector<InputCamera::Ptr> loadLookat(const std::string& lookatPath, const std::vector<sibr::Vector2u>& wh= std::vector<sibr::Vector2u>(),float zNear= -1, float zFar= -1);\n\n\t\tstatic std::vector<InputCamera::Ptr> InputCamera::loadTransform(const std::string& transformPath, int w, int h, std::string extension, const float zNear = 0.01f, const float zFar = 1000.0f, const int offset = 0, const int fovXfovYFlag = 0);\n\n\t\t/** Load cameras from a Colmap txt file.\n\t\t* \\param colmapSparsePath path to the Colmap sparse directory, should contains cameras.txt and images.txt\n\t\t* \\param zNear default near-plane value to use\n\t\t* \\param zFar default far-plane value to use.\n\t\t* \\param fovXfovYFlag should we use two dimensional fov.\n\t\t* \\returns the loaded cameras\n\t\t* \\note the camera frame is internally transformed to be consistent with fribr and RC.\n\t\t*/\n\t\tstatic std::vector<InputCamera::Ptr> loadColmap(const std::string& colmapSparsePath, const float zNear = 0.01f, const float zFar = 1000.0f, const int fovXfovYFlag = 0);\n\n\t\tstatic std::vector<InputCamera::Ptr> loadColmapFromHttp(const std::string& camera_str, const std::string& image_str, const float zNear, const float zFar, const int fovXfovYFlag);\n\n\t\tstatic std::vector<InputCamera::Ptr> loadColmapBin(const std::string& colmapSparsePath, const float zNear = 0.01f, const float zFar = 1000.0f, const int fovXfovYFlag = 0);\n\n\t\tstatic std::vector<InputCamera::Ptr> loadJSON(const std::string& jsonPath, const float zNear = 0.01f, const float zFar = 1000.0f);\n\n\t\t/** Load cameras from a bundle file.\n\t\t* \\param bundlerPath path to the bundle file.\n\t\t* \\param zNear default near-plane value to use\n\t\t* \\param zFar default far-plane value to use.\n\t\t* \\param listImagePath path to the list_images.txt file. Will default to a file in the same directory as the bundle.out file.\n\t\t* \\param path: if this is a path, then you can load more images that those defined in the list_images file of the datset; TODO: possibly should require a list_images.txt for the path\n\t\t* \\returns the loaded cameras\n\t\t*/\n\t\tstatic std::vector<InputCamera::Ptr> loadBundle(const std::string& bundlerPath,  float zNear = 0.01f, float zFar = 1000.0f, const std::string & listImagePath = \"\", bool path = false);\n\n\t\t/** Load cameras from a bundle file.\n\t\t* \\param bundlerPath path to the bundle file.\n\t\t* \\param zNear default near-plane value to use\n\t\t* \\param zFar default far-plane value to use.\n\t\t* \\param listImagePath path to the list_images.txt file. Will default to a file in the same directory as the bundle.out file.\n\t\t* \\returns the loaded cameras\n\t\t*/\n\t\tstatic std::vector<InputCamera::Ptr> loadBundleFRIBR(const std::string& bundlerPath, float zNear = 0.01f, float zFar = 1000.0f, const std::string & listImagePath = \"\");\n\n\t\t/** Load cameras from a Meshrrom SFM cameras.sfm txt file.\n\t\t* \\param meshroomSFMPath path to the Meshroom StructureFromMotion/{dd63cea98bda0e3b53ec76f17b0753b3e4dde589}/ directory, should contains cameras.sfm \n\t\t* \\param zNear default near-plane value to use\n\t\t* \\param zFar default far-plane value to use.\n\t\t* \\returns the loaded cameras\n\t\t* \\note the camera frame is internally transformed to be consistent with fribr and RC.\n\t\t*/\n\t\tstatic std::vector<InputCamera::Ptr> loadMeshroom(const std::string& meshroomSFMPath, const float zNear = 0.01f, const float zFar = 1000.0f);\n\n\t\tuint _id; ///< Input camera id\n\n\tprotected:\n\n\t\tfloat _focal; ///< focal length\n\t\tfloat _focalx; ///< focal length x, if there is one (colmap typically; -1 by default use with caution)\n\t\tfloat _k1; ///< K1 bundler distorsion parameter\n\t\tfloat _k2; ///< K2 bundler dist parameter\n\t\tuint _w; ///< Image width\n\t\tuint _h; ///< Image height\n\t\tstd::string _name; ///< Input image name\n\t\tbool _active; ///< is the camera currently in use.\n\t};\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/Resources.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <iostream>\n#include <fstream>\n#include <algorithm>\n#include <sstream>\n#include \"core/assets/Resources.hpp\"\n\n/// \\todo TODO: If you care about security (did someone want to hack/use your app\n/// to hide a virus/retrieve informations from this compiled code), comment\n/// the following line and resolve warnings by finding new safe-functions.\n#pragma warning(disable:4996) // affect this .cpp only\n\nnamespace sibr\n{\n\n\tResources* Resources::_instance = NULL;\n\n\tResources* Resources::Instance()\n\t{\n\t\tif (_instance == 0)\n\t\t\t_instance = new Resources;\n\t\treturn _instance;\n\t}\n\n\tResources::Resources()\n\t{\n\t\t_rscPaths.push_back(sibr::getInstallDirectory());\n\t\tstd::ifstream rscFile(sibr::getInstallDirectory() + \"/ibr_resources.ini\");\n\n\t\tif(rscFile.good())\n\t\t{\n\t\t\tfor(std::string line; safeGetline(rscFile, line); )\n\t\t\t{\n\t\t\t\t_rscPaths.push_back(line);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tstd::ifstream rscFile2(sibr::getInstallDirectory() + \"/bin/ibr_resources.ini\");\n\t\t\tfor(std::string line; safeGetline(rscFile2, line); )\n\t\t\t\t_rscPaths.push_back(line);\n\t\t}\n\n\t\t/// \\todo WIP: used in prevision to load plugins (TODO: test under linux)\n\t\tstd::ifstream pathFile(sibr::getInstallDirectory() + \"/ibr_paths.ini\");\n\t\tif(pathFile.good())\n\t\t{\n\t\t\tfor(std::string line; safeGetline(pathFile, line); )\n\t\t\t{\n\t\t\t\tstd::string name    = line.substr(0, line.find(\"=\"));\n\t\t\t\tstd::string value   = line.substr(line.find(\"=\")+1, line.length());\n\t\t\t\tchar* curEnv = getenv(name.c_str());\n\t\t\t\tstd::string currentEnv;\n\t\t\t\tif(curEnv!=NULL)\n\t\t\t\t\tcurrentEnv = std::string(curEnv);\n#ifdef SIBR_OS_WINDOWS\n\t\t\t\tstd::replace(value.begin(), value.end(), '/', '\\\\'); // linux to windows path\n\t\t\t\tchar delimiter = ';';\n#else\n\t\t\t\tstd::replace(value.begin(), value.end(), '\\\\', '/'); // windows to linux path\n\t\t\t\tchar delimiter = ':';\n#endif\n\t\t\t\tstd::stringstream ss;\n\t\t\t\tss << delimiter;\n\t\t\t\tif(!currentEnv.empty())\n\t\t\t\t\tif (currentEnv.at(currentEnv.length()-1) != delimiter)\n\t\t\t\t\t\tcurrentEnv.append(ss.str());    \n\n\t\t\t\tline = name + \"=\" + currentEnv + value;\n\t\t\t\tputenv(const_cast<char*>(line.c_str()));\n\n\t\t\t\tstd::cout<<\"[Resources] env: \"<<name<<\"=\"<<getenv(name.c_str())<<std::endl;\n\t\t\t}\n\t\t}\n\t}\n\n\tResources::~Resources()\n\t{\n\t}\n\n\tstd::string Resources::getResourceFilePathName(std::string const & filename, bool & success)\n\t{\n\t\t// we assume the first element of _rscPaths if the current dir\n\t\t// Weird bug -- GD: I have no idea why, but if I dont call this the paths dont work under linux\n\t\tstd::string installdir = sibr::getInstallDirectory();\n\t\t// someone gave us the correct full path\n\t\tstd::ifstream rscFileTest(filename);\n\t\tif (success = rscFileTest.good()) \n\t\t\treturn filename;\n\t\tfor(std::string rscPath : _rscPaths)\n\t\t{\n\t\t\tstd::string filePathName  = sibr::getInstallDirectory() + \"/\" + rscPath + \"/\" + filename;\n\t\t\tstd::ifstream rscFile(filePathName);\n\t\t\tif (success = rscFile.good()) {\n\t\t\t\treturn filePathName;\n\t\t\t}\n\t\t}\n\t\treturn filename;\n\t}\n\n\tstd::string Resources::getResourceFilePathName(std::string const & filename)\n\t{\n\t\tbool success = false;\n\t\treturn getResourceFilePathName(filename,success);\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/Resources.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include \"core/assets/Config.hpp\"\r\n\r\n#include <vector>\r\n#include <string>\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/** Singleton used to store a list of plausible path to look for (based on the ibr_resources.ini)\r\n\t\\ingroup sibr_assets\r\n\t*/\r\n\tclass SIBR_ASSETS_EXPORT Resources\r\n\t{\r\n\tpublic:\r\n\t\t/// Our singleton\r\n\t\tstatic Resources* Instance();\r\n\r\n\tprotected:\r\n\t\t/// Constructor.\r\n\t\tResources();\r\n\r\n\t\t/// Destructor\r\n\t\tvirtual ~Resources();\r\n\r\n\tpublic:\r\n\t\t/** Look for the filename into plausible resource paths.\r\n\t\t * \\param filename file name\r\n\t\t * \\param success was the file found in the registered locations\r\n\t\t * \\return the full file path\r\n\t\t */\r\n\t\tstd::string getResourceFilePathName(std::string const & filename, bool & success);\r\n\r\n\t\t/** Look for the filename into plausible resource paths.\r\n\t\t * \\param filename file name\r\n\t\t * \\return the full file path\r\n\t\t */\r\n\t\tstd::string getResourceFilePathName(std::string const & filename);\r\n\r\n\tprotected:\r\n\t\tstd::vector<std::string>    _rscPaths; ///< List of directories to check into.\r\n\t\tstatic Resources *          _instance; ///< Singleton.\r\n\t};\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/UVUnwrapper.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"UVUnwrapper.hpp\"\n#include <core/system/SimpleTimer.hpp>\n#include <core/graphics/Utils.hpp>\n#include \"xatlas.h\"\n\nint printCallback(const char * format, ...) {\n\tva_list args;\n\tva_start(args, format);\n\tstd::cout << \"\\r\";\n\tconst int res = vprintf(format, args);\n\tva_end(args);\n\treturn res;\n}\n\nbool progressCallback(xatlas::ProgressCategory category, int progress, void *userData){\n\tstd::cout << \"\\r\\t\" << xatlas::StringForEnum(category) << \"[\" << std::flush;\n\tfor (int i = 0; i < 10; i++)\n\t\tstd::cout << (progress / ((i + 1) * 10) ? \"*\" : \" \");\n\tstd::cout << \"] \" << progress << \"%\" << std::flush;\n\tif(progress == 100) {\n\t\tstd::cout << std::endl;\n\t}\n\treturn true;\n}\n\nvoid setPixel(uint8_t *dest, int destWidth, int x, int y, const sibr::Vector3ub & color){\n\tuint8_t *pixel = &dest[x * 3 + y * (destWidth * 3)];\n\tpixel[0] = color[0];\n\tpixel[1] = color[1];\n\tpixel[2] = color[2];\n}\n\n// https://github.com/miloyip/line/blob/master/line_bresenham.c\n// License: public domain.\nstatic void rasterizeLine(uint8_t *dest, int destWidth, const int *p1, const int *p2, const sibr::Vector3ub & color)\n{\n\tconst int dx = abs(p2[0] - p1[0]), sx = p1[0] < p2[0] ? 1 : -1;\n\tconst int dy = abs(p2[1] - p1[1]), sy = p1[1] < p2[1] ? 1 : -1;\n\tint err = (dx > dy ? dx : -dy) / 2;\n\tint current[2];\n\tcurrent[0] = p1[0];\n\tcurrent[1] = p1[1];\n\twhile(setPixel(dest, destWidth, current[0], current[1], color), current[0] != p2[0] || current[1] != p2[1]){\n\t\tconst int e2 = err;\n\t\tif (e2 > -dx) { err -= dy; current[0] += sx; }\n\t\tif (e2 < dy) { err += dx; current[1] += sy; }\n\t}\n}\n\n/*\nhttps://github.com/ssloy/tinyrenderer/wiki/Lesson-2:-Triangle-rasterization-and-back-face-culling\nCopyright Dmitry V. Sokolov\n\nThis software is provided 'as-is', without any express or implied warranty.\nIn no event will the authors be held liable for any damages arising from the use of this software.\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it freely,\nsubject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n*/\nvoid rasterizeTriangle(uint8_t *dest, int destWidth, const int *t0, const int *t1, const int *t2, const sibr::Vector3ub & color)\n{\n\tif (t0[1] > t1[1]) std::swap(t0, t1);\n\tif (t0[1] > t2[1]) std::swap(t0, t2);\n\tif (t1[1] > t2[1]) std::swap(t1, t2);\n\tconst int total_height = t2[1] - t0[1];\n\tfor (int i = 0; i < total_height; i++) {\n\t\tconst bool second_half = i > t1[1] - t0[1] || t1[1] == t0[1];\n\t\tconst int segment_height = second_half ? t2[1] - t1[1] : t1[1] - t0[1];\n\t\tconst float alpha = (float)i / total_height;\n\t\tconst float beta = (float)(i - (second_half ? t1[1] - t0[1] : 0)) / float(segment_height);\n\t\tint A[2], B[2];\n\t\tfor (int j = 0; j < 2; j++) {\n\t\t\tA[j] = int(t0[j] + (t2[j] - t0[j]) * alpha);\n\t\t\tB[j] = int(second_half ? t1[j] + (t2[j] - t1[j]) * beta : t0[j] + (t1[j] - t0[j]) * beta);\n\t\t}\n\t\tif (A[0] > B[0]) std::swap(A, B);\n\t\tfor (int j = A[0]; j <= B[0]; j++) {\n\t\t\tsetPixel(dest, destWidth, j, t0[1] + i, color);\n\t\t}\n\t}\n}\n\nusing namespace sibr;\n\nUVUnwrapper::UVUnwrapper(const sibr::Mesh& mesh, unsigned int res) : _mesh(mesh) {\n\t_size = res;\n\t// Create empty atlas.\n\txatlas::SetPrint(printCallback, false);\n\t_atlas = xatlas::Create();\n\txatlas::SetProgressCallback(_atlas, progressCallback, nullptr);\n\n\t// Add the mesh to the atlas.\n\tSIBR_LOG << \"[UVMapper] Adding one mesh with \" << mesh.vertices().size() << \" vertices and \" << mesh.triangles().size() << \" triangles.\" << std::endl;\n\t// For now consider everything as one mesh. Splitting in components *might* help.\n\txatlas::MeshDecl meshDecl;\n\tmeshDecl.vertexCount = uint32_t(mesh.vertices().size());\n\tmeshDecl.vertexPositionData = mesh.vertexArray();\n\tmeshDecl.vertexPositionStride = sizeof(sibr::Vector3f);\n\tif (mesh.hasNormals()) {\n\t\tmeshDecl.vertexNormalData = mesh.normalArray();\n\t\tmeshDecl.vertexNormalStride = sizeof(sibr::Vector3f);\n\t}\n\t// UV can be used as a hint.\n\tif (mesh.hasTexCoords()) {\n\t\tmeshDecl.vertexUvData = mesh.texCoordArray();\n\t\tmeshDecl.vertexUvStride = sizeof(sibr::Vector2f);\n\t}\n\tmeshDecl.indexCount = uint32_t(mesh.triangles().size() * 3);\n\tmeshDecl.indexData = mesh.triangleArray();\n\tmeshDecl.indexFormat = xatlas::IndexFormat::UInt32;\n\tconst xatlas::AddMeshError error = xatlas::AddMesh(_atlas, meshDecl, 1);\n\tif (error != xatlas::AddMeshError::Success) {\n\t\txatlas::Destroy(_atlas);\n\t\tSIBR_ERR << \"\\r[UVMapper] Error adding mesh: \" << xatlas::StringForEnum(error) << std::endl;\n\t}\n\t// Not necessary. Only called here so geometry totals are printed after the AddMesh progress indicator\n\txatlas::AddMeshJoin(_atlas);\n}\n\n\t\nsibr::Mesh::Ptr UVUnwrapper::unwrap() {\n\n\t// Generate atlas.\n\tSIBR_LOG << \"[UVMapper] Generating atlas..\" << std::endl;\n\n\txatlas::ChartOptions chartOptions = xatlas::ChartOptions();\n\txatlas::PackOptions packOptions = xatlas::PackOptions();\n\tpackOptions.bruteForce = false;\n\tpackOptions.resolution = uint32_t(_size);\n\tTimer timer;\n\ttimer.tic();\n\txatlas::Generate(_atlas, chartOptions, packOptions);\n\n\tSIBR_LOG << \"[UVMapper] Generation took: \" << timer.deltaTimeFromLastTic<Timer::s>() << \"s.\" << std::endl;\n\tSIBR_LOG << \"[UVMapper] Output resolution: \" << _atlas->width << \"x\" << _atlas->height << std::endl;\n\tSIBR_LOG << \"[UVMapper] Generated \" << _atlas->chartCount << \" charts, \" << _atlas->atlasCount << \" atlases.\" << std::endl;\n\tfor (uint32_t i = 0; i < _atlas->atlasCount; i++) {\n\t\tSIBR_LOG << \"[UVMapper] \\tAtlas \" << i << \": utilisation: \" << _atlas->utilization[i] * 100.0f << \"%\" << std::endl;\n\t}\n\n\n\tuint32_t totalVertices = 0;\n\tuint32_t totalFaces = 0;\n\tfor (uint32_t i = 0; i < _atlas->meshCount; i++) {\n\t\tconst xatlas::Mesh& xmesh = _atlas->meshes[i];\n\t\ttotalVertices += xmesh.vertexCount;\n\t\ttotalFaces += xmesh.indexCount / 3;\n\t}\n\tSIBR_LOG << \"[UVMapper] Output geometry data: \" << totalVertices << \" vertices, \" << totalFaces << \" triangles.\" << std::endl;\n\t// Write meshes.\n\tuint32_t firstVertex = 0;\n\tstd::vector<sibr::Vector3f> positions;\n\tstd::vector<sibr::Vector3f> normals;\n\tstd::vector<sibr::Vector2f> texcoords;\n\tstd::vector<sibr::Vector3f> colors;\n\tstd::vector<sibr::Vector3u> triangles;\n\t\n\t// We could preallocate and paraellize if needed.\n\tfor (uint32_t i = 0; i < _atlas->meshCount; i++) {\n\t\tconst xatlas::Mesh& xmesh = _atlas->meshes[i];\n\t\tfor (uint32_t v = 0; v < xmesh.vertexCount; v++) {\n\t\t\tconst xatlas::Vertex& vertex = xmesh.vertexArray[v];\n\t\t\tconst sibr::Vector3f& pos = _mesh.vertices()[vertex.xref];\n\t\t\tpositions.emplace_back(pos);\n\t\t\tif (_mesh.hasNormals()) {\n\t\t\t\tconst sibr::Vector3f& n = _mesh.normals()[vertex.xref];\n\t\t\t\tnormals.emplace_back(n);\n\t\t\t}\n\t\t\tif (_mesh.hasColors()) {\n\t\t\t\tconst sibr::Vector3f& c = _mesh.colors()[vertex.xref];\n\t\t\t\tcolors.emplace_back(c);\n\t\t\t}\n\t\t\t\n\t\t\t_mapping.emplace_back(vertex.xref);\n\t\t\ttexcoords.emplace_back(vertex.uv[0] / float(_atlas->width), vertex.uv[1] / float(_atlas->height));\n\t\t}\n\t\tfor (uint32_t f = 0; f < xmesh.indexCount; f += 3) {\n\t\t\tconst uint32_t i0 = firstVertex + xmesh.indexArray[f + 0];\n\t\t\tconst uint32_t i1 = firstVertex + xmesh.indexArray[f + 1];\n\t\t\tconst uint32_t i2 = firstVertex + xmesh.indexArray[f + 2];\n\t\t\ttriangles.emplace_back(i0, i1, i2);\n\t\t}\n\t\tfirstVertex += xmesh.vertexCount;\n\t}\n\tMesh::Ptr finalMesh(new Mesh(false));\n\tfinalMesh->vertices(positions);\n\tfinalMesh->normals(normals);\n\tfinalMesh->texCoords(texcoords);\n\tfinalMesh->colors(colors);\n\tfinalMesh->triangles(triangles);\n\n\tSIBR_LOG << \"[UVMapper] Done.\" << std::endl;\n\treturn finalMesh;\n}\n\nconst std::vector<uint>& UVUnwrapper::mapping() const\n{\n\treturn _mapping;\n}\n\n\nstd::vector<ImageRGB::Ptr> UVUnwrapper::atlasVisualization() const {\n\tif(!_atlas || _atlas->width <= 0 || _atlas->height <= 0) {\n\t\tSIBR_WRG << \"[UVMapper] Atlas has not been created/processed.\" << std::endl;\n\t\treturn {};\n\t}\n\n\tSIBR_LOG << \"[UVMapper] Rasterizing result maps...\" << std::endl;\n\t\n\t// Rasterize unwrapped meshes.\n\t// \\todo port to SIBR image API.\n\tstd::vector<uint8_t> outputChartsImage;\n\tconst uint32_t imageDataSize = _atlas->width * _atlas->height * 3;\n\toutputChartsImage.resize(_atlas->atlasCount * imageDataSize);\n\tfor (uint32_t i = 0; i < _atlas->meshCount; i++) {\n\t\tconst xatlas::Mesh &xmesh = _atlas->meshes[i];\n\t\tconst sibr::Vector3ub white = { 255, 255, 255 };\n\t\t// Rasterize mesh charts.\n\t\tfor (uint32_t j = 0; j < xmesh.chartCount; j++) {\n\t\t\tconst xatlas::Chart *chart = &xmesh.chartArray[j];\n\t\t\tconst sibr::Vector3ub color = sibr::randomColor<unsigned char>();\n\t\t\tfor (uint32_t k = 0; k < chart->faceCount; k++) {\n\t\t\t\tint verts[3][2];\n\t\t\t\tfor (int l = 0; l < 3; l++) {\n\t\t\t\t\tconst xatlas::Vertex &v = xmesh.vertexArray[xmesh.indexArray[chart->faceArray[k] * 3 + l]];\n\t\t\t\t\tverts[l][0] = int(v.uv[0]);\n\t\t\t\t\tverts[l][1] = int(v.uv[1]);\n\t\t\t\t}\n\t\t\t\tuint8_t *imageData = &outputChartsImage[chart->atlasIndex * imageDataSize];\n\t\t\t\trasterizeTriangle(imageData, _atlas->width, verts[0], verts[1], verts[2], color);\n\t\t\t\trasterizeLine(imageData, _atlas->width, verts[0], verts[1], white);\n\t\t\t\trasterizeLine(imageData, _atlas->width, verts[1], verts[2], white);\n\t\t\t\trasterizeLine(imageData, _atlas->width, verts[2], verts[0], white);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Convert raw vectors to images.\n\tstd::vector<ImageRGB::Ptr> views(_atlas->meshCount);\n\tfor (uint32_t i = 0; i < _atlas->meshCount; i++) {\n\t\tviews[i].reset(new ImageRGB(_atlas->width, _atlas->height));\n\t\tuint8_t *imageData = &outputChartsImage[i * imageDataSize];\n#pragma omp parallel for\n\t\tfor(int y = 0; y < int(_atlas->height); ++y) {\n\t\t\tfor(int x = 0; x < int(_atlas->width); ++x) {\n\t\t\t\tconst size_t baseId = (y * _atlas->width + x)*3;\n\t\t\t\tfor(int j = 0; j < 3; ++j) {\n\t\t\t\t\tviews[i](x, y)[j] = imageData[baseId + j];\n\t\t\t\t}\t\t\n\t\t\t}\n\t\t}\n\t\tviews[i]->flipH();\n\t}\n\treturn views;\n}\n\nUVUnwrapper::~UVUnwrapper() {\n\txatlas::Destroy(_atlas);\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/UVUnwrapper.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <core/assets/Config.hpp>\n#include <core/graphics/Mesh.hpp>\n\nnamespace xatlas {\n\tstruct Atlas;\n}\n\nnamespace sibr\n{\n\t/** Unwraps a mesh onto a plane, generating texture coordinates for each vertex.\n\t * Internaly relies on xatlas for unwrapping.\n\t\\ingroup sibr_assets\n\t*/\n\tclass SIBR_ASSETS_EXPORT UVUnwrapper {\n\tpublic:\n\n\t\t/** Constructor.\n\t\t *\\param mesh the mesh to unwrap, if UVs are already present they will be used as a guide\n\t\t *\\param res the target texture width, will determine UV accuracy\n\t\t */\n\t\tUVUnwrapper(const sibr::Mesh& mesh, unsigned int res);\n\n\t\t/** Unwrap the mesh, return a copy with UV coordinates. Note that some vertices might be duplicated if they are assigned different UVs in two faces.\n\t\t * \\return the unwrapped mesh\n\t\t */\n\t\tsibr::Mesh::Ptr unwrap();\n\n\t\t/** For each vertex of the unwrapped mesh, the mapping give the index of the corresponding vertex in the input mesh.\n\t\t * \\return a reference to the mapping vector\n\t\t */\n\t\tconst std::vector<uint> & mapping() const;\n\t\t\n\t\t/** Generate debug visualization by rasterizing the meshes in texture space.\n\t\t * \\return a set of images, one per atlas\n\t\t */\n\t\tstd::vector<ImageRGB::Ptr> atlasVisualization() const;\n\n\t\t/// Destructor.\n\t\t~UVUnwrapper();\n\t\t\n\tprivate:\n\t\t\n\t\tconst sibr::Mesh& _mesh; ///< Unwrapped mesh.\n\t\tunsigned int _size; ///< Width of the atlas, detemrine the accuracy of the estimated UVs.\n\t\txatlas::Atlas* _atlas; ///< Atlas object.\n\t\tstd::vector<uint> _mapping; ///< Mapping from the new vertices to the old (some might be duplicated with different UV values).\n\t\t\n\t};\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/colmapheader.h",
    "content": "// Copyright (c) 2022, ETH Zurich and UNC Chapel Hill.\r\n// All rights reserved.\r\n//\r\n// Redistribution and use in source and binary forms, with or without\r\n// modification, are permitted provided that the following conditions are met:\r\n//\r\n//     * Redistributions of source code must retain the above copyright\r\n//       notice, this list of conditions and the following disclaimer.\r\n//\r\n//     * Redistributions in binary form must reproduce the above copyright\r\n//       notice, this list of conditions and the following disclaimer in the\r\n//       documentation and/or other materials provided with the distribution.\r\n//\r\n//     * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of\r\n//       its contributors may be used to endorse or promote products derived\r\n//       from this software without specific prior written permission.\r\n//\r\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\r\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\n// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE\r\n// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r\n// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r\n// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r\n// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r\n// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r\n// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r\n// POSSIBILITY OF SUCH DAMAGE.\r\n//\r\n// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)\r\n\r\n#ifndef COLMAP_SRC_UTIL_ENDIAN_H_\r\n#define COLMAP_SRC_UTIL_ENDIAN_H_\r\n\r\n#include <vector>\r\n#include <algorithm>\r\n#include <iostream>\r\n\r\n//namespace colmap {\r\n\r\n// Reverse the order of each byte.\r\ntemplate <typename T>\r\nT ReverseBytes(const T& data);\r\n\r\n// Check the order in which bytes are stored in computer memory.\r\nbool IsLittleEndian();\r\nbool IsBigEndian();\r\n\r\n// Convert data between endianness and the native format. Note that, for float\r\n// and double types, these functions are only valid if the format is IEEE-754.\r\n// This is the case for pretty much most processors.\r\ntemplate <typename T>\r\nT LittleEndianToNative(const T x);\r\ntemplate <typename T>\r\nT BigEndianToNative(const T x);\r\ntemplate <typename T>\r\nT NativeToLittleEndian(const T x);\r\ntemplate <typename T>\r\nT NativeToBigEndian(const T x);\r\n\r\n// Read data in little endian format for cross-platform support.\r\ntemplate <typename T>\r\nT ReadBinaryLittleEndian(std::istream* stream);\r\ntemplate <typename T>\r\nvoid ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data);\r\n\r\n// Write data in little endian format for cross-platform support.\r\ntemplate <typename T>\r\nvoid WriteBinaryLittleEndian(std::ostream* stream, const T& data);\r\ntemplate <typename T>\r\nvoid WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data);\r\n\r\n////////////////////////////////////////////////////////////////////////////////\r\n// Implementation\r\n////////////////////////////////////////////////////////////////////////////////\r\n\r\ntemplate <typename T>\r\nT ReverseBytes(const T& data) {\r\n  T data_reversed = data;\r\n  std::reverse(reinterpret_cast<char*>(&data_reversed),\r\n               reinterpret_cast<char*>(&data_reversed) + sizeof(T));\r\n  return data_reversed;\r\n}\r\n\r\ninline bool IsLittleEndian() {\r\n#ifdef BOOST_BIG_ENDIAN\r\n  return false;\r\n#else\r\n  return true;\r\n#endif\r\n}\r\n\r\ninline bool IsBigEndian() {\r\n#ifdef BOOST_BIG_ENDIAN\r\n  return true;\r\n#else\r\n  return false;\r\n#endif\r\n}\r\n\r\ntemplate <typename T>\r\nT LittleEndianToNative(const T x) {\r\n  if (IsLittleEndian()) {\r\n    return x;\r\n  } else {\r\n    return ReverseBytes(x);\r\n  }\r\n}\r\n\r\ntemplate <typename T>\r\nT BigEndianToNative(const T x) {\r\n  if (IsBigEndian()) {\r\n    return x;\r\n  } else {\r\n    return ReverseBytes(x);\r\n  }\r\n}\r\n\r\ntemplate <typename T>\r\nT NativeToLittleEndian(const T x) {\r\n  if (IsLittleEndian()) {\r\n    return x;\r\n  } else {\r\n    return ReverseBytes(x);\r\n  }\r\n}\r\n\r\ntemplate <typename T>\r\nT NativeToBigEndian(const T x) {\r\n  if (IsBigEndian()) {\r\n    return x;\r\n  } else {\r\n    return ReverseBytes(x);\r\n  }\r\n}\r\n\r\ntemplate <typename T>\r\nT ReadBinaryLittleEndian(std::istream* stream) {\r\n  T data_little_endian;\r\n  stream->read(reinterpret_cast<char*>(&data_little_endian), sizeof(T));\r\n  return LittleEndianToNative(data_little_endian);\r\n}\r\n\r\ntemplate <typename T>\r\nvoid ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data) {\r\n  for (size_t i = 0; i < data->size(); ++i) {\r\n    (*data)[i] = ReadBinaryLittleEndian<T>(stream);\r\n  }\r\n}\r\n\r\ntemplate <typename T>\r\nvoid WriteBinaryLittleEndian(std::ostream* stream, const T& data) {\r\n  const T data_little_endian = NativeToLittleEndian(data);\r\n  stream->write(reinterpret_cast<const char*>(&data_little_endian), sizeof(T));\r\n}\r\n\r\ntemplate <typename T>\r\nvoid WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data) {\r\n  for (const auto& elem : data) {\r\n    WriteBinaryLittleEndian<T>(stream, elem);\r\n  }\r\n}\r\n\r\n//}  // namespace colmap\r\n\r\n#include <algorithm>\r\n#include <cstdarg>\r\n#include <fstream>\r\n#include <sstream>\r\n\r\nbool IsNotWhiteSpace(const int character) {\r\n  return character != ' ' && character != '\\n' && character != '\\r' &&\r\n         character != '\\t';\r\n}\r\n\r\nbool StringStartsWith(const std::string& str, const std::string& prefix) {\r\n  return !prefix.empty() && prefix.size() <= str.size() &&\r\n         str.substr(0, prefix.size()) == prefix;\r\n}\r\n\r\nvoid StringLeftTrim(std::string* str) {\r\n  str->erase(str->begin(),\r\n             std::find_if(str->begin(), str->end(), IsNotWhiteSpace));\r\n}\r\n\r\nvoid StringRightTrim(std::string* str) {\r\n  str->erase(std::find_if(str->rbegin(), str->rend(), IsNotWhiteSpace).base(),\r\n             str->end());\r\n}\r\n\r\nvoid StringTrim(std::string* str) {\r\n  StringLeftTrim(str);\r\n  StringRightTrim(str);\r\n}\r\n\r\n#endif  // COLMAP_SRC_UTIL_ENDIAN_H_\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/assets/sibr_assets.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_assets sibr_assets\n\n\t\\brief Assets and files utilities.\n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nproject(sibr_graphics)\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\r\nsource_group(\"Source Files\" FILES ${SOURCES})\r\n\r\nfile(GLOB RESOURCES \"resources/*.ini\")\r\nsource_group(\"Resources Files\" FILES ${RESOURCES})\r\n\r\n## Specify target rules\r\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\r\n\r\ninclude_directories(\r\n\t${Boost_INCLUDE_DIRS}\r\n\t${imgui_INCLUDE_DIRS}\r\n)\r\nif(WIN32)\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\t${Boost_LIBRARIES}\r\n\t${ASSIMP_LIBRARIES}\r\n\t${GLEW_LIBRARIES}\r\n\t${OPENGL_LIBRARIES}\r\n\t${OpenCV_LIBRARIES}\r\n\tOpenMP::OpenMP_CXX\r\n\timgui\r\n\tglfw3\r\n\tsibr_system\r\n)\r\nelse()\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\t${Boost_LIBRARIES}\r\n\t${ASSIMP_LIBRARIES}\r\n\t${GLEW_LIBRARIES}\r\n\t${OPENGL_LIBRARIES}\r\n\t${OpenCV_LIBRARIES}\r\n\tOpenMP::OpenMP_CXX\r\n\timgui\r\n\t${GLFW_LIBRARY}\r\n\tsibr_system\r\n)\r\nendif()\r\n\r\nif (NOT WIN32)\r\n\ttarget_link_libraries(${PROJECT_NAME}\r\n \t\t#GLEW\r\n \t\trt m dl X11 pthread Xrandr Xinerama Xxf86vm Xcursor\r\n\t\t# X11 Xi Xrandr Xxf86vm Xinerama Xcursor dl rt m pthread\r\n\t)\r\nendif()\r\n\r\nadd_definitions(-DSIBR_GRAPHICS_EXPORTS -DIMGUI_EXPORTS -DBOOST_ALL_DYN_LINK)\r\n\r\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\r\n\r\n\r\n## High level macro to install in an homogen way all our ibr targets\r\ninclude(install_runtime)\r\nibr_install_target(${PROJECT_NAME}\r\n\tINSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\r\n\tRESOURCES  ${RESOURCES}\r\n\tRSC_FOLDER \"core\"\r\n)\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Camera.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"core/graphics/Camera.hpp\"\n\nnamespace sibr\n{\n\t\n\tByteStream&\t\toperator << (ByteStream& stream, const Camera& c )\n\t{\n\t\tCamera::Transform3f t = c.transform();\n\t\tfloat fovy = c.fovy();\n\t\tfloat aspect = c.aspect();\n\t\tfloat znear = c.znear();\n\t\tfloat zfar = c.zfar();\n\t\treturn stream\n\t\t\t<< t << fovy << aspect << znear << zfar;\n\t}\n\n\tByteStream&\t\toperator >> (ByteStream& stream, Camera& c )\n\t{\n\t\tCamera::Transform3f t;\n\t\tfloat fovy = 0.f;\n\t\tfloat aspect = 0.f;\n\t\tfloat znear = 0.f;\n\t\tfloat zfar = 0.f;\n\t\tstream\n\t\t\t>> t >> fovy >> aspect >> znear >> zfar;\n\t\tc.transform(t);\n\t\tc.fovy(fovy);\n\t\tc.aspect(aspect);\n\t\tc.znear(znear);\n\t\tc.zfar(zfar);\n\t\treturn stream;\n\t}\n\n\tvoid\tCamera::perspective( float fovRad, float ratio, float znear, float zfar )\n\t{\n\t\t_fov = fovRad;\n\t\t_aspect = ratio;\n\t\t_znear = znear;\n\t\t_zfar = zfar;\n\t\t_dirtyViewProj = true;\n\t}\n\t\n\tVector3f\t\t\tCamera::project( const Vector3f& p3d ) const\n\t{\n\t\tVector4f p4d;\n\t\tp4d[0] = p3d[0]; p4d[1] = p3d[1]; p4d[2] = p3d[2]; p4d[3] = 1.0;\n\t\tVector4f p3d_t = viewproj() * p4d;\n\t\tp3d_t = p3d_t / p3d_t[3];\n\t\t//p3d_t[2] = p3d_t[2]*0.5f + 0.5f; // [-1;1] to [0;1] // not used\n\t\treturn Vector3f(p3d_t[0], p3d_t[1], p3d_t[2]); // so return [-1;1]\n\t\t\n\t\t//p3d_t[2] = p3d_t[2]*0.5f + 0.5f; // [-1;1] to [0;1] // not used\n\t\t//return Vector3f(p3d_t[0], p3d_t[1], p3d_t[2]); // so return [-1;1]\n\t}\n\n\tVector3f\t\t\tCamera::unproject( const Vector3f& p3d ) const\n\t{\n\t\tVector4f p4d;\n\t\tp4d[0] = p3d[0]; p4d[1] = p3d[1]; p4d[2] = p3d[2]; p4d[3] = 1.0;\n\t\t//p4d[2] = p4d[2]*2.f - 1.f; // [0;1] to [-1;1]  // not used\n\t\tVector4f p3d_t = invViewproj() * p4d;//;viewproj().inverse() * p4d;\n\t\treturn Vector3f(p3d_t[0],p3d_t[1],p3d_t[2])/p3d_t[3];\n\t}\n\n\tbool\t\tCamera::frustumTest(const Vector3f& position3d, const Vector2f& pixel2d) const\n\t{\n\t\treturn (pixel2d.cwiseAbs().array() < (1.0f-1e-5f) ).all() && (dir().dot(position3d - position()) > 0);\n\t}\n\n\tbool\t\tCamera::frustumTest(const Vector3f& position3d) const\n\t{\n\t\treturn frustumTest(position3d, project(position3d).xy());\n\t}\n\n\tvoid\t\tCamera::forceUpdateViewProj( void ) const\n\t{\n\t\t_matViewProj = sibr::Matrix4f(proj()*view());\n\t\t//_matViewProj = proj()*view();\n\t\t_invMatViewProj = _matViewProj.inverse();\n\t\t_dirtyViewProj = false;\n\t}\n\n\tVector3f\t\t\tCamera::dir( void ) const\n\t{ \n\t\treturn quatRotateVec(rotation(), Vector3f( 0.f, 0.f,-1.f));\n\t}\n\n\tVector3f\t\t\tCamera::up( void ) const\t\n\t{ \n\t\treturn quatRotateVec(rotation(), Vector3f( 0.f, 1.f, 0.f));\n\t}\n\n\tVector3f\t\t\tCamera::right( void ) const\t\n\t{ \n\t\treturn quatRotateVec(rotation(), Vector3f( 1.f, 0.f, 0.f));\n\t}\n\n\tMatrix4f\tCamera::proj( void ) const\n\t{\n\t\t//std::cout << \"FOV: \" << _fov << \"Aspect\" << _aspect << \"ZNEAR\" << _znear << \"ZFAR\" << _zfar << std::endl << std::flush;\n\t\tif (ortho())\n\t\t\treturn sibr::orthographic(_right, _top, _znear, _zfar);\n\t\telse\n\t\t\treturn sibr::perspective(_fov, _aspect, _znear, _zfar, _p);\n\t}\n\n\t/*static*/ Camera\tCamera::interpolate( const Camera& from, const Camera& to, float dist01 )\n\t{\n\t\tdist01 = std::max(0.f, std::min(1.f, dist01));\n\t\tTransform3f t = Transform3f::interpolate(from._transform, to._transform, dist01);\n\t\tCamera out = from;\n\t\tout._transform = t;\n\t\tout.fovy(dist01*from.fovy() + (1.0f-dist01)*to.fovy());\n\t\tout.aspect(dist01*from.aspect() + (1.0f-dist01)*to.aspect());\n\t\tout.zfar(dist01*from.zfar() + (1.0f-dist01)*to.zfar());\n\t\tout.znear(dist01*from.znear() + (1.0f-dist01)*to.znear());\n\t\tif (from.ortho()) {\n\t\t\tout.orthoRight(dist01*from.orthoRight() + (1.0f-dist01)*to.orthoRight());\n\t\t\tout.orthoTop(dist01*from.orthoTop() + (1.0f-dist01)*to.orthoTop());\n\t\t}\n\t\treturn out;\n\t}\n\n\tvoid Camera::setStereoCam(bool isLeft, float focal, float iod) \n\t{\n\t\t_matViewProj = sibr::perspectiveStereo(_fov, _aspect, _znear, _zfar, focal, iod, isLeft)*view();\n\t\t_invMatViewProj = _matViewProj.inverse();\n\t}\n\n\tvoid Camera::setOrthoCam(float right, float top)\n\t{\n\t\t_matViewProj = sibr::orthographic(right,top,_znear,_zfar)*view();\n\t\t_invMatViewProj = _matViewProj.inverse();\n\t\t_dirtyViewProj = false;\n\t\t_isOrtho = true;\n\t\t_right = right;\n\t\t_top = top;\n\t}\n\t\n\tvoid \t\t\t\t\tCamera::transform( const Transform3f& t )\n\t{\n\t\t_transform = t;\n\t\t_dirtyViewProj = true;\n\t}\n\n} // namespace sibr"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Camera.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include \"core/system/Transform3.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\t/** Represent a basic camera.\r\n\t\\note In practice, InputCamera is used most of the time\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tclass SIBR_GRAPHICS_EXPORT Camera\r\n\t{\r\n\tpublic:\r\n\t\tSIBR_CLASS_PTR(Camera);\r\n\t\ttypedef Transform3<float>\t\tTransform3f;\r\n\r\n\tpublic:\r\n\r\n\t\t/// Default constructor.\r\n\t\tCamera( void ):\r\n\t\t\t_matViewProj(Matrix4f::Identity()), _invMatViewProj(Matrix4f::Identity()),\r\n\t\t\t_dirtyViewProj(true), _savePath(\"\"), _debugVideoFrames(false),\r\n\t\t\t_fov(70.f/180.0f*float(M_PI)), _aspect(1.f), _znear(0.01f), _zfar(1000.f), _right(1.0f), _top(1.0f), _isOrtho(false), _p(0.5f, 0.5f) { }\r\n\r\n\t\t/** Set the camera pose.\r\n\t\t\\param translation the camera translation\r\n\t\t\\param rotation the camera rotation\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\tset( const Vector3f& translation, const Quaternionf& rotation );\r\n\r\n\t\t/** Set the camera pose based on two points and a up vector.\r\n\t\t\\param eye the camera position\r\n\t\t\\param center the camera target point\r\n\t\t\\param up the camera up vector\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\tsetLookAt( const Vector3f& eye, const Vector3f& center, const Vector3f& up );\r\n\r\n\t\t/** Translate the camera.\r\n\t\t\\param v the translation\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\ttranslate( const Vector3f& v );\r\n\r\n\t\t/** Translate the camera with respect to a reference frame.\r\n\t\t\\param v the translation\r\n\t\t\\param ref the reference frame\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\ttranslate( const Vector3f& v, const Transform3f& ref );\r\n\r\n\t\t/** Set the camera position.\r\n\t\t\\param v the new position\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\tposition( const Vector3f& v );\r\n\r\n\t\t/** \\return the camer position. */\r\n\t\tconst Vector3f&\t\t\tposition( void ) const;\r\n\r\n\t\t/** Rotate the camera.\r\n\t\t\\param rotation the quaternion rotation to apply\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\trotate( const Quaternionf& rotation );\r\n\r\n\t\t/** Rotate the camera.\r\n\t\t\\param v the euler angles to apply\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\trotate( const Vector3f& v );\r\n\t\t\r\n\t\t/** Rotate the camera with respect to a reference frame.\r\n\t\t\\param v the rotation euler angles\r\n\t\t\\param ref the reference frame\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\trotate( const Vector3f& v, const Transform3f& ref );\r\n\r\n\t\t/** Set the camera rotation.\r\n\t\t\\param v the new rotation euler angles\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\trotation( const Vector3f& v );\r\n\r\n\t\t/** Set the camera rotation.\r\n\t\t\\param q the new rotation\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\trotation( const Quaternionf& q );\r\n\t\t\r\n\t\t/** \\return the camera rotation. */\r\n\t\tconst Quaternionf&\t\trotation( void ) const;\r\n\r\n\t\t/** Set the camera transform.\r\n\t\t\\param t the new transform\r\n\t\t*/\r\n\t\tvoid \t\t\t\t\ttransform( const Transform3f& t );\r\n\t\t\r\n\t\t/** \\return the camera transform. */\r\n\t\tconst Transform3f&\t\ttransform( void ) const;\r\n\r\n\t\t/////////////////////////////////////////////////////////////////\r\n\t\t///// ==================== Projection  ==================== /////\r\n\t\t/////////////////////////////////////////////////////////////////\r\n\r\n\t\t/** Set the vertical field of view (in radians).\r\n\t\t\\param value the new value\r\n\t\t*/\r\n\t\tvoid\t\t\t\tfovy( float value );\r\n\r\n\t\t/** \\return the vertical field of view (in radians). */\r\n\t\tfloat\t\t\t\tfovy( void ) const;\r\n\r\n\t\t/** Set the aspect ratio.\r\n\t\t\\param value the new value\r\n\t\t*/\r\n\t\tvoid\t\t\t\taspect( float value );\r\n\r\n\t\t/** \\return the aspect ratio. */\r\n\t\tfloat\t\t\t\taspect( void ) const;\r\n\r\n\t\t/** Set the near plane.\r\n\t\t\\param value the new value\r\n\t\t*/\r\n\t\tvoid\t\t\t\tznear( float value );\r\n\r\n\t\t/** \\return the near plane distance */\r\n\t\tfloat\t\t\t\tznear( void ) const;\r\n\r\n\t\t/** Set the far plane.\r\n\t\t\\param value the new value\r\n\t\t*/\r\n\t\tvoid\t\t\t\tzfar( float value );\r\n\r\n\t\t/** \\return the far plane distance */\r\n\t\tfloat\t\t\t\tzfar( void ) const;\r\n\r\n\t\t/** Set the right frustum extent.\r\n\t\t\\param value the new value\r\n\t\t*/\r\n\t\tvoid\t\t\t\torthoRight( float value );\r\n\r\n\t\t/** \\return the right frustum distance */\r\n\t\tfloat\t\t\t\torthoRight( void ) const;\r\n\r\n\t\t/** Set the top frustum extent.\r\n\t\t\\param value the new value\r\n\t\t*/\r\n\t\tvoid\t\t\t\torthoTop( float value );\r\n\r\n\t\t/** \\return the top frustum distance */\r\n\t\tfloat\t\t\t\torthoTop( void ) const;\r\n\r\n\t\t/** \\return true if the camera is orthographic. */\r\n\t\tbool\t\t\t\tortho(void) const;\r\n\r\n\t\t/** \\return the camera direction vector. */\r\n\t\tVector3f\t\t\tdir( void ) const;\r\n\r\n\t\t/** \\return the camera up vector. */\r\n\t\tVector3f\t\t\tup( void ) const;\r\n\r\n\t\t/** \\return the camera right vector. */\r\n\t\tVector3f\t\t\tright( void ) const;\r\n\r\n\t\t/** Project 3D point using perspective projection.\r\n\t\t* \\param point3d 3D point\r\n\t\t* \\return pixel coordinates in [-1,1] and depth in [-1,1]\r\n\t\t*/\r\n\t\tVector3f\t\t\tproject( const Vector3f& point3d ) const;\r\n\r\n\t\t/** Back-project pixel coordinates and depth.\r\n\t\t* \\param pixel2d pixel coordinates p[0],p[1] in [-1,1] and depth p[2] in [-1,1]\r\n\t\t* \\return 3D point\r\n\t\t*/\r\n\t\tVector3f\t\t\tunproject( const Vector3f& pixel2d ) const;\r\n\r\n\t\t/** Update the projection parameters of the camera.\r\n\t\t\\param fovRad the vertical field ov view in radians\r\n\t\t\\param ratio the aspect ratio\r\n\t\t\\param znear the near plane distance\r\n\t\t\\param zfar the far plane distance\r\n\t\t*/\r\n\t\tvoid\t\t\t\tperspective( float fovRad, float ratio, float znear, float zfar );\r\n\r\n\t\t/** Check if a point falls inside the camera frustum.\r\n\t\t\\param position3d the point location in 3D\r\n\t\t\\return true if the point falls inside\r\n\t\t*/\r\n\t\tbool\t\t\t\tfrustumTest(const Vector3f& position3d) const;\r\n\t\t\r\n\t\t/** Check if a point falls inside the camera frustum. Use this version if you already have the projected point.\r\n\t\t\\param position3d the point location in 3D\r\n\t\t\\param pixel2d the projection location of the point in image space ([-1,1])\r\n\t\t\\return true if the point falls inside\r\n\t\t*/\r\n\t\tbool\t\t\t\tfrustumTest(const Vector3f& position3d, const Vector2f& pixel2d) const;\r\n\r\n\t\t/** \\return the camera model matrix (for camera stub rendering for instance). */\r\n\t\tMatrix4f\t\t\tmodel( void ) const { return _transform.matrix(); }\r\n\r\n\t\t/** \\return the camera view matrix. */\r\n\t\tMatrix4f\t\t\tview( void ) const { return _transform.invMatrix(); }\r\n\r\n\t\t/** \\return the camera projection matrix. */\r\n\t\tvirtual Matrix4f\tproj( void ) const;\r\n\r\n\t\t/** \\return the camera view-proj matrix (cached). */\r\n\t\tconst Matrix4f&\t\tviewproj( void ) const;\r\n\r\n\t\t/** \\return the camera inverse view-proj matrix (cached). */\r\n\t\tconst Matrix4f&\t\tinvViewproj( void ) const;\r\n\r\n\t\t/** Set the camera principal point. \r\n\t\t\\param p the principal point, expressed in [0,1] \r\n\t\t*/\r\n\t\tvoid principalPoint(const sibr::Vector2f & p);\r\n\r\n\t\t/** Interpolate between two cameras.\r\n\t\t\\param from start camera\r\n\t\t\\param to end camera\r\n\t\t\\param dist01 the interpolation factor\r\n\t\t\\return a camera with interpolated parameters\r\n\t\t*/\r\n\t\tstatic Camera\t\tinterpolate( const Camera& from, const Camera& to, float dist01 );\r\n\r\n\t\t/** Set stereo camera projection parameters.\r\n\t\t\\param isLeft is the camera for the left eye (else right)\r\n\t\t\\param focal the focal distance\r\n\t\t\\param iod the inter ocular distance\r\n\t\t*/\r\n\t\tvoid \t\t\t\tsetStereoCam(bool isLeft, float focal, float iod);\r\n\r\n\t\t/** Set orthographic camera projection parameters.\r\n\t\t\\param right the right frustum extent\r\n\t\t\\param top the top frustum extent\r\n\t\t*/\r\n\t\tvoid\t\t\t\tsetOrthoCam(float right, float top);\r\n\r\n\t\t/** \\return true if the rendering generated with the camera be saved. */\r\n\t\tbool\t\t\t\tneedSave() const { return _savePath!=\"\"; }\r\n\r\n\t\t/**\\return true if the rendering generated with the camera be saved as a frame. */\r\n\t\tbool\t\t\t\tneedVideoSave() const { return _debugVideoFrames; }\r\n\r\n\t\t/** \\return the save destination path for renderings */\r\n\t\tstd::string\t\t\tsavePath() const { return _savePath; }\r\n\r\n\t\t/** Set the save destination path for renderings.\r\n\t\t\\param savePath the new path\r\n\t\t*/\r\n\t\tvoid\t\t\t\tsetSavePath(std::string savePath) { _savePath = savePath; }\r\n\r\n\t\t/** Toggle video saving.\r\n\t\t\\todo Cleanup naming.\r\n\t\t\\param debug if true, saving frames\r\n\t\t*/\r\n\t\tvoid\t\t\t\tsetDebugVideo(const bool debug) { _debugVideoFrames = debug; }\r\n\t\t\r\n\tprotected:\r\n\r\n\t\t/** Trigger a viewproj matrix udpate. */\r\n\t\tvoid\t\t\t\tforceUpdateViewProj( void ) const;\r\n\r\n\t\tstd::string\t\t\t\t_savePath; ///< Save destination path when reocrding images.\r\n\t\tbool\t\t\t\t\t_debugVideoFrames; ///< Is video saving enabled or not. \\todo Cleanup.\r\n\t\tmutable Matrix4f\t\t_matViewProj; ///< View projection matrix.\r\n\t\tmutable Matrix4f\t\t_invMatViewProj; ///< Inverse projection matrix.\r\n\t\tmutable bool\t\t\t_dirtyViewProj; ///< Does the camera matrix need an update.\r\n\r\n\t\tTransform3f\t\t_transform; ///< The camera pose.\r\n\t\tfloat\t\t\t_fov; ///< The vertical field of view (radians)\r\n\t\tfloat\t\t\t_aspect; ///< Aspect ratio.\r\n\t\tfloat\t\t\t_znear; ///< Near plane.\r\n\t\tfloat\t\t\t_zfar; ///< Far plane.\r\n\t\tfloat\t\t\t_right; ///< Frustum half width.\r\n\t\tfloat\t\t\t_top; ///< Frustum half height.\r\n\t\tsibr::Vector2f   _p = {0.5f, 0.5}; ///< Principal point.\r\n\t\tbool\t\t\t_isOrtho; ///< Is the camera orthographic.\r\n\t};\r\n\r\n\t/** Write a camera to a byte stream.\r\n\t\\param stream the stream to write to\r\n\t\\param c the camera\r\n\t\\return the stream (for chaining).\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT ByteStream&\t\toperator << (ByteStream& stream, const Camera& c );\r\n\r\n\t/** Read a camera from a byte stream.\r\n\t\\param stream the stream to read from\r\n\t\\param c the camera\r\n\t\\return the stream (for chaining).\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT ByteStream&\t\toperator >> (ByteStream& stream, Camera& c );\r\n\r\n\t///// DEFINITIONS /////\r\n\r\n\t/////////////////////////////////////////////////////////////////\r\n\tinline const Transform3f&\t\tCamera::transform( void ) const {\r\n\t\treturn _transform;\r\n\t}\r\n\r\n\tinline void\t\t\t\tCamera::set( const Vector3f& translation, const Quaternionf& rotation ) {\r\n\t\t_dirtyViewProj = true; _transform.set(translation, rotation);\r\n\t}\r\n\r\n\tinline void\t\t\t\tCamera::setLookAt( const Vector3f& eye, const Vector3f& at, const Vector3f& up ) {\r\n\t\tconst Vector3f zAxis( (eye - at).normalized() );\r\n\t\tconst Vector3f xAxis( (up.normalized().cross(zAxis)).normalized() );\r\n\t\tconst Vector3f yAxis( zAxis.cross(xAxis).normalized() );\r\n\r\n\t\tEigen::Matrix3f rotation;\r\n\t\trotation << xAxis, yAxis, zAxis;\r\n\t\tQuaternionf q(rotation);\r\n\r\n\t\t_transform.set(eye,q);\r\n\t\tforceUpdateViewProj();\r\n\t}\r\n\r\n\tinline void\t\t\t\tCamera::translate( const Vector3f& v ) {\r\n\t\t_dirtyViewProj = true; _transform.translate(v);\r\n\t}\r\n\tinline void\t\t\t\tCamera::translate( const Vector3f& v, const Transform3f& ref ) {\r\n\t\t_dirtyViewProj = true; _transform.translate(v, ref);\r\n\t}\r\n\tinline void\t\t\t\tCamera::position( const Vector3f& v ) {\r\n\t\t_dirtyViewProj = true; _transform.position(v);\r\n\t}\r\n\tinline const Vector3f&\t\tCamera::position( void ) const {\r\n\t\treturn _transform.position();\r\n\t}\r\n\r\n\tinline void\t\t\t\t\tCamera::rotate( const Quaternionf& rotation ) {\r\n\t\t_dirtyViewProj = true; _transform.rotate(rotation);\r\n\t}\r\n\tinline void\t\t\t\t\tCamera::rotate( const Vector3f& v ) {\r\n\t\t_dirtyViewProj = true; _transform.rotate(v);\r\n\t}\r\n\tinline void\t\t\t\t\tCamera::rotate( const Vector3f& v, const Transform3f& ref ) {\r\n\t\t_dirtyViewProj = true; _transform.rotate(v, ref);\r\n\t}\r\n\r\n\tinline void\t\t\t\t\tCamera::rotation( const Vector3f& v ) {\r\n\t\t_dirtyViewProj = true; _transform.rotation(v);\r\n\t}\r\n\tinline void\t\t\t\t\tCamera::rotation( const Quaternionf& q ) {\r\n\t\t_dirtyViewProj = true; _transform.rotation(q);\r\n\t}\r\n\r\n\tinline const Quaternionf&\t\tCamera::rotation( void ) const {\r\n\t\treturn _transform.rotation();\r\n\t}\r\n\r\n\t/////////////////////////////////////////////////////////////////\r\n\r\n\tinline void\tCamera::fovy( float value ) {\r\n\t\t_fov = value; _dirtyViewProj = true;\r\n\t}\r\n\tinline float\tCamera::fovy( void ) const {\r\n\t\treturn _fov;\r\n\t}\r\n\r\n\tinline void\tCamera::aspect( float value ) {\r\n\t\t_aspect = value; _dirtyViewProj = true;\r\n\t}\r\n\tinline float\tCamera::aspect( void ) const {\r\n\t\treturn _aspect;\r\n\t}\r\n\r\n\tinline void\tCamera::znear( float value ) {\r\n\t\t_znear = value; _dirtyViewProj = true;\r\n\t}\r\n\tinline float\tCamera::znear( void ) const {\r\n\t\treturn _znear;\r\n\t}\r\n\r\n\tinline void\tCamera::zfar( float value ) {\r\n\t\t_zfar = value; _dirtyViewProj = true;\r\n\t}\r\n\tinline float\tCamera::zfar( void ) const {\r\n\t\treturn _zfar;\r\n\t}\r\n\r\n\tinline void Camera::principalPoint(const sibr::Vector2f & p) {\r\n\t\t_p = p; _dirtyViewProj = true;\r\n\t}\r\n\r\n\tinline void\tCamera::orthoRight( float value ) {\r\n\t\t_right = value; _dirtyViewProj = true;\r\n\t}\r\n\tinline float\tCamera::orthoRight( void ) const {\r\n\t\treturn _right;\r\n\t}\r\n\r\n\tinline void\tCamera::orthoTop( float value ) {\r\n\t\t_top = value; _dirtyViewProj = true;\r\n\t}\r\n\tinline float\tCamera::orthoTop( void ) const {\r\n\t\treturn _top;\r\n\t}\r\n\tinline bool\tCamera::ortho(void) const {\r\n\t\treturn _isOrtho;\r\n\t}\r\n\r\n\r\n\tinline const Matrix4f&\t\t\tCamera::viewproj( void ) const {\r\n\t\tif (_dirtyViewProj)\r\n\t\t\tforceUpdateViewProj();\r\n\r\n\t\treturn _matViewProj;\r\n\t}\r\n\r\n\tinline const Matrix4f&\t\t\tCamera::invViewproj( void ) const {\r\n\t\tif (_dirtyViewProj)\r\n\t\t\tforceUpdateViewProj();\r\n\r\n\t\treturn _invMatViewProj;\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/system/Config.hpp\"\n# include \"core/system/Utils.hpp\"\n\n//#define GLEW_STATIC\n#include <GL/glew.h>\n\n# include <functional>\n\n# define GLFW_INCLUDE_GLU\n# include <GLFW/glfw3.h>\n\n\n// (used by Image)\n# pragma warning(push, 0)\n#  include <opencv2/opencv.hpp>\n#  include <opencv2/core.hpp>\n#  include <opencv2/highgui.hpp>\n# pragma warning(pop)\n\n\n\n# ifdef SIBR_OS_WINDOWS\n//// Export Macro (used for creating DLLs) ////\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_GRAPHICS_EXPORT\n#      ifdef SIBR_GRAPHICS_EXPORTS\n          /* We are building this library */\n#        define SIBR_GRAPHICS_EXPORT __declspec(dllexport)\n#      else\n          /* We are using this library */\n#        define SIBR_GRAPHICS_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT \n#    endif\n#  endif\n# else\n# define SIBR_GRAPHICS_EXPORT\n# endif\n\n\n/** Macro to check OpenGL error and throw \\p std::runtime_error if found */\n# undef CHECK_GL_ERROR\n# define CHECK_GL_ERROR  {\t\t\t\t\t\t\t\t\\\n  GLenum err = glGetError();\t\t\t\t\t\t\t\\\n  if (err) {\t\t\t\t\t\t\t\t\t\t\t\\\n\tstd::string errorStr = \"Unknown\";\t\t\t\t\t\\\n\tswitch (err) {\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_INVALID_ENUM:\t\t\t\t\t\t\t\t\\\n\t\terrorStr = \"GL_INVALID_ENUM\";\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_INVALID_VALUE:\t\t\t\t\t\t\t\t\\\n\t\terrorStr = \"GL_INVALID_VALUE\";\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_INVALID_OPERATION:\t\t\t\t\t\t\t\\\n\t\terrorStr = \"GL_INVALID_OPERATION\";\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_STACK_OVERFLOW:\t\t\t\t\t\t\t\t\\\n\t\terrorStr = \"GL_STACK_OVERFLOW\";\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_STACK_UNDERFLOW:\t\t\t\t\t\t\t\\\n\t\terrorStr = \"GL_STACK_UNDERFLOW\";\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_OUT_OF_MEMORY:\t\t\t\t\t\t\t\t\\\n\t\terrorStr = \"GL_OUT_OF_MEMORY\";\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tcase GL_INVALID_FRAMEBUFFER_OPERATION:\t\t\t\t\\\n\t\terrorStr = \"GL_INVALID_FRAMEBUFFER_OPERATION\";\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\tdefault:\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n  SIBR_ERR << \"OpenGL error 0x0\" << std::hex << err  << std::dec  << \" (\" << int(err) << \") \" << errorStr << \" at \" << __FILE__ << \":\" << __LINE__ << std::endl; \\\n  }\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n}\n\n#define SIBR_GLSL(version, shader)  \"#version \" #version \"\\n\" #shader\n\n\nnamespace sibr\n{\n\t/** Clamp a value.\n\t\\param value value to clamp\n\t\\param min min value\n\t\\param max max value\n\t\\return min(max(value, min), max)\n\t\\ingroup sibr_graphics\n\t*/\n\ttemplate <typename T>\n\tinline T\tclamp( T value, T min, T max ) {\n\t\treturn std::max(min, std::min(max, value));\n\t}\n\n} // namespace sibr\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Frustum.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n// Partially based on:\n// http://www.lighthouse3d.com/tutorials/view-frustum-culling\n\n#include \"core/graphics/Camera.hpp\"\n#include \"core/graphics/Frustum.hpp\"\n\nnamespace sibr\n{\n\tFrustum::Frustum(const Camera& cam)\n\t{\n\t\tfloat ratio = cam.aspect();\n\t\tfloat angle = cam.fovy();\n\t\tfloat nearD = cam.znear();\n\t\tfloat farD = cam.zfar();\n\n\t\t// compute width and height of the near and far plane sections\n\t\tfloat tang = (float)tan(SIBR_DEGTORAD(angle) * 0.5);\n\t\tfloat nh = nearD * tang;\n\t\tfloat nw = nh * ratio;\n\t\tfloat fh = farD  * tang;\n\t\tfloat fw = fh * ratio;\n\n\t\tVector3f nc, fc, X, Y, Z;\n\t\tconst Vector3f& p = cam.position();\n\n\t\t// compute the Z axis of camera\n\t\t// this axis points in the opposite direction from\n\t\t// the looking direction\n\t\tZ = -cam.dir();\n\n\t\t// X axis of camera with given \"up\" vector and Z axis\n\t\tX = cam.up().cross(Z);\n\t\tX.normalize();\n\n\t\t// the real \"up\" vector is the cross product of Z and X\n\t\tY = cam.up();\n\n\t\t// compute the centers of the near and far planes\n\t\tnc = p - Z * nearD;\n\t\tfc = p - Z * farD;\n\n\t\t_planes[NEARP].buildFrom( -Z, nc );\n\t\t_planes[FARP].buildFrom( Z, fc );\n\n\t\tVector3f aux, normal;\n\n\t\taux = (nc + Y*nh) - p;\n\t\taux.normalize();\n\t\tnormal = aux.cross(X);\n\t\t_planes[TOP].buildFrom( normal, nc + Y*nh );\n\n\t\taux = (nc - Y*nh) - p;\n\t\taux.normalize();\n\t\tnormal = X.cross(aux);\n\t\t_planes[BOTTOM].buildFrom( normal, nc - Y*nh );\n\n\t\taux = (nc - X*nw) - p;\n\t\taux.normalize();\n\t\tnormal = aux.cross(Y);\n\t\t_planes[LEFT].buildFrom( normal, nc - X*nw );\n\n\t\taux = (nc + X*nw) - p;\n\t\taux.normalize();\n\t\tnormal = Y.cross(aux);\n\t\t_planes[RIGHT].buildFrom( normal, nc + X*nw );\n\t}\n\n\tFrustum::TestResult\tFrustum::testSphere(const Vector3f& p, float radius)\n\t{\n\t\tfloat distance;\n\t\tTestResult result = INSIDE;\n\n\t\tfor (int i = 0; i < 6; i++) {\n\t\t\tdistance = _planes[i].distanceWithPoint(p);\n\t\t\tif (distance < -radius)\n\t\t\t\treturn OUTSIDE;\n\t\t\telse if (distance < radius)\n\t\t\t\tresult = INTERSECT;\n\t\t}\n\t\treturn result;\n\t}\n\n\tfloat\tFrustum::Plane::distanceWithPoint(const Vector3f& p)\n\t{\n\t\t// dist = A*rx + B*ry + C*rz + D = n . r  + D\n\t\treturn A*p.x() + B*p.y() + C*p.z() + D;\n\t}\n\tvoid\tFrustum::Plane::buildFrom(const Vector3f& normal, const Vector3f& point)\n\t{\n\t\tVector3f n = normal;\n\t\tn.normalize();\n\t\tA = n.x();\n\t\tB = n.y();\n\t\tC = n.z();\n\t\tD = -n.dot(point);\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Frustum.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <array>\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Vector.hpp\"\n\nnamespace sibr\n{\n\tclass Camera;\n\n\t/** Represent a 3D frustum defined by 6 planes.\n\t* \\warning This class has not been strongly tested!\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT Frustum\n\t{\n\tpublic:\n\n\t\t/// Result of intersection test.\n\t\tenum TestResult\n\t\t{\n\t\t\tOUTSIDE = 0,\n\t\t\tINTERSECT,\n\t\t\tINSIDE\n\t\t};\n\n\t\t/// Frustum plane representation.\n\t\tstruct Plane\n\t\t{\n\t\t\tfloat A;\n\t\t\tfloat B;\n\t\t\tfloat C;\n\t\t\tfloat D;\n\n\t\t\t/** Get the distance from a point to the plane.\n\t\t\t\\param p 3D point\n\t\t\t\\return distance\n\t\t\t*/\n\t\t\tfloat\tdistanceWithPoint(const Vector3f& p);\n\n\t\t\t/** Build a plane from a normal and a point.\n\t\t\t\\param normal the normal\n\t\t\t\\param point a point belonging to the plane\n\t\t\t*/\n\t\t\tvoid\tbuildFrom(const Vector3f& normal, const Vector3f& point);\n\t\t};\n\n\tpublic:\n\n\t\t/** Construct the furstum associated to a camera.\n\t\t\\param cam the camera\n\t\t*/\n\t\tFrustum(const Camera& cam);\n\n\t\t/** Test if a sphere intersects the frustum or is contained in it.\n\t\t\\param sphere sphere center\n\t\t\\param radius sphere radis\n\t\t\\return if the sphere is inside, intersecting or outside the frustum\n\t\t*/\n\t\tTestResult\ttestSphere(const Vector3f& sphere, float radius);\n\n\tprivate:\n\n\t\t/// Location of each plane.\n\t\tenum \n\t\t{\n\t\t\tTOP = 0, \n\t\t\tBOTTOM, \n\t\t\tLEFT,\n\t\t\tRIGHT, \n\t\t\tNEARP, \n\t\t\tFARP,\n\n\t\t\tCOUNT\n\t\t};\n\n\n\t\tstd::array<Plane, COUNT> _planes; ///< Frustum planes.\n\n\t};\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/GPUQuery.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"GPUQuery.hpp\"\n\nusing namespace sibr;\n\nGPUQuery::GPUQuery(GLenum type, size_t count) : _count(count), _type(type){\n\tif(count < 2) {\n\t\tSIBR_WRG << \"Using a buffer of size >= 2 is recommended to avoid synchronization problems.\" << std::endl;\n\t}\n\t_ids.resize(count);\n\tglGenQueries(GLsizei(_ids.size()), &_ids[0]);\n\t_current = _count - 1;\n\t// Dummy initial query.\n\tfor (int i = 0; i < _count; ++i)\n\t{\n\t\tbegin();\n\t\tend();\n\t}\n}\n\nvoid GPUQuery::begin() {\n\tif(_observing) {\n\t\tSIBR_WRG << \"Query already started...\" << std::endl;\n\t\treturn;\n\t}\n\t_current = (_current + 1) % _count;\n\tglBeginQuery(_type, _ids[_current]);\n\t_observing = true;\n}\n\nvoid GPUQuery::end() {\n\tif (!_observing) {\n\t\tSIBR_WRG << \"Query not running...\" << std::endl;\n\t\treturn;\n\t}\n\tglEndQuery(_type);\n\t_observing = false;\n}\n\nuint64 GPUQuery::value() {\n\tif (_observing) {\n\t\tSIBR_WRG << \"Query still running, ending it first...\" << std::endl;\n\t\tend();\n\t}\n\t// We want the ID of the previous frame, taking into account that we have incremented the counter once more when ending it. So minus 1.\n\t// Except if you have only one query, in which case we query this one, but it will stall the GPU.\n\tconst size_t previous = (_current - 1) % _count;\n\tGLuint64 data = 0;\n\tglGetQueryObjectui64v(_ids[previous], GL_QUERY_RESULT, &data);\n\t//CHECK_GL_ERROR;\n\treturn data;\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/GPUQuery.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n#include <core/system/Config.hpp>\n#include <core/graphics/Config.hpp>\n\nnamespace sibr { \n\n\t/**\n\t * Provide a buffered wrapper around an OpenGL query object, avoiding manual synchronization.\n\t * See section 4.2 of the OpenGL 4.6 specification for more details on the types of queries available\n\t * (time elapsed, number of primitives, number of fragment writes...).\n\t * \n\t * For example, to get the processing time of a mesh draw call, you can use the following.\n\t * In renderer initialisation:\n\t *\t\tGPUQuery query(GL_TIME_ELAPSED);\n\t * In the rendering loop:\n\t *\t\tquery.begin();\n\t *\t\tmesh.draw();\n\t *\t\tquery.end();\n\t * In your GUI loop:\n\t *\t\tconst uint64 time = query.value();\n\t *\t\t//... display it.\n\t *\t\n\t * \\warning Because the query is using buffering to avoid stalling when querying the value, \n\t * you SHOULD NOT use the same query object for multiple timings in the same frame. \n\t * It should also be use for multiple consecutive frames ; because of buffering again, \n\t * the first time value() is queried, it might be erroneous.\n\t *\n\t * \\note If you want to create a query inline (for a one shot measurement), set the buffer \n\t * count to 1, and know that it will introduce a stall when querying the value.\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT GPUQuery\n\t{\n\t\tSIBR_CLASS_PTR(GPUQuery);\n\n\tpublic:\n\n\t\t/** Create a query of a given type.\n\t\t\\param type the OpenGL enum type\n\t\t\\param count number of buffered internal queries (ideally >= 2).\n\t\t*/\n\t\tGPUQuery(GLenum type, size_t count = 2);\n\n\t\t/** Start measuring. */\n\t\tvoid begin();\n\n\t\t/** Stop measuring. */\n\t\tvoid end();\n\n\t\t/** Obtain the raw value (time in nanoseconds, number of primitives,...) for the query before last.\n\t\tThis allows for buffering from one frame to the next and avoid stalls (except if count is set to 1).\n\t\t\\return the query value.\n\t\t*/\n\t\tuint64 value();\n\n\tprivate:\n\t\t\n\t\tstd::vector<GLuint> _ids; ///< Internal queries IDs.\n\t\tconst size_t _count; ///< Number of queries.\n\t\tGLenum _type; ///< Type of query.\n\t\tsize_t _current = 0; ///< Current internla query used.\n\t\tbool _observing = false; ///< Are we currently measuring.\n\t};\n\n} \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/GUI.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/graphics/Window.hpp\"\r\n#include \"core/graphics/GUI.hpp\"\r\n#include \"core/graphics/Mesh.hpp\"\r\n\r\n// We extend ImGui functionality so we need the internal definitions.\r\n#define IMGUI_DEFINE_MATH_OPERATORS\r\n#include <imgui/imgui_internal.h>\r\n\r\nnamespace sibr\r\n{\r\n\t\r\n\tbool\t\tshowImGuiWindow(const std::string& windowTitle, const IRenderTarget& rt, ImGuiWindowFlags flags, Viewport & viewport,  bool invalidTexture,  bool updateLayout, int handle )\r\n\t{\r\n\t\tbool isWindowFocused = false;\r\n\t\t// If we are asked to, we need to update the viewport at launch.\r\n\t\tif (updateLayout) {\r\n\t\t\tImGui::SetNextWindowPos(ImVec2(viewport.finalLeft(), viewport.finalTop()));\r\n\t\t\tImGui::SetNextWindowSize(ImVec2(0, 0));\r\n\t\t\tImGui::SetNextWindowContentSize(ImVec2(viewport.finalWidth(), viewport.finalHeight()));\r\n\t\t}\r\n\r\n\t\tif (::ImGui::Begin(windowTitle.c_str(), NULL, flags))\r\n\t\t{\r\n\t\t\t// Get the current cursor position (where your window is)\r\n\t\t\tImVec2 pos = /*ImGui::GetItemRectMin() + */::ImGui::GetCursorScreenPos();\r\n\t\t\tVector2f offset, size;\r\n\t\t\tVector2i availRegionSize(::ImGui::GetContentRegionAvail().x, ::ImGui::GetContentRegionAvail().y);\r\n\t\t\t\r\n\t\t\tfitImageToDisplayRegion(viewport.finalSize(), availRegionSize, offset, size);\r\n\t\t\t\r\n\t\t\tsize = size.cwiseMax( sibr::Vector2f( 1.0f,1.0f) );\r\n\r\n\t\t\t\t\r\n\t\t\tpos.x += offset.x();\r\n\t\t\tpos.y += offset.y();\r\n\r\n\t\t\t\r\n\t\t\tImGui::SetCursorPos(ImVec2(offset.x(), ImGui::GetTitleBarHeight()+offset.y()));\r\n\t\t\tImGui::InvisibleButton((windowTitle + \"--TEXTURE-INVISIBLE_BUTTON\").c_str(), ImVec2(size.x(), size.y()));\r\n\t\t\tif (!invalidTexture) {\r\n\t\t\t\t::ImGui::GetWindowDrawList()->AddImage((void*)(intptr_t)(rt.handle(handle)),\r\n\t\t\t\t\tpos, ImVec2(pos.x + size.x(), pos.y + size.y()),\r\n\t\t\t\t\tImVec2(0, 1), ImVec2(1, 0));\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tisWindowFocused = ImGui::IsWindowFocused();\r\n\r\n\t\t\tviewport = Viewport(pos.x, pos.y, pos.x+size.x(), pos.y+size.y());\r\n\r\n\t\t\t// Hand back the inputs to sibr.\r\n\t\t\tif (ImGui::IsItemHovered()) {\r\n\t\t\t\tImGui::CaptureKeyboardFromApp(false);\r\n\t\t\t\tImGui::CaptureMouseFromApp(false);\r\n\t\t\t}\r\n\t\t}\r\n\t\t::ImGui::End();\r\n\r\n\t\treturn isWindowFocused;\r\n\t}\r\n\r\n\tMesh::Ptr generateMeshForText(const std::string & text, unsigned int & separationIndex){\r\n\t\t// Technically we don't care if we already are in the middle of a ImGui frame.\r\n\t\t// as long as we clear the draw list. ImGui will detect the empty draw lists and cull them.\r\n\t\tImGui::PushID(1234567809);\r\n\t\tImGui::SetNextWindowPos(ImVec2(0,0));\r\n\t\tImGui::Begin(text.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs);\r\n\t\tImGui::SetWindowFontScale(ImGui::GetIO().FontGlobalScale);\r\n\r\n\t\tImGui::Text(text.c_str());\r\n\t\t// Get back the draw list.\r\n\t\tImDrawList * drawlist = ImGui::GetWindowDrawList();\r\n\t\tconst int vertCount = drawlist->VtxBuffer.Size;\r\n\t\tconst int indexCount = drawlist->IdxBuffer.Size;\r\n\t\t// We generate one mesh from the draw list.\r\n\t\tstd::vector<sibr::Vector3f> vertices(vertCount);\r\n\t\tstd::vector<sibr::Vector2f> uvs(vertCount);\r\n\t\tstd::vector<sibr::Vector3f> colors(vertCount);\r\n\t\tstd::vector<sibr::Vector3u> faces(indexCount / 3);\r\n\r\n\t\tsibr::Vector3f centroid(0.0f, 0.0f, 0.0f);\r\n\t\tfor (int k = 0; k < vertCount; ++k) {\r\n\t\t\tconst auto & vtx = drawlist->VtxBuffer[k];\r\n\t\t\tvertices[k][0] = (vtx.pos.x)*2.0f;\r\n\t\t\tvertices[k][1] = -vtx.pos.y*2.0f;\r\n\t\t\tuvs[k][0] = vtx.uv.x; uvs[k][1] = vtx.uv.y;\r\n\t\t\tImVec4 col = ImGui::ColorConvertU32ToFloat4(vtx.col);\r\n\t\t\tcolors[k][0] = col.x; colors[k][1] = col.y;\r\n\t\t\tcolors[k][2] = col.z; vertices[k][2] = col.w;\r\n\t\t\tcentroid += vertices[k];\r\n\t\t}\r\n\t\tfor (int k = 0; k < indexCount; k += 3) {\r\n\t\t\tfaces[k / 3][0] = (unsigned int)drawlist->IdxBuffer[k];\r\n\t\t\tfaces[k / 3][1] = (unsigned int)drawlist->IdxBuffer[k + 1];\r\n\t\t\tfaces[k / 3][2] = (unsigned int)drawlist->IdxBuffer[k + 2];\r\n\t\t}\r\n\t\t// Center the mesh?\r\n\t\tcentroid /= float(vertices.size());\r\n\t\tfor (int k = 0; k < vertices.size(); ++k) {\r\n\t\t\tvertices[k] -= centroid;\r\n\t\t}\r\n\t\tMesh::Ptr mesh = std::make_shared<Mesh>();\r\n\t\tmesh->vertices(vertices);\r\n\t\tmesh->colors(colors);\r\n\t\tmesh->texCoords(uvs);\r\n\t\tmesh->triangles(faces);\r\n\t\t// Store the separation idnex between the background and the text foreground.\r\n\t\tseparationIndex = drawlist->CmdBuffer[0].ElemCount;\r\n\t\t\r\n\t\t// Finish the window, then clear the draw list.\r\n\t\tImGui::End();\r\n\t\tImGui::PopID();\r\n\t\tdrawlist->Clear();\r\n\t\treturn mesh;\r\n\t}\r\n\r\n\r\n\tvoid \t\t\tfitImageToDisplayRegion(const Vector2f & imgSize, const Vector2i & regionSize, Vector2f& offset, Vector2f& size)\r\n\t{\r\n\t\t\r\n\t\tVector2f ratios = imgSize.cwiseQuotient(regionSize.cast<float>());\r\n\t\tif (ratios.x() < ratios.y())\r\n\t\t{\r\n\t\t\tfloat aspect = imgSize.x() / imgSize.y();\r\n\t\t\tsize.y() = float(regionSize.y());\r\n\t\t\tsize.x() = size.y() * aspect;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfloat aspect = imgSize.y() / imgSize.x();\r\n\t\t\tsize.x() = float(regionSize.x());\r\n\t\t\tsize.y() = size.x() * aspect;\r\n\t\t}\r\n\t\toffset = regionSize.cast<float>() / 2 - size / 2;\r\n\t}\r\n\t\r\n\r\n\r\n\tsibr::Vector2f ZoomData::topLeft()\t\tconst { return center - diagonal; }\r\n\tsibr::Vector2f ZoomData::bottomRight()\tconst { return center + diagonal; }\r\n\r\n\tsibr::Vector2f ZoomData::uvFromBoxPos(const sibr::Vector2f& pos) const\r\n\t{\r\n\t\treturn topLeft() + 2.0f*diagonal.cwiseProduct(pos);\r\n\t}\r\n\r\n\tZoomData ZoomData::scaled(const sibr::Vector2f& size) const \r\n\t{\r\n\t\tZoomData out;\r\n\t\tout.center = center.cwiseProduct(size);\r\n\t\tout.diagonal = diagonal.cwiseProduct(size);\r\n\t\treturn out;\r\n\t}\r\n\r\n\tvoid ZoomInterraction::updateZoom(const sibr::Vector2f& canvasSize)\r\n\t{\r\n\t\tconst auto & d = callBackData;\r\n\t\tif (d.ctrlPressed) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tsibr::Vector2f posF = zoomData.uvFromBoxPos(d.positionRatio);\r\n\r\n\t\tif (d.isHoovered && d.isClickedRight && !zoomData.underMofidication) {\r\n\t\t\tzoomData.underMofidication = true;\r\n\t\t\tzoomData.tmpTopLeft = posF;\r\n\t\t\tzoomData.firstClickPixel = d.mousePos;\r\n\t\t}\r\n\t\tif (d.isHoovered && zoomData.underMofidication) {\r\n\t\t\tzoomData.tmpBottonRight = posF;\r\n\t\t\tzoomData.secondClickPixel = d.mousePos;\r\n\t\t}\r\n\r\n\t\tif (zoomData.underMofidication) {\r\n\t\t\tImGui::GetWindowDrawList()->AddRect(\r\n\t\t\t\tImVec2(zoomData.firstClickPixel[0], zoomData.firstClickPixel[1]),\r\n\t\t\t\tImVec2(zoomData.secondClickPixel[0], zoomData.secondClickPixel[1]),\r\n\t\t\t\tIM_COL32(255, 0, 0, 255), 0, 0, 2\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tif (d.isReleasedRight && zoomData.underMofidication) {\r\n\t\t\tzoomData.underMofidication = false;\r\n\t\t\tif ((zoomData.tmpBottonRight - zoomData.tmpTopLeft).cwiseProduct(canvasSize).cwiseAbs().minCoeff() > 10) {\r\n\t\t\t\tzoomData.center = 0.5f*(zoomData.tmpBottonRight + zoomData.tmpTopLeft);\r\n\t\t\t\tzoomData.diagonal = 0.5f*(zoomData.tmpBottonRight - zoomData.tmpTopLeft).cwiseAbs();\r\n\t\t\t\tauto scaledBox = zoomData.scaled(canvasSize);\r\n\t\t\t\tfloat target_ratio = canvasSize[0] / canvasSize[1];\r\n\t\t\t\tfloat current_ratio = scaledBox.diagonal[0] / scaledBox.diagonal[1];\r\n\t\t\t\tif (current_ratio > target_ratio) {\r\n\t\t\t\t\tscaledBox.diagonal.y() = scaledBox.diagonal.x() / target_ratio;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tscaledBox.diagonal.x() = scaledBox.diagonal.y() * target_ratio;\r\n\t\t\t\t}\r\n\t\t\t\tzoomData.diagonal = scaledBox.diagonal.cwiseQuotient(canvasSize);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (d.isHoovered && d.scroll != 0) {\r\n\t\t\tzoomData.diagonal = zoomData.diagonal.cwiseProduct(pow(1.15f, -d.scroll)*sibr::Vector2f(1, 1));\r\n\t\t}\r\n\r\n\t\t\r\n\r\n\t\tzoomData.diagonal = zoomData.diagonal.cwiseMin(sibr::Vector2f(0.5, 0.5));\r\n\t\tusing Box = Eigen::AlignedBox2f;\r\n\t\tusing Corner = Box::CornerType;\r\n\r\n\t\tBox target(sibr::Vector2f(0, 0), sibr::Vector2f(1, 1));\r\n\t\tBox current(zoomData.topLeft(), zoomData.bottomRight());\r\n\t\t\r\n\t\tif (!target.contains(current)) {\r\n\t\t\tBox inside = current;\r\n\t\t\tinside.clamp(target);\r\n\t\t\tfor (int c = 0; c < 4; ++c) {\r\n\t\t\t\tCorner cType = (Corner)c;\r\n\t\t\t\tif ( (current.corner(cType)-inside.corner(cType)).isZero() ) {\t\t\t\r\n\t\t\t\t\tCorner opposite = (Corner)(3 - c);\r\n\t\t\t\t\tzoomData.center += (inside.corner(opposite) - current.corner(opposite));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\r\n\tvoid SegmentSelection::update(const CallBackData & callback, const sibr::Vector2i & size, const ZoomData & zoom)\r\n\t{\r\n\t\tsibr::Vector2i pos = zoom.scaled(size.cast<float>()).uvFromBoxPos(callback.positionRatio).cast<int>();\r\n\r\n\t\tif (callback.isHoovered && callback.isClickedRight && callback.ctrlPressed && (!first || valid)) {\r\n\t\t\tfirstPosScreen = callback.mousePos.cast<int>();\r\n\t\t\tfirstPosIm = pos.cast<int>();\r\n\t\t\tsecondPosScreen = firstPosScreen;\r\n\t\t\tfirst = true;\r\n\t\t} else if (callback.isHoovered && first) {\r\n\t\t\tsecondPosScreen = callback.mousePos.cast<int>();\r\n\t\t\tsecondPosIm = pos.cast<int>();\r\n\r\n\t\t\tif (callback.isClickedRight) {\r\n\t\t\t\tfirst = false;\r\n\t\t\t\tvalid = true;\r\n\t\t\t\tcomputeRasterizedLine();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid SegmentSelection::computeRasterizedLine()\r\n\t{\r\n\t\tif (!valid) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tsibr::Vector2i diff = secondPosIm - firstPosIm;\r\n\t\tint l = diff.cwiseAbs().maxCoeff();\r\n\t\trasterizedLine.resize(l + 1);\r\n\t\tfor (int i = 0; i <= l; ++i) {\r\n\t\t\trasterizedLine[i] = (firstPosIm.cast<float>() + (i / (float)l)*diff.cast<float>()).cast<int>();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid DisplayImageGui(\r\n\t\tGLuint texture,\r\n\t\tconst sibr::Vector2i & displaySize,\r\n\t\tconst sibr::Vector2f& uv0,\r\n\t\tconst sibr::Vector2f& uv1 \r\n\t) {\r\n\t\tImGui::Image((void*)(intptr_t)(texture), ImVec2(float(displaySize[0]), float(displaySize[1])), ImVec2(uv0[0], uv0[1]), ImVec2(uv1[0], uv1[1]));\r\n\t}\r\n\r\n\tvoid ImageWithCallback(\r\n\t\tGLuint texture,\r\n\t\tconst sibr::Vector2i & displaySize,\r\n\t\tCallBackData & callbackDataOut,\r\n\t\tconst sibr::Vector2f & uv0,\r\n\t\tconst sibr::Vector2f & uv1\r\n\t) {\r\n\t\tCallBackData & data = callbackDataOut;\r\n\r\n\t\tdata.itemPos = toSIBR<float>(ImGui::GetCursorScreenPos());\r\n\t\tDisplayImageGui(texture, displaySize, uv0, uv1);\r\n\r\n\t\tdata.itemSize = toSIBR<float>(ImGui::GetItemRectSize());\r\n\t\tdata.isHoovered = ImGui::IsItemHovered();\r\n\t\tdata.isClickedLeft = ImGui::IsMouseClicked(0);\r\n\t\tdata.isReleasedLeft = ImGui::IsMouseReleased(0);\r\n\t\tdata.isClickedRight = ImGui::IsItemClicked(1);\r\n\t\tdata.isReleasedRight = ImGui::IsMouseReleased(1);\r\n\t\tdata.ctrlPressed = ImGui::GetIO().KeyCtrl;\r\n\t\tdata.scroll = ImGui::GetIO().MouseWheel;\r\n\r\n\t\tif (data.isHoovered) {\r\n\t\t\tdata.mousePos = toSIBR<float>(ImGui::GetIO().MousePos);\r\n\t\t\tdata.positionRatio = (data.mousePos - data.itemPos).cwiseQuotient(data.itemSize);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid ImageWithZoom(GLuint texture, const sibr::Vector2i & displaySize, ZoomInterraction & zoom)\r\n\t{\r\n\t\tImageWithCallback(texture, displaySize, zoom.callBackData, zoom.zoomData.topLeft(), zoom.zoomData.bottomRight());\r\n\t\tzoom.updateZoom(displaySize.template cast<float>());\r\n\t}\r\n\r\n} // namespace sibr\r\n\r\n\r\nnamespace ImGui {\r\n\r\n\tconst float GetTitleBarHeight() { return GetTextLineHeight() + GetStyle().FramePadding.y * 2.0f; }\r\n\r\n\tvoid PushScaledItemWidth(float item_width)\r\n\t{\r\n\t\tImGui::PushItemWidth(ImGui::GetIO().FontGlobalScale * item_width);\r\n\t}\r\n\r\n\tbool TabButton(const char * label, bool highlight, const ImVec2 & size)\r\n\t{\r\n\t\tif (highlight) {\r\n\t\t\tImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(0, 0.8f, 0.8f));\r\n\t\t\tImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(0, 0.6f, 0.6f));\r\n\t\t}\r\n\t\tbool b = ImGui::Button(label, size);\r\n\t\tif (highlight) {\r\n\t\t\tImGui::PopStyleColor(2);\r\n\t\t}\r\n\t\treturn b;\r\n\t}\r\n\r\n\tvoid PlotMultiLines(const char* label, std::vector<float*> values, int values_count, const std::vector<ImVec4>& colors, float scale_min, float scale_max, ImVec2 graph_size) {\r\n\t\t// Note: code extracted from ImGui and udpated to display multiple lines on the same graph.\r\n\t\tImGuiWindow* window = GetCurrentWindow();\r\n\t\tif (window->SkipItems)\r\n\t\t\treturn;\r\n\r\n\t\tImGuiContext& g = *GImGui;\r\n\t\tconst ImGuiStyle& style = g.Style;\r\n\t\t// Force the plot type.\r\n\t\tImGuiPlotType plot_type = ImGuiPlotType_Lines;\r\n\t\tconst ImVec2 label_size = CalcTextSize(label, NULL, true);\r\n\t\tif (graph_size.x == 0.0f)\r\n\t\t\tgraph_size.x = CalcItemWidth();\r\n\t\tif (graph_size.y == 0.0f)\r\n\t\t\tgraph_size.y = label_size.y + (style.FramePadding.y * 2);\r\n\r\n\t\tconst ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));\r\n\t\tconst ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);\r\n\t\tconst ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));\r\n\t\tItemSize(total_bb, style.FramePadding.y);\r\n\t\tif (!ItemAdd(total_bb, 0, &frame_bb))\r\n\t\t\treturn;\r\n\t\tconst bool hovered = ItemHoverable(inner_bb, 0);\r\n\r\n\t\t// Determine scale from values if not specified\r\n\t\tif (scale_min == FLT_MAX || scale_max == FLT_MAX)\r\n\t\t{\r\n\t\t\tfloat v_min = FLT_MAX;\r\n\t\t\tfloat v_max = -FLT_MAX;\r\n\t\t\tfor (int j = 0; j < values.size(); ++j) {\r\n\t\t\t\tfor (int i = 0; i < values_count; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst float v = values[j][i];\r\n\t\t\t\t\tv_min = ImMin(v_min, v);\r\n\t\t\t\t\tv_max = ImMax(v_max, v);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (scale_min == FLT_MAX)\r\n\t\t\t\tscale_min = v_min;\r\n\t\t\tif (scale_max == FLT_MAX)\r\n\t\t\t\tscale_max = v_max;\r\n\t\t}\r\n\r\n\t\tRenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\r\n\t\tint values_offset = 0;\r\n\r\n\t\tif (values_count > 0)\r\n\t\t{\r\n\t\t\tint res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);\r\n\t\t\tint item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);\r\n\r\n\t\t\t// No tooltip for now.\r\n\r\n\t\t\tconst float t_step = 1.0f / (float)res_w;\r\n\t\t\tconst float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));\r\n\r\n\t\t\tfor (int vid = 0; vid < values.size(); ++vid) {\r\n\t\t\t\tfloat v0 = values[vid][(0 + values_offset) % values_count];\r\n\t\t\t\tfloat t0 = 0.0f;\r\n\t\t\t\tImVec2 tp0 = ImVec2(t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale));                       // Point in the normalized space of our target rectangle\r\n\t\t\t\tfloat histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands\r\n\r\n\t\t\t\tconst ImU32 col_base = GetColorU32(colors[vid >= colors.size() ? 0 : vid]);\r\n\t\t\t\tconst ImU32 col_hovered = col_base;\r\n\r\n\t\t\t\tfor (int n = 0; n < res_w; n++)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst float t1 = t0 + t_step;\r\n\t\t\t\t\tconst int v1_idx = (int)(t0 * item_count + 0.5f);\r\n\t\t\t\t\tIM_ASSERT(v1_idx >= 0 && v1_idx < values_count);\r\n\t\t\t\t\tconst float v1 = values[vid][(v1_idx + values_offset + 1) % values_count];\r\n\t\t\t\t\tconst ImVec2 tp1 = ImVec2(t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale));\r\n\r\n\t\t\t\t\t// NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.\r\n\t\t\t\t\tImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);\r\n\t\t\t\t\tImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));\r\n\t\t\t\t\tif (plot_type == ImGuiPlotType_Lines)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twindow->DrawList->AddLine(pos0, pos1, col_base);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (plot_type == ImGuiPlotType_Histogram)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (pos1.x >= pos0.x + 2.0f)\r\n\t\t\t\t\t\t\tpos1.x -= 1.0f;\r\n\t\t\t\t\t\twindow->DrawList->AddRectFilled(pos0, pos1, col_base);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tt0 = t1;\r\n\t\t\t\t\ttp0 = tp1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/GUI.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n# include \"core/graphics/Image.hpp\"\r\n# include \"core/graphics/RenderTarget.hpp\"\r\n# include \"core/graphics/Window.hpp\"\r\n\r\n#include <imgui/imgui.h>\r\n\r\nnamespace sibr\r\n{\r\n\t\r\n\t/**\tShow the content of a rendertarget in an ImGui window.\r\n\t\\param windowTitle the window name (unique)\r\n\t\\param rt the rendrtarget to display\r\n\t\\param flags ImGui flags\r\n\t\\param viewport will contain the window extent on screen\r\n\t\\param invalidTexture ignore the RT\r\n\t\\param updateLayout force update the camera location on screen\r\n\t\\param handle the texture index to display from the input RT\r\n\t\\return true if window is focused (useful for managing interactions).\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT bool\t\tshowImGuiWindow(const std::string& windowTitle, const IRenderTarget& rt, ImGuiWindowFlags flags, Viewport & viewport,  bool invalidTexture,  bool updateLayout, int handle = 0);\r\n\r\n\t/**\r\n\tHelper that compute the location and extent to display an image in a given region without cropping or distorting it\r\n\t\\param imgSize the image size\r\n\t\\param regionSize the region size\r\n\t\\param offset will containg the top-left corner location\r\n\t\\param size will contain the size to use\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void \t\tfitImageToDisplayRegion(const Vector2f & imgSize, const Vector2i & regionSize, Vector2f& offset, Vector2f& size);\r\n\t\r\n\t/** Generate a mesh for a given label, using ImGui internally. This will generate a mesh that contain both the background and foreground geometry packed together sequentially.\r\n\t\\param text the text to generate the label of\r\n\t\\param separationIndex will contain the location of the first triangle of the foreground mesh\r\n\t\\return the mesh containing first the background triangles then the foreground triangles\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT Mesh::Ptr generateMeshForText(const std::string & text, unsigned int & separationIndex);\r\n\r\n} // namespace sibr\r\n\r\nnamespace ImGui {\r\n\r\n\t/** \\return the height of the title bar (for layout)\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT const float GetTitleBarHeight();\r\n\t\r\n\t/**\r\n\t* Push a width for item which is HiDPI aware.\r\n\t* \\param item_width The with to push, in regular pixels.\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void PushScaledItemWidth(float item_width);\r\n\r\n\t/** Helper to create a tab button item.\r\n\t\\param label the button text\r\n\t\\param highlight should the button be highlit\r\n\t\\param size the size of the button (0,0 will autosize).\r\n\t\\return true if the tab is active\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT bool TabButton(const char* label, bool highlight, const ImVec2& size = ImVec2(0, 0));\r\n\r\n\t/** Plot multiple curves on a graph. All curves should have the same number of samples.\r\n\t\tOnly lines are supported, code is based on internal ImGui implementation for one curve.\r\n\t\t\\param label the graph ImGui label\r\n\t\t\\param values a list of pointers to list of values\r\n\t\t\\param values_count number of samples in each list\r\n\t\t\\param colors one or mulitple colors to use for each curve\r\n\t\t\\param scale_min the value corresponding to the bottom of the graph\r\n\t\t\\param scale_max the value corresponding to the top of the graph\r\n\t\t\\param graph_size size of the graph widget\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void PlotMultiLines(const char* label, std::vector<float*> values, int values_count, const std::vector<ImVec4>& colors, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0.0f, 0.0f));\r\n}\r\n\r\nnamespace sibr {\r\n\r\n\t/** Display an image using ImGui::image, with additional options.\r\n\t\\param texture the ID of the texture to display\r\n\t\\param displaySize the target size\r\n\t\\param uv0 bottom-left corner of the image to display\r\n\t\\param uv1 top-right corner of the image to display\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void DisplayImageGui(\r\n\t\tGLuint texture,\r\n\t\tconst sibr::Vector2i & displaySize,\r\n\t\tconst sibr::Vector2f& uv0 = { 0, 0 },\r\n\t\tconst sibr::Vector2f& uv1 = { 1, 1 }\r\n\t);\r\n\r\n\t/** Store user interaction information to be returned when displaying an image. */\r\n\tstruct SIBR_GRAPHICS_EXPORT CallBackData {\r\n\t\tsibr::Vector2f positionRatio; ///< Position ratio.\r\n\t\tsibr::Vector2f itemPos, itemSize, mousePos; ///< Mouse information.\r\n\t\tfloat scroll = 0.0f; ///< Scroll amount.\r\n\t\tbool isHoovered = false, isClickedRight = false, isClickedLeft = false,\r\n\t\t\tisReleasedRight = false, isReleasedLeft = false, ctrlPressed = false; ///< Is the image currently: hovered, right-clicked pressed, left-click pressed , right-click released, left-click released, is the ctrl key pressed.\r\n\t};\r\n\r\n\t/** Display an image using ImGui::image with support for returning interaction information (did the user click the image, etc.).\r\n\t\\param texture the ID of the texture to display\r\n\t\\param displaySize the target size\r\n\t\\param callbackDataOut will contain interaction information for the current frame \r\n\t\\param uv0 bottom-left corner of the image to display\r\n\t\\param uv1 top-right corner of the image to display\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void ImageWithCallback(\r\n\t\tGLuint texture,\r\n\t\tconst sibr::Vector2i & displaySize,\r\n\t\tCallBackData & callbackDataOut,\r\n\t\tconst sibr::Vector2f& uv0 = { 0, 0 },\r\n\t\tconst sibr::Vector2f& uv1 = { 1, 1 }\r\n\t);\r\n\r\n\t/** Store additional user zoom information to be returned when displaying an image. */\r\n\tstruct SIBR_GRAPHICS_EXPORT ZoomData {\r\n\r\n\t\t/** \\return the zoomed region top left corner in image space. */\r\n\t\tsibr::Vector2f topLeft()\t\tconst;\r\n\r\n\t\t/** \\return the zoomed region bottom right corner in image space. */\r\n\t\tsibr::Vector2f bottomRight()\tconst;\r\n\r\n\t\t/** Convert pixel coordinates in UV, taking the zoom into account.\r\n\t\t\\param pos the pixel position\r\n\t\t\\return the corresponding UVs\r\n\t\t*/\r\n\t\tsibr::Vector2f uvFromBoxPos(const sibr::Vector2f& pos)\tconst;\r\n\r\n\t\t/** Rescale zoom data using a reference region size.\r\n\t\t\\param size the region size\r\n\t\t\\return the resized data\r\n\t\t*/\r\n\t\tZoomData scaled(const sibr::Vector2f& size) const; \r\n\r\n\t\tsibr::Vector2f center = sibr::Vector2f(0.5f, 0.5f), diagonal = sibr::Vector2f(0.5f, 0.5f),\r\n\t\t\ttmpTopLeft, tmpBottonRight, firstClickPixel, secondClickPixel; ///< Zoomed region corners and location.\r\n\t\tbool underMofidication = false; ///< Is the user currently zooming. \r\n\t};\r\n\r\n\t/** Store user interaction and zooming information to be returned when displaying an image. */\r\n\tstruct SIBR_GRAPHICS_EXPORT ZoomInterraction {\r\n\t\t/** Update zoom information.\r\n\t\t\\param canvasSize size ot the displayed region of the image\r\n\t\t*/\r\n\t\tvoid updateZoom(const sibr::Vector2f& canvasSize);\r\n\r\n\t\tCallBackData callBackData; ///< Interaction data.\r\n\t\tZoomData zoomData; ///< Zoom data.\r\n\t};\r\n\r\n\t/** Display an image using ImGui::image with support for returning user zoom information.\r\n\t\\param texture the ID of the texture to display\r\n\t\\param displaySize the target size\r\n\t\\param zoom will contain zoom information for the current frame\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void ImageWithZoom(\r\n\t\tGLuint texture,\r\n\t\tconst sibr::Vector2i & displaySize,\r\n\t\tZoomInterraction & zoom\r\n\t);\r\n\r\n\t/** Represent a segment defined by the user by clicking on screen.  */\r\n\tstruct SIBR_GRAPHICS_EXPORT SegmentSelection {\r\n\r\n\t\t/** Update based on user interaction data.\r\n\t\t\\param callback user interaction data\r\n\t\t\\param size the region size\r\n\t\t\\param zoom optional zoom interaction data\r\n\t\t*/\r\n\t\tvoid update(const CallBackData& callback, const sibr::Vector2i& size, const ZoomData& zoom = {});\r\n\r\n\t\t/** Generate a rasterized line as a list of pixels.*/\r\n\t\tvoid computeRasterizedLine();\r\n\r\n\t\tsibr::Vector2i firstPosScreen, secondPosScreen, firstPosIm, secondPosIm; ///< Segment endpoints in image and screen space.\r\n\t\tstd::vector<sibr::Vector2i> rasterizedLine; ///< List of pixel covered by the rasterized line.\r\n\t\tbool first = false, valid = false; ///< Current interactions state.\r\n\t};\r\n}\r\n\r\n/** Convert an ImGui vector to a sibr vector.\r\n\\param v the vector to convert\r\n\\return the corresponding sibr vector.\r\n*/\r\ntemplate<typename T> sibr::Vector<T, 2> toSIBR(const ImVec2 & v) {\r\n\treturn sibr::Vector<T, 2>(v.x, v.y);\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Image.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/graphics/Image.hpp\"\r\n#include <fstream>\r\n\r\nnamespace sibr\r\n{\r\n\tnamespace opencv\r\n\t{\r\n\r\n\r\n\t\tfloat\t\t\timageTypeCVRange(int cvDepth)\r\n\t\t{\r\n\t\t\t// keep in mind\r\n\t\t\t//enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };\r\n\t\t\tstatic float ranges[] = {\r\n\t\t\t\timageTypeRange<uint8>(),\r\n\t\t\t\timageTypeRange<int8>(),\r\n\t\t\t\timageTypeRange<uint16>(),\r\n\t\t\t\timageTypeRange<int16>(),\r\n\t\t\t\timageTypeRange<int32>(),\r\n\t\t\t\timageTypeRange<float>(),\r\n\t\t\t\timageTypeRange<double>()\r\n\t\t\t};\r\n\t\t\treturn ranges[cvDepth];\r\n\t\t}\r\n\r\n\t\tvoid\t\t\tconvertBGR2RGB(cv::Mat& img)\r\n\t\t{\r\n\t\t\tswitch (img.channels())\r\n\t\t\t{\r\n\t\t\tcase 3:\r\n\t\t\t\tcv::cvtColor(img, img, cv::COLOR_BGR2RGB);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 4:\r\n\t\t\t\tcv::cvtColor(img, img,  cv::COLOR_BGRA2RGBA);\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid\t\t\tconvertRGB2BGR(cv::Mat& img)\r\n\t\t{\r\n\t\t\tswitch (img.channels())\r\n\t\t\t{\r\n\t\t\tcase 3:\r\n\t\t\t\tcv::cvtColor(img, img, cv::COLOR_RGB2BGR);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 4:\r\n\t\t\t\tcv::cvtColor(img, img, cv::COLOR_RGBA2BGRA);\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t} // namespace opencv\r\n\r\n\tsibr::ImageRGBA convertL32FtoRGBA(const sibr::ImageL32F & imgF)\r\n\t{\r\n\t\tsibr::ImageRGBA out(imgF.w(), imgF.h());\r\n\t\tfor (uint y = 0; y < out.h(); ++y) {\r\n\t\t\tfor (uint x = 0; x < out.w(); ++x) {\r\n\t\t\t\tunsigned char const * p = reinterpret_cast<unsigned char const *>(&imgF(x, y).x());\r\n\t\t\t\tfor (std::size_t i = 0; i != sizeof(float); ++i) {\r\n\t\t\t\t\tout(x, y)[i] = p[i];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn out;\r\n\t}\r\n\r\n\tsibr::ImageL32F convertRGBAtoL32F(const sibr::ImageRGBA & imgRGBA)\r\n\t{\r\n\t\tsibr::ImageL32F out(imgRGBA.w(), imgRGBA.h());\r\n#pragma omp parallel for\r\n\t\tfor (int y = 0; y < int(out.h()); ++y) {\r\n\t\t\tfor (uint x = 0; x < out.w(); ++x) {\r\n\t\t\t\tunsigned char * p = reinterpret_cast<unsigned char *>(&out(x, y).x());\r\n\t\t\t\tfor (std::size_t i = 0; i != sizeof(float); ++i) {\r\n\t\t\t\t\tp[i] = imgRGBA(x, y)[i];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn out;\r\n\t}\r\n\r\n\tsibr::ImageRGBA convertRGB32FtoRGBA(const sibr::ImageRGB32F & imgF)\r\n\t{\r\n\t\tsibr::ImageRGBA out(3*imgF.w(), imgF.h());\r\n#pragma omp parallel for\r\n\t\tfor (int y = 0; y < int(imgF.h()); ++y) {\r\n\t\t\tfor (uint x = 0; x < imgF.w(); ++x) {\r\n\t\t\t\tfor (int k = 0; k < 3; k++) {\r\n\t\t\t\t\tunsigned char const * p = reinterpret_cast<unsigned char const *>(&imgF(x, y)[k]);\r\n\t\t\t\t\tfor (std::size_t i = 0; i != sizeof(float); ++i) {\r\n\t\t\t\t\t\tout(k*imgF.w() + x, y)[i] = p[i];\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn out;\r\n\t}\r\n\r\n\t sibr::ImageRGB32F convertRGBAtoRGB32F(const sibr::ImageRGBA& imgRGBA)\r\n\t{\r\n\t\tsibr::ImageRGB32F out(imgRGBA.w() / 3, imgRGBA.h());\r\n#pragma omp parallel for\r\n\t\tfor (int y = 0; y < int(out.h()); ++y) {\r\n\t\t\tfor (uint x = 0; x < out.w(); ++x) {\r\n\t\t\t\tfor (int k = 0; k < 3; k++) {\r\n\t\t\t\t\tunsigned char* p = reinterpret_cast<unsigned char*>(&out(x, y)[k]);\r\n\t\t\t\t\tfor (std::size_t i = 0; i != sizeof(float); ++i) {\r\n\t\t\t\t\t\tp[i] = imgRGBA(k * out.w() + x, y)[i];\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn out;\r\n\t}\r\n\r\n\t sibr::ImageRGBA convertNormalMapToSphericalHalf(const sibr::ImageRGB32F & imgF)\r\n\t{\r\n\t\tuint phi_uint; \r\n\t\tuint theta_uint;\r\n\t\tunsigned char * phi_ptr = reinterpret_cast<unsigned char *>(&phi_uint);\r\n\t\tunsigned char * theta_ptr = reinterpret_cast<unsigned char *>(&theta_uint);\r\n\r\n\t\tImageRGBA out(imgF.w(),imgF.h());\r\n\r\n\t\tfor (uint i = 0; i < out.h(); ++i) {\r\n\t\t\tfor (uint j = 0; j < out.w(); ++j) {\t\t\r\n\t\t\t\tconst double phi = std::acos((double)imgF(j, i)[2]);\r\n\t\t\t\tconst double theta = std::atan2((double)imgF(j, i)[1], (double)imgF(j, i)[0]);\r\n\t\t\t\tphi_uint = (uint)((phi / M_PI) * (1 << 16));\r\n\t\t\t\ttheta_uint = (uint)((0.5*(theta / M_PI + 1.0)) * (1 << 16));\r\n\r\n\t\t\t\tunsigned char * out_ptr = reinterpret_cast<unsigned char *>(&out(j, i)[0]);\r\n\t\t\t\tout_ptr[0] = phi_ptr[0];\r\n\t\t\t\tout_ptr[1] = phi_ptr[1];\r\n\t\t\t\tout_ptr[2] = theta_ptr[0];\r\n\t\t\t\tout_ptr[3] = theta_ptr[1];\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn out;\r\n\t}\r\n\r\n\t sibr::ImageRGB32F convertSphericalHalfToNormalMap(const sibr::ImageRGBA & imgRGBA)\r\n\t{\t\r\n\t\tuint phi_uint;\r\n\t\tuint theta_uint; \r\n\t\tunsigned char * phi_ptr = reinterpret_cast<unsigned char *>(&phi_uint);\r\n\t\tunsigned char * theta_ptr = reinterpret_cast<unsigned char *>(&theta_uint);\r\n\r\n\t\tImageRGB32F out(imgRGBA.w(), imgRGBA.h());\r\n\r\n\t\tfor (uint i = 0; i < out.h(); ++i) {\r\n\t\t\tfor (uint j = 0; j < out.w(); ++j) {\t\r\n\t\t\t\tunsigned char const * out_ptr = reinterpret_cast<unsigned char const *>(&imgRGBA(j, i)[0]);\r\n\t\t\t\tphi_ptr[0] = out_ptr[0];\r\n\t\t\t\tphi_ptr[1] = out_ptr[1];\r\n\t\t\t\ttheta_ptr[2] = out_ptr[0];\r\n\t\t\t\ttheta_ptr[3] = out_ptr[1];\r\n\r\n\t\t\t\tfloat theta = ((float)phi_uint*2.0f / (1 << 16) - 1.0f)*float(M_PI);\r\n\t\t\t\tfloat phi = ((float)theta_uint /(1 << 16))*float(M_PI);\r\n\t\t\t\tfloat sin_t = std::sin(theta);\r\n\t\t\t\tfloat cos_t = std::cos(theta);\r\n\t\t\t\tfloat sin_p = std::sin(phi);\r\n\t\t\t\tfloat cos_p = std::cos(phi);\r\n\t\t\t\tout(j, i) = sibr::Vector3f(sin_t*cos_p, sin_t*sin_p, cos_t);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn out;\r\n\t}\r\n\r\n\tImage<unsigned char, 3> coloredClass(const Image<unsigned char, 1>::Ptr imClass) { \r\n\t\t\r\n\t\tconst int color_list[25][3] = {\r\n\t\t\t{255, 179, 0},{128, 62, 117},{166, 189, 215} ,{193, 0, 32},{0,128,255},{0, 125, 52},\r\n\t\t\t{246, 118, 142},{0, 83, 138},{255, 122, 92} ,{0, 255, 0},{255, 142, 0},{179, 40, 81},\r\n\t\t\t{244, 200, 0},{127, 24, 13},{147, 170, 0} ,{89, 51, 21},{241, 58, 19},{35, 44, 22},\r\n\t\t\t{83, 55, 122},{255,0,128},{128,255,0} ,{128,0,255},{206, 162, 98},{128,128,128},{255,255,255}\r\n\t\t};\r\n\r\n\t\tstd::vector<Vector3ub> colors(256);\r\n\t\tfor (int i = 0; i < 255; i++) {\r\n\t\t\tcolors[i] = Vector3ub(color_list[i % 25][0], color_list[i % 25][1], color_list[i % 25][2]);\r\n\t\t}\r\n\t\tcolors[255] = Vector3ub(0, 0, 0);\r\n\t\tImage<unsigned char, 3> imClassColor(imClass->w(), imClass->h());\r\n\r\n\t\tfor (unsigned int i = 0; i < imClass->w(); i++) {\r\n\t\t\tfor (unsigned int j = 0; j < imClass->h(); j++) {\r\n\t\t\t\timClassColor(i, j) = colors[imClass(i, j).x() % 256];\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn imClassColor;\r\n\t}\r\n\r\n\tImage<unsigned char, 3> coloredClass(const Image<int, 1>::Ptr imClass) { \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \r\n\t\tconst int color_list[25][3] = {\r\n\t\t\t{ 255, 179, 0 },{ 128, 62, 117 },{ 166, 189, 215 } ,{ 193, 0, 32 },{ 0,128,255 },{ 0, 125, 52 },\r\n\t\t\t{ 246, 118, 142 },{ 0, 83, 138 },{ 255, 122, 92 } ,{ 0, 255, 0 },{ 255, 142, 0 },{ 179, 40, 81 },\r\n\t\t\t{ 244, 200, 0 },{ 127, 24, 13 },{ 147, 170, 0 } ,{ 89, 51, 21 },{ 241, 58, 19 },{ 35, 44, 22 },\r\n\t\t\t{ 83, 55, 122 },{ 255,0,128 },{ 128,255,0 } ,{ 128,0,255 },{ 206, 162, 98 },{ 128,128,128 },{ 255,255,255 }\r\n\t\t};\r\n\r\n\t\tstd::vector<Vector3ub> colors(256);\r\n\t\tfor (int i = 0; i < 255; i++) {\r\n\t\t\tcolors[i] = Vector3ub(color_list[i % 25][0], color_list[i % 25][1], color_list[i % 25][2]);\r\n\t\t}\r\n\t\tcolors[255] = Vector3ub(0, 0, 0);\r\n\t\tImage<unsigned char, 3> imClassColor(imClass->w(), imClass->h());\r\n\r\n\t\tfor (unsigned int j = 0; j < imClass->h(); j++) {\r\n\t\t\tfor (unsigned int i = 0; i < imClass->w(); i++) {\r\n\r\n\t\t\t\tVector3ub color;\r\n\t\t\t\tif (imClass(i, j).x() < 0)\r\n\t\t\t\t\tcolor = colors[255];\r\n\t\t\t\telse\r\n\t\t\t\t\tcolor = colors[imClass(i, j).x() % 256];\r\n\r\n\t\t\t\timClassColor(i, j) = color;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn imClassColor;\r\n\t}\r\n\r\n\tvoid showFloat(const Image<float, 1> & im, bool logScale, double min, double max) {\r\n\t\tImage<unsigned char, 1> imIntensity(im.w(), im.h());\r\n\t\tImage<unsigned char, 3> imColor(im.w(), im.h());\r\n\r\n\t\tif (min == -DBL_MAX && max == DBL_MAX) {\r\n\t\t\tcv::minMaxLoc(im.toOpenCV(), &min, &max);\r\n\t\t}\r\n\t\telse if (min == -DBL_MAX) {\r\n\t\t\tdouble drop;\r\n\t\t\tcv::minMaxLoc(im.toOpenCV(), &min, &drop);\r\n\t\t}\r\n\t\telse if (max == DBL_MAX) {\r\n\t\t\tdouble drop;\r\n\t\t\tcv::minMaxLoc(im.toOpenCV(), &drop, &max);\r\n\t\t}\r\n\r\n\t\tif (logScale) {\r\n\t\t\tmin = log(min);\r\n\t\t\tmax = log(max);\r\n\t\t}\r\n\r\n\t\tfor (unsigned int j = 0; j < im.h(); j++) {\r\n\t\t\tfor (unsigned int i = 0; i < im.w(); i++) {\r\n\t\t\t\tif (logScale)\r\n\t\t\t\t\timIntensity(i, j).x() = static_cast<unsigned char>(std::max(0.0, std::min((log(im(i, j).x()) - min) * 255 / (max - min), 255.0)));\r\n\t\t\t\telse\r\n\t\t\t\t\timIntensity(i, j).x() = static_cast<unsigned char>(std::max(0.0, std::min((im(i, j).x() - min) * 255 / (max - min), 255.0)));\r\n\t\t\t}\r\n\t\t}\r\n\t\tcv::Mat colorMat;\r\n\t\tcv::applyColorMap(imIntensity.toOpenCV(), colorMat, cv::COLORMAP_PARULA);\r\n\t\timColor.fromOpenCVBGR(colorMat);\r\n\t\tshow(imColor);\r\n\t}\r\n\r\n\tcv::Mat duplicate3(cv::Mat c) {\r\n\t\tcv::Mat out;\r\n\t\tcv::Mat in[] = { c, c, c };\r\n\t\tcv::merge(in, 3, out);\r\n\t\treturn out;\r\n\t}\r\n\r\n\t// Adopted from http://www.64lines.com/jpeg-width-height. Gets the JPEG size from the file stream passed\r\n\t// to the function, file reference: http://www.obrador.com/essentialjpeg/headerinfo.htm\r\n\tsibr::Vector2i IImage::get_jpeg_size(std::ifstream& file)\r\n\t{\r\n\t\t// Check for valid JPG\r\n\t\tif (file.get() != 0xFF || file.get() != 0xD8)\r\n\t\t\treturn Eigen::Vector2i(-1, -1);\r\n\t\tfile.get(); file.get(); // Skip the rest of JPG identifier.\r\n\r\n\t\tstd::streampos block_length = static_cast<std::streampos>(file.get() * 256 + file.get() - 2);\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\t// Skip the first block since it doesn't contain the resolution\r\n\t\t\tfile.seekg(file.tellg() + block_length);\r\n\r\n\t\t\t// Check if we are at the start of another block\r\n\t\t\tif (!file.good() || file.get() != 0xFF)\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t// If the block is not the \"Start of frame\", skip to the next block\r\n\t\t\tif (file.get() != 0xC0)\r\n\t\t\t{\r\n\t\t\t\tblock_length = static_cast<std::streampos>(file.get() * 256 + file.get() - 2);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\t// Found the appropriate block. Extract the dimensions.\r\n\t\t\tfor (int i = 0; i < 3; ++i) file.get();\r\n\r\n\t\t\tint height = file.get() * 256 + file.get();\r\n\t\t\tint width = file.get() * 256 + file.get();\r\n\t\t\treturn sibr::Vector2i(width, height);\r\n\t\t}\r\n\t\treturn sibr::Vector2i(-1, -1);\r\n\t}\r\n\r\n\t// Adopted from http://stackoverflow.com/questions/22638755/image-dimensions-without-loading\r\n\t// kudos to Lukas (http://stackoverflow.com/users/643315/lukas).\r\n\tsibr::Vector2i IImage::imageResolution(const std::string& file_path)\r\n\t{\r\n\t\tenum ValidFormats {\r\n\t\t\tPNG = 0,\r\n\t\t\tBMP,\r\n\t\t\tTGA,\r\n\t\t\tJPG,\r\n\t\t\tJPEG,\r\n\t\t\tVALID_COUNT\r\n\t\t};\r\n\r\n\t\tstd::string valid_extensions[] = {\r\n\t\t\t\"png\",\r\n\t\t\t\"bmp\",\r\n\t\t\t\"tga\",\r\n\t\t\t\"jpg\",\r\n\t\t\t\"jpeg\"\r\n\t\t};\r\n\r\n\t\tstd::string extension = sibr::to_lower(sibr::getExtension(file_path));\r\n\t\t\r\n\t\tint extension_id = 0;\r\n\t\twhile (extension_id < VALID_COUNT &&\r\n\t\t\textension != valid_extensions[extension_id])\r\n\t\t\textension_id++;\r\n\r\n\t\tif (extension_id == VALID_COUNT)\r\n\t\t\treturn Eigen::Vector2i(-1, -1);\r\n\r\n\t\tstd::ifstream file(file_path, std::ios::binary);\r\n\t\tif (!file.good())\r\n\t\t\treturn Eigen::Vector2i(-1, -1);\r\n\r\n\t\tuint32_t temp = 0;\r\n\t\tint32_t  width = -1;\r\n\t\tint32_t  height = -1;\r\n\t\tswitch (extension_id)\r\n\t\t{\r\n\t\tcase PNG:\r\n\t\t\tfile.seekg(16);\r\n\t\t\tfile.read(reinterpret_cast<char*>(&width), 4);\r\n\t\t\tfile.read(reinterpret_cast<char*>(&height), 4);\r\n\t\t\twidth = sibr::ByteStream::ntohl(width);\r\n\t\t\theight = sibr::ByteStream::ntohl(height);\r\n\t\t\tbreak;\r\n\t\tcase BMP:\r\n\t\t\tfile.seekg(14);\r\n\t\t\tfile.read(reinterpret_cast<char*>(&temp), 4);\r\n\t\t\tif (temp == 40) // Windows Format\r\n\t\t\t{\r\n\t\t\t\tfile.read(reinterpret_cast<char*>(&width), 4);\r\n\t\t\t\tfile.read(reinterpret_cast<char*>(&height), 4);\r\n\t\t\t}\r\n\t\t\telse if (temp == 20) // MAC Format\r\n\t\t\t{\r\n\t\t\t\tfile.read(reinterpret_cast<char*>(&width), 2);\r\n\t\t\t\tfile.read(reinterpret_cast<char*>(&height), 2);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase TGA:\r\n\t\t\tfile.seekg(12);\r\n\t\t\tfile.read(reinterpret_cast<char*>(&width), 2);\r\n\t\t\tfile.read(reinterpret_cast<char*>(&height), 2);\r\n\t\t\tbreak;\r\n\t\tcase JPG:\r\n\t\tcase JPEG:\r\n\t\t\treturn get_jpeg_size(file);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\treturn sibr::Vector2i(width, height);\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Image.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/system/ByteStream.hpp\"\n\n# pragma warning(push, 0)\n#  include <opencv2/core/core.hpp>\n#  include <opencv2/imgproc/imgproc.hpp>\n#  include <opencv2/highgui/highgui.hpp>\n#  include <boost/filesystem.hpp>\n# pragma warning(pop)\n\n#include <utility>\n#include <vector>\n\nnamespace cv\n{\n\t/** Extend OpenCV support for Eigen types. \n\t\\ingroup sibr_graphics\n\t*/\n\t/*template <typename T_Type, int cn>\n\tclass DataType<Eigen::Matrix<T_Type, cn, 1, Eigen::DontAlign> >\n\t{\n\tpublic:\n\t\ttypedef Eigen::Matrix<T_Type, cn, 1, Eigen::DontAlign> value_type; ///< Vector type.\n\t\ttypedef Eigen::Matrix<typename DataType<T_Type>::work_type, cn, 1, Eigen::DontAlign> work_type; ///< Wrapper type.\n\t\ttypedef T_Type channel_type; ///< Component type.\n\t\ttypedef value_type vec_type; ///< Vector type.\n\t\tenum { generic_type = 0, depth = DataDepth<channel_type>::value, channels = cn, fmt = ((channels - 1) << 8) + DataDepth<channel_type>::fmt, type = CV_MAKETYPE(depth, channels) };\n\t};*/\n}\n\nnamespace sibr\n{\n\n\t/**\n\t* \\addtogroup sibr_graphics\n\t* @{\n\t*/\n\n\tnamespace opencv\n\t{\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <typename T_Type>\n\t\tSIBR_GRAPHICS_EXPORT int\t\timageType(void);// { return -1; } // default, unknown\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< uint8 >(void) { return CV_8U; }\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< int8  >(void) { return CV_8S; }\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< uint16>(void) { return CV_16U; }\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< int16 >(void) { return CV_16S; }\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< int32 >(void) { return CV_32S; }\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< float >(void) { return CV_32F; }\n\t\t/** \\return the OpenCV type corresponding to a C type. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline int\t\timageType< double>(void) { return CV_64F; }\n\n\t\t/** \\return the size of the range of values a type can take when used in OpenCV. */\n\t\ttemplate <typename T_Type>\n\t\tinline float\t\t\timageTypeRange(void) {\n\t\t\treturn (float)std::numeric_limits<T_Type>::max();//-std::numeric_limits<T_Type>::min();\n\t\t}\n\t\t/** \\return the size of the range of values a type can take when used in OpenCV. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline float\t\t\timageTypeRange< float >(void) { return 1.f; }\n\t\t/** \\return the size of the range of values a type can take when used in OpenCV. */\n\t\ttemplate <> SIBR_GRAPHICS_EXPORT inline float\t\t\timageTypeRange< double>(void) { return 1.f; }\n\n\t\t/** Get the size of the range of values an OpenCV type can take.\n\t\t\\param cvDepth the OpenCV type depth\n\t\t\\return the size of the range\n\t\t*/\n\t\tSIBR_GRAPHICS_EXPORT float\t\t\timageTypeCVRange(int cvDepth);\n\n\t\t/** Convert a BGR cv::Mat into a RGB cv::Mat, in-place.\n\t\t\\param dst the matrix to convert\n\t\t*/\n\t\tSIBR_GRAPHICS_EXPORT void\t\t\tconvertBGR2RGB(cv::Mat& dst);\n\n\t\t/** Convert a BGR cv::Mat into a RGB cv::Mat, in-place.\n\t\t\\param dst the matrix to convert\n\t\t*/\n\t\tSIBR_GRAPHICS_EXPORT void\t\t\tconvertRGB2BGR(cv::Mat& dst);\n\t}\n\n\ttypedef\tVector4f ColorRGBA;\n\n\t/** @} */\n\n\t/**\n\t* Interface virtual class for all the templated image classes.\n\t* Contains all functions not making reference to the internal type or numComp in their signature/return type\n\t* \\sa Image\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT IImage {\n\tpublic:\n\t\tSIBR_CLASS_PTR(IImage);\n\n\t\t/** Load an image from the disk (png, jpeg, exr, etc., see OpenCV cv::imread documentation for more details).\n\t\t\\param filename the path to the file\n\t\t\\param verbose display additional informations\n\t\t\\param warning_if_not_found log if the file doesn't exist, even if verbose is set to false\n\t\t\\return a success flag\n\t\t*/\n\t\tvirtual bool\t\t\tload(const std::string& filename, bool verbose = true, bool warning_if_not_found = true) = 0;\n\t\t\n\t\t/** Load an image from the disk (stored as a raw binary blob).\n\t\t\\param filename the path to the file\n\t\t\\param verbose display additional informations\n\t\t\\return a success flag\n\t\t*/\n\t\tvirtual bool\t\t\tloadByteStream(const std::string& filename, bool verbose = true) = 0;\n\n\t\t/** Save an image to the disk (png, jpeg, see OpenCV cv::imwrite documentation for more details).\n\t\t\\param filename the path to the file\n\t\t\\param verbose display additional informations\n\t\t\\warning HDR images will be converted to LDR, \\sa saveHDR .\n\t\t\\warning Some image formats can't be stored in some file formats.\n\t\t*/\n\t\tvirtual void\t\t\tsave(const std::string& filename, bool verbose = true) const = 0;\n\n\t\t/** Save an image to the disk (as a raw binary blob).\n\t\t\\param filename the path to the file\n\t\t\\param verbose display additional informations\n\t\t*/\n\t\tvirtual void\t\t\tsaveByteStream(const std::string& filename, bool verbose = true) const = 0;\n\n\t\t/** Save an HDR image to the disk (exr, hdr, see OpenCV cv::imwrite documentation for more details).\n\t\t\\param filename the path to the file\n\t\t\\param verbose display additional informations\n\t\t*/\n\t\tvirtual void\t\t\tsaveHDR(const std::string& filename, bool verbose = true) const = 0;\n\n\t\t/** \\return the image width. */\n\t\tvirtual uint\t\t\tw(void) const = 0;\n\n\t\t/** \\return the image height. */\n\t\tvirtual uint\t\t\th(void) const = 0;\n\n\t\t/** \\return the image size. */\n\t\tvirtual sibr::Vector2u\tsize(void) const = 0;\n\n\t\t/** Check if a pixel (x,y) is inside the image boundaries.\n\t\t\\param xy the pixel coordinates\n\t\t\\return true if 0<=x<w and 0<=y=<h */\n\t\tvirtual bool\t\t\tisInRange(const ::sibr::Vector2i & xy)  const = 0;\n\n\t\t/** Get the value stored at a pixel and convert it to a string representation.\n\t\t\\param xy the pixel coordinates\n\t\t\\return a string representation of the pixel value.\n\t\t*/\n\t\tvirtual std::string\t\tpixelStr(const ::sibr::Vector2i & xy)  const = 0;\n\n\t\t/** \\return the number of components of the image. */\n\t\tvirtual uint\t\t\tnumComp(void) const = 0;\n\n\t\t/** \\return the size of a pixel value in bytes. */\n\t\tvirtual uint\t\t\tsizeOfComp(void) const = 0;\n\n\t\t/** Flip the image along the horizontal axis. */\n\t\tvirtual void\t\t\tflipH(void) = 0;\n\n\t\t/** Flip the image along the vertical axis. */\n\t\tvirtual void\t\t\tflipV(void) = 0;\n\n\t\t/** \\return the image OpenCV type. */\n\t\tvirtual int\t\t\t\topencvType(void) const = 0;\n\n\t\t/** \\return a reference to the underlying OpenCV matrix. */\n\t\tvirtual const cv::Mat&\ttoOpenCV(void) const = 0;\n\n\t\t/** \\return a reference to the underlying OpenCV matrix. */\n\t\tvirtual cv::Mat&\t\ttoOpenCVnonConst(void) = 0;\n\n\t\t/** \\return a copy of the matrix with channels flipped.\n\t\t\\note Only applies to 3 and 4 channel images.\n\t\t*/\n\t\tvirtual cv::Mat\t\t\ttoOpenCVBGR(void) const = 0;\n\n\t\t/** Replace the content of the image with the content of another matrix.\n\t\t\\param img the new matrix\n\t\t*/\n\t\tvirtual void\t\t\tfromOpenCV(const cv::Mat& img) = 0;\n\n\t\t/** Replace the content of the image with the content of another matrix, flipping channels.\n\t\t\\param img the new matrix\n\t\t\\note Only applies to 3 and 4 channel images.\n\t\t*/\n\t\tvirtual void\t\t\tfromOpenCVBGR(const cv::Mat& img) = 0;\n\n\t\t/** Get the size of jpeg image file by reading its header.\n\t\t\\param file the input filestream, already opened.\n\t\t\\return The size (width,heighgt) else (-1, -1) if the header cannot be read .\n\t\t*/\n\t\tstatic sibr::Vector2i\t\t\tget_jpeg_size(std::ifstream& file);\n\n\t\t/** Get the size of an image file from its header. Supported file type: {png, jpg, jpeg, bmp, tga}.\n\t\t\\param file_path the input file path\n\t\t\\return The size (width,heighgt) else (-1, -1) if the header cannot be read .\n\t\t*/\n\t\tstatic sibr::Vector2i\t\t\timageResolution(const std::string& file_path);\n\n\t};\n\n\n\t/** Wrapper around an image pointer.\n\t* \\ingroup sibr_graphics */\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tclass ImagePtr;\n\n\t/**\n\t* This class is used to store images. Internally, a cv::Mat\n\t* is used. The template parameter define a fixed size/format that\n\t* will be used to convert automatically the image format when\n\t* you load or copy from another image.\n\t* \\warning We disallow copy as we would have to do a costly in-depth copy of the underlying cv::Mat.\n\t* If you store images in a vector attribute of a class, you might have to SIBR_DISALLOW_COPY of your class.\n\t* \\note OpenCV uses generally BGR channels (e.g. after loading an image file). \n\t* However the internal cv::Mat of this class stores\n\t* RGB channels. You can get RGB cv::Mat with toOpenCV() or use\n\t* toOpenCVBGR(). (Most of OpenCV's features works with RGB too but\n\t* not imshow, imwrite, imread.)\n\t* \\ingroup sibr_graphics\n\t*/\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tclass Image : public IImage {\n\tpublic:\n\t\ttypedef T_Type\t\t\t\t\t\tType;\n\t\ttypedef ImagePtr<T_Type, T_NumComp> Ptr;\n\n\t\ttypedef Eigen::Matrix<T_Type, T_NumComp, 1, Eigen::DontAlign> Pixel;\n\t\tenum { e_NumComp = T_NumComp };\n\n\tpublic:\n\n\t\t/// Default constructor.\n\t\tImage(void);\n\n\t\t/** Constructor.\n\t\t\\param width image width\n\t\t\\param height image height\n\t\t\\warning The image content will be undefined.\n\t\t*/\n\t\tImage(uint width, uint height);\n\n\t\t/** Constructor.\n\t\t\\param width image width\n\t\t\\param height image height\n\t\t\\param init default value to use for all components of all pixels\n\t\t*/\n\t\tImage(uint width, uint height, const T_Type& init);\n\n\t\t/** Constructor.\n\t\t\\param width image width\n\t\t\\param height image height\n\t\t\\param init default value to use for all pixels\n\t\t*/\n\t\tImage(uint width, uint height, const Pixel& init);\n\n\t\t/** Move constructor.\n\t\t\\param other image to move, don't use after move\n\t\t*/\n\t\tImage(Image&& other);\n\n\t\t/** Move operator.\n\t\t\\param other image to move, don't use after move\n\t\t*/\n\t\tImage& operator=(Image&& other) noexcept;\n\n\t\tImage& fill(Pixel const& value);\n\n\t\t/**\n\t\t\\copydoc IImage::load\n\t\t*/\n\t\tbool\t\tload(const std::string& filename, bool verbose = true, bool warning_if_not_found = true);\n\n\t\t/**\n\t\t\\copydoc IImage::loadByteStream\n\t\t*/\n\t\tbool\t\tloadByteStream(const std::string& filename, bool verbose = true);\n\t\t\n\t\t/**\n\t\t\\copydoc IImage::save\n\t\t*/\n\t\tvoid\t\tsave(const std::string& filename, bool verbose = true) const;\n\n\t\t/**\n\t\t\\copydoc IImage::saveByteStream\n\t\t*/\n\t\tvoid\t\tsaveByteStream(const std::string& filename, bool verbose = true) const;\n\n\t\t/**\n\t\t\\copydoc IImage::saveHDR\n\t\t*/\n\t\tvoid\t\tsaveHDR(const std::string& filename, bool verbose = true) const;\n\n\t\t/** Pixel accessor.\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tconst Pixel&\toperator()(uint x, uint y) const;\n\n\t\t/** Pixel accessor.\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tPixel&\t\t\toperator()(uint x, uint y);\n\n\t\t/** Pixel accessor.\n\t\t\\param xy pixel coordinates\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tconst Pixel&\toperator()(const sibr::Vector2i & xy) const;\n\n\t\t/** Pixel accessor.\n\t\t\\param xy pixel coordinates\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tPixel&\t\t\toperator()(const sibr::Vector2i & xy);\n\n\t\t/** Pixel accessor.\n\t\t\\param xy pixel coordinates\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tconst Pixel&\toperator()(const ::sibr::Vector2f & xy) const;\n\n\t\t/** Pixel accessor.\n\t\t\\param xy pixel coordinates\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tPixel&\t\t\toperator()(const ::sibr::Vector2f & xy);\n\n\t\t/** \\copydoc IImage::pixelStr */\n\t\tvirtual std::string\t\tpixelStr(const ::sibr::Vector2i & xy)  const;\n\n\t\t/** \\return a pointer to the raw image data. */\n\t\tconst void*\t\tdata(void) const;\n\n\t\t/** \\return a pointer to the raw image data. */\n\t\tvoid*\t\t\tdata(void);\n\n\n\t\t/** Convert a pixel value to a 4 components float vector (in 0,1).\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return the normalized expanded value\n\t\t*/\n\t\tColorRGBA\t color(uint x, uint y) const;\n\n\t\t/** Set a pixel value from 4 components float vector (in 0,1).\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\param c the new value\n\t\t*/\n\t\tvoid\t\t color(uint x, uint y, const ColorRGBA& c);\n\n\t\t/** Helper to convert a 4 components float vector (in 0,1) to the proper pixel format.\n\t\t\\param rgba the value to convert\n\t\t\\return the converted value\n\t\t*/\n\t\tstatic Pixel color(const ColorRGBA& rgba);\n\n\t\t/** Generate a resized version of the current image.\n\t\t\\param width the target width\n\t\t\\param height the target height\n\t\t\\param cv_interpolation_method the up/down scaling method\n\t\t\\return the resized image\n\t\t*/ \n\t\tImage\t\tresized(int width, int height, int cv_interpolation_method = cv::INTER_LINEAR) const;\n\t\t\n\t\t/** Generate a resized version of the current image so that the maximum \n\t\tdimension (either width or height) is now equal to maxlen. Preserve the original ratio.\n\t\tExample: src is 2048x1024, resizedMax(1024) -> dst is 1024x512\n\t\t\\param maxlen the target maximum dimension value\n\t\t\\return the resized image\n\t\t*/ \n\t\tImage\t\tresizedMax(int maxlen) const;\n\n\t\t/** \\return a deep copy of the image. */\n\t\tImage\t\tclone(void) const;\n\n\t\t/** \\return a pointer to a deep copy of the image. */\n\t\tImagePtr<T_Type, T_NumComp>\t  clonePtr(void) const;\n\n\t\t/** \\return the image width. */\n\t\tuint\t\t\tw(void) const;\n\n\t\t/** \\return the image height. */\n\t\tuint\t\t\th(void) const;\n\n\t\t/** \\return the image size. */\n\t\tsibr::Vector2u size(void) const;\n\n\t\t/** Check if a pixel (x,y) is inside the image boundaries.\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return true if 0<=x<w and 0<=y=<h */\n\t\ttemplate <typename T>\n\t\tbool\t\t\tisInRange(T x, T y) const { return (x >= 0 && y >= 0 && x < (T)w() && y < (T)h()); }\n\n\t\t/** Check if a pixel (x,y) is inside the image boundaries.\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return true if 0<=x<w and 0<=y=<h \n\t\t\\todo Duplicate call used in inpainting, remove.\n\t\t*/\n\t\ttemplate <typename T>\n\t\tbool\t\t\tinRange(T x, T y) const { return isInRange(x, y); }\n\n\t\t/** Check if a pixel (x,y) is inside the image boundaries.\n\t\t\\param xy the pixel coordinates\n\t\t\\return true if 0<=x<w and 0<=y=<h */\n\t\tbool\t\t\tisInRange(const sibr::Vector2i & xy)  const { return (xy.x() >= 0 && xy.y() >= 0 && xy.x() < (int)w() && xy.y() < (int)h()); }\n\t\t\n\t\t/** Check if a pixel (x,y) is inside the image boundaries.\n\t\t\\param xy the pixel coordinates\n\t\t\\return true if 0<=x<w and 0<=y=<h */\n\t\tbool\t\t\tisInRange(const sibr::Vector2f & xy)  const { return (xy.x() >= 0 && xy.y() >= 0 && xy.x() < (float)w() && xy.y() < (float)h()); }\n\n\t\t/** \\copydoc IImage::numComp */\n\t\tuint\t\tnumComp(void) const;\n\n\t\t/** \\copydoc IImage::sizeOfComp */\n\t\tuint\t\tsizeOfComp(void) const;\n\n\t\t/** \\copydoc IImage::flipH */\n\t\tvoid\t\tflipH(void);\n\n\t\t/** \\copydoc IImage::flipV */\n\t\tvoid\t\tflipV(void);\n\n\t\t/** \\copydoc IImage::opencvType */\n\t\tint\t\t\t\topencvType(void) const { return CV_MAKETYPE(opencv::imageType<T_Type>(), T_NumComp); }\n\n\t\t/** \\copydoc IImage::toOpenCV */\n\t\tconst cv::Mat&\ttoOpenCV(void) const { return _pixels; }\n\n\t\t/** \\copydoc IImage::toOpenCVnonConst */\n\t\tcv::Mat&\t\ttoOpenCVnonConst(void) { return _pixels; }\n\t\t\n\t\t/** \\copydoc IImage::toOpenCVBGR */\n\t\tcv::Mat\t\t\ttoOpenCVBGR(void) const;\n\t\t\n\t\t/** \\copydoc IImage::fromOpenCV */\n\t\tvoid\t\t\tfromOpenCV(const cv::Mat& img);\n\t\t\n\t\t/** \\copydoc IImage::fromOpenCVBGR */\n\t\tvoid\t\t\tfromOpenCVBGR(const cv::Mat& img);\n\n\t\t/** Find the component-wise minimum and maximum values contained in the image.\n\t\t\\param minImage will contain the minimum value\n\t\t\\param maxImage will contain the maximum value\n\t\t*/\n\t\tvoid findMinMax(Pixel& minImage, Pixel& maxImage);\n\n\t\t/** Rescale an image content in a defined range.\n\t\t\\param minValue the lower value of the range\n\t\t\\param maxValue the upper value of the range\n\t\t*/\n\t\tvoid remap(const Pixel& minValue, const Pixel& maxValue);\n\n\t\t/** Cast into another image type.\n\t\t\\return the converted image\n\t\t*/\n\t\ttemplate<class T_Image> T_Image cast() const {\n\t\t\tT_Image b;\n\t\t\tb.fromOpenCV(toOpenCV());\n\t\t\treturn b;\n\t\t}\n\n\t\t/** Fetch bilinear interpolated value from floating point pixel coordinates.\n\t\t\t\\param pixel query position in [0,w[x[0,h[\n\t\t\t\\return the interpolated value\n\t\t*/\n\t\tPixel bilinear(const sibr::Vector2f & pixel) const;\n\n\t\t/** Fetch bicubic interpolated value from floating point pixel coordinates.\n\t\t\t\\param pixelPosition query position in [0,w[x[0,h[\n\t\t\t\\return the interpolated value\n\t\t*/\n\t\tPixel bicubic(const sibr::Vector2f & pixelPosition) const;\n\n\t\t/** Disallow copy constructor.\n\t\t\\param other image to copy\n\t\t*/\n\t\tImage( const Image& other) = delete;\n\n\t\t/** Disallow copy operator.\n\t\t\\param other image to copy\n\t\t*/\n\t\tImage& \t\toperator =(const Image& other) = delete;\n\n\tprotected:\n\n\t\t/** Helper for bicubic interpolation.\n\t\t\\param t blend factor\n\t\t\\param colors colors at the four corners\n\t\t\\return interpolated value\n\t\t*/\n\t\tstatic Eigen::Matrix<float, T_NumComp, 1, Eigen::DontAlign> monoCubic(float t, const Eigen::Matrix<float, T_NumComp, 4, Eigen::DontAlign>& colors);\n\n\t\tcv::Mat\t\t\t_pixels; ///< Pixels stored in RGB format\n\t};\n\n\t/** Provides a wrapper around a pointer to an image. \n\t\\ingroup sibr_graphics\n\t*/\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tclass ImagePtr {\n\tpublic:\n\t\t\n\t\tusing ImageType = Image<T_Type, T_NumComp>; ///< Underlying image type.\n\n\t\tstd::shared_ptr<Image<T_Type, T_NumComp>> imPtr; ///< Pointer type.\n\t\t\n\t\t/// Default constructor.\n\t\tImagePtr() { imPtr = std::shared_ptr<Image<T_Type, T_NumComp>>(); };\n\n\t\t/** Constructor from a raw pointer.\n\t\t\\param imgPtr the raw pointer to wrap\n\t\t*/\n\t\tImagePtr(Image<T_Type, T_NumComp>* imgPtr) { imPtr = std::shared_ptr<Image<T_Type, T_NumComp>>(imgPtr); };\n\t\t\n\t\t/** Constructor from a shared pointer.\n\t\t\\param imgPtr the shared pointer to wrap\n\t\t*/\n\t\tImagePtr(const std::shared_ptr<Image<T_Type, T_NumComp>>& imgPtr)  {imPtr = std::shared_ptr<Image<T_Type, T_NumComp>>(imgPtr); };\n\n\t\t/** Generate a pointer by cloning an image.\n\t\t\\param img the image to clone\n\t\t\\return the pointer\n\t\t*/\n\t\tstatic ImagePtr fromImg(const ImageType & img) { return ImagePtr(std::make_shared<Image<T_Type, T_NumComp>>(img.clone())); };\n\n\t\t/** Set a new pointee.\n\t\t\\param ptr the new image pointer\n\t\t*/\n\t\tvoid reset(ImageType * ptr) { imPtr.reset(ptr); };\n\n\t\t/** \\return the image */\n\t\tImage<T_Type, T_NumComp>*\tget() { return imPtr.get(); };\n\n\t\t/** Pixel accessor.\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tconst typename Image<T_Type, T_NumComp>::Pixel&\t\t\toperator()(uint x, uint y) const;\n\n\t\t/** Pixel accessor.\n\t\t\\param x x coordinate\n\t\t\\param y y coordinate\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\ttypename Image<T_Type, T_NumComp>::Pixel&\t\t\t\toperator()(uint x, uint y);\n\n\t\t/** Pixel accessor.\n\t\t\\param xy pixel coordinates\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\tconst typename Image<T_Type, T_NumComp>::Pixel&\t\t\toperator()(const sibr::Vector2i & xy) const;\n\n\t\t/** Pixel accessor.\n\t\t\\param xy pixel coordinates\n\t\t\\return a reference to the pixel value.\n\t\t*/\n\t\ttypename Image<T_Type, T_NumComp>::Pixel&\t\t\t\toperator()(const sibr::Vector2i & xy);\n\n\t\t/** \\return the dereferenced image */\n\t\tImage<T_Type, T_NumComp>&\t\t\t\t\t\t\t\toperator * () { return imPtr.operator*(); };\n\n\t\t/** \\return the dereferenced image */\n\t\tconst Image<T_Type, T_NumComp>&\t\t\t\t\t\t\toperator * () const { return imPtr.operator*(); };\n\n\t\t/** \\return raw pointer to the image */\n\t\tImage<T_Type, T_NumComp>*\t\t\t\t\t\t\t\toperator -> () { return imPtr.operator->(); };\n\n\t\t/** \\return raw pointer to the image */\n\t\tconst Image<T_Type, T_NumComp>*\t\t\t\t\t\t\toperator -> () const { return imPtr.operator->(); };\n\t\t\n\t\t/** Assign a shared ptr.\n\t\t\\param imgShPtr the shared pointer\n\t\t\\return a reference to the updated pointer\n\t\t*/ \n\t\tstd::shared_ptr<Image<T_Type, T_NumComp>> & \t\t\toperator = (std::shared_ptr<Image<T_Type, T_NumComp>> & imgShPtr) { imPtr = imgShPtr; return &imPtr; };\n\t\t\n\t\t/** \\return true if the image pointer is initialized. */\n\t\toperator bool() { return imPtr.get() != nullptr; };\n\n\t\t/** \\return true if the image pointer is initialized. */\n\t\toperator bool() const { return imPtr.get() != nullptr; };\n\n\t};\n\n\t/**\n\t* \\addtogroup sibr_graphics\n\t* @{\n\t*/\n\n\t/// Standard image types\n\ttypedef Image<unsigned char, 3> ImageRGB;\n\ttypedef Image<unsigned char, 4> ImageRGBA;\n\ttypedef Image<unsigned char, 1> ImageL8;\n\ttypedef Image<unsigned char, 2> ImageUV8;\n\ttypedef Image<unsigned short int, 3> ImageRGB16;\n\ttypedef Image<unsigned short int, 1> ImageL16;\n\ttypedef Image<float, 3>         ImageRGB32F;\n\ttypedef Image<float, 3>         ImageFloat3;\n\ttypedef Image<float, 4>         ImageRGBA32F;\n\ttypedef Image<float, 4>         ImageFloat4;\n\ttypedef Image<float, 1>         ImageL32F;\n\ttypedef Image<float, 1>         ImageFloat1;\n\ttypedef Image<float, 2>         ImageFloat2;\n\ttypedef Image<float, 2>         ImageUV32F;\n\ttypedef Image<bool, 1>          ImageBool1;\n\ttypedef Image<double, 1>        ImageDouble1;\n\ttypedef Image<double, 2>        ImageDouble2;\n\ttypedef Image<double, 3>        ImageDouble3;\n\ttypedef Image<double, 4>        ImageDouble4;\n\ttypedef Image<int, 1>        ImageInt1;\n\ttypedef Image<int, 2>        ImageInt2;\n\ttypedef Image<int, 3>        ImageInt3;\n\ttypedef Image<int, 4>        ImageInt4;\n\n\n\t/** Convert an integer ID map to a colored image using a different random color for each ID. Note that 255 is black.\n\t\\param imClass the ID map\n\t\\return a color coded map\n\t*/\n\tSIBR_GRAPHICS_EXPORT Image<unsigned char, 3> coloredClass(const Image<unsigned char, 1>::Ptr imClass);\n\n\t/** Convert an integer ID map to a colored image using a different random color for each ID. Note that 255 is black.\n\t\\param imClass the ID map\n\t\\return a color coded map\n\t*/\n\tSIBR_GRAPHICS_EXPORT Image<unsigned char, 3> coloredClass(const Image<int, 1>::Ptr imClass);\n\n\t/** Display a 32F image in a debug window, using the Parula colormap after normalizing the values.\n\t\\param im the float image to display\n\t\\param logScale display log(img)\n\t\\param min optional lower bound for the normalization\n\t\\param max optional upper bound for the normalization\n\t*/\n\tSIBR_GRAPHICS_EXPORT void showFloat(const Image<float, 1> & im, bool logScale = false, double min = -DBL_MAX, double max = DBL_MAX);\n\n\t/** Convert a L32F into a RGBA image while preserving bit-level representation.\n\tUseful to save float maps as PNG, and benefit from PNG compression on disk.\n\t\\param imgF the image to convert\n\t\\return the packed RGBA image \n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::ImageRGBA convertL32FtoRGBA(const sibr::ImageL32F & imgF);\n\n\t/** Convert a RGBA into a L32F image while preserving bit-level representation.\n\tUseful to decode float maps stored as as PNG.\n\t\\param imgRGBA the image to convert\n\t\\return the unpacked float image \n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::ImageL32F convertRGBAtoL32F(const sibr::ImageRGBA  & imgRGBA);\n\n\t/** Convert a RGB32F into a RGBA image (3 times wider) while preserving bit-level representation.\n\tUseful to save float maps as PNG, and benefit from PNG compression on disk.\n\t\\param imgF the image to convert\n\t\\return the packed RGBA image \n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::ImageRGBA convertRGB32FtoRGBA(const sibr::ImageRGB32F & imgF);\n\n\t/** Convert a RGBA into a RGB32F image while preserving bit-level representation.\n\tUseful to decode float maps stored as as PNG.\n\t\\param imgRGBA the image to convert\n\t\\return the unpacked float image \n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::ImageRGB32F convertRGBAtoRGB32F(const sibr::ImageRGBA & imgRGBA);\n\n\t/** Convert a RGB32 normal map into a UV16 map storing theta and phi as half floats, packed into a RGBA8.\n\t\\param imgF the XYZ normal map\n\t\\return the packed theta,phi normal map\n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::ImageRGBA convertNormalMapToSphericalHalf(const sibr::ImageRGB32F & imgF);\n\n\t/** Convert a RGBA map, packing theta and phi as half floats, into a RGB32 normal map.\n\t\\param imgF packed theta,phi normal map\n\t\\return the XYZ normal map\n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::ImageRGB32F convertSphericalHalfToNormalMap(const sibr::ImageRGBA & imgF);\n\n\t/** Create a three channels cv::Mat by repeating a single channel cv::Mat.\n\t\\param c the input cv::Mat\n\t\\return a three channels cv::Mat\n\t*/\n\tSIBR_GRAPHICS_EXPORT cv::Mat duplicate3(cv::Mat c);\n\n\t/** Display an image into a popup OpenCV window.\n\t\\param img the image to display\n\t\\param windowTitle the window title\n\t\\param closeWindow close the window after key presses\n\t*/\n\ttemplate <typename T_Type, unsigned T_NumComp>\n\tstatic void\t\tshow(const Image<T_Type, T_NumComp> & img, const std::string& windowTitle = \"sibr::show()\", bool closeWindow = true) {\n\t\tcv::namedWindow(windowTitle, cv::WINDOW_AUTOSIZE | cv::WINDOW_KEEPRATIO | cv::WINDOW_GUI_EXPANDED);\n\t\t// Note: CV_GUI_EXPANDED does only work with Qt\n\t\t\n\t\tcv::imshow(windowTitle, img.toOpenCVBGR());\n\t\tcv::waitKey(0);\n\t\tif (closeWindow) {\n\t\t\tcv::destroyWindow(windowTitle);\n\t\t}\n\t}\n\n\t/*** @} */ \n\n\t// ----- DEFINITIONS -------------\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>::Image(void) :\n\t\t_pixels(0, 0, opencvType()) { }\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>::Image(uint width, uint height) :\n\t\t_pixels(height, width, opencvType()) { }\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>::Image(uint width, uint height, const T_Type& init) :\n\t\t_pixels(height, width, opencvType(), init) { }\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>::Image(uint width, uint height, const Pixel& init)\n\t{\n\t\tcv::Scalar scal(0);\n\t\tfor (int i = 0; i < T_NumComp; i++)\n\t\t\tscal(i) = init(i);\n\n\t\t_pixels = cv::Mat(height, width, opencvType(), scal);\n\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>::Image(Image<T_Type, T_NumComp>&& other) {\n\t\toperator =(std::move(other));\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>& Image<T_Type, T_NumComp>::operator=(Image<T_Type, T_NumComp>&& other) noexcept {\n\t\t_pixels = std::move(other._pixels);\n\t\treturn *this;\n\t}\n\n\ttemplate <typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>& Image<T_Type, T_NumComp>::fill(Pixel const& value) {\n\t\tstd::fill(_pixels.begin<Pixel>(), _pixels.end<Pixel>(), value);\n\t\treturn *this;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tconst void*\t\t\tImage<T_Type, T_NumComp>::data(void) const {\n\t\tSIBR_ASSERT(_pixels.isContinuous() == true); // if not true, you don't want to use this function\n\t\treturn _pixels.ptr();\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid*\t\t\tImage<T_Type, T_NumComp>::data(void) {\n\t\tSIBR_ASSERT(_pixels.isContinuous() == true); // if not true, you don't want to use this function\n\t\treturn _pixels.ptr();\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tcv::Mat\t\t\tImage<T_Type, T_NumComp>::toOpenCVBGR(void) const {\n\t\tcv::Mat out = toOpenCV().clone();\n\t\topencv::convertRGB2BGR(out);\n\t\treturn out;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\t\tImage<T_Type, T_NumComp>::fromOpenCVBGR(const cv::Mat& imgSrc) {\n\t\tcv::Mat img = imgSrc.clone();\n\t\topencv::convertBGR2RGB(img);\n\t\tfromOpenCV(img);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\t\tImage<T_Type, T_NumComp>::fromOpenCV(const cv::Mat& imgSrc) {\n\t\tcv::Mat img = imgSrc.clone();\n\n\t\tif (img.depth() != opencv::imageType<T_Type>())\n\t\t{\n\t\t\timg.convertTo(img, opencv::imageType<T_Type>(),\n\t\t\t\topencv::imageTypeRange<T_Type>() / opencv::imageTypeCVRange(img.depth()));\n\t\t}\n\n\t\tcv::Vec<T_Type, T_NumComp> p;\n\t\tif (img.channels() != T_NumComp)\n\t\t{\n\t\t\t_pixels = cv::Mat(img.rows, img.cols, opencvType());\n\t\t\tfor (int y = 0; y < img.rows; ++y)\n\t\t\t{\n\t\t\t\tfor (int x = 0; x < img.cols; ++x)\n\t\t\t\t{\n\t\t\t\t\tconst T_Type* ptr = img.ptr<T_Type>(y, x);\n\t\t\t\t\tassert(ptr != nullptr);\n\t\t\t\t\tuint i;\n\t\t\t\t\tfor (i = 0; i < (uint)img.channels() && i < T_NumComp; ++i)\n\t\t\t\t\t\tp[i] = ptr[i];\n\t\t\t\t\tfor (; i < T_NumComp && i < 3; ++i)\n\t\t\t\t\t\tp[i] = p[0];\n\t\t\t\t\tfor (; i < T_NumComp && i < 4; ++i)\n\t\t\t\t\t\tp[i] = static_cast<T_Type>(opencv::imageTypeRange<T_Type>());\n\n\t\t\t\t\t_pixels.at<cv::Vec<T_Type, T_NumComp>>(y, x) = p;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\t_pixels = img;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>\t\tImage<T_Type, T_NumComp>::clone(void) const {\n\t\tImage<T_Type, T_NumComp> img;\n\t\timg._pixels = _pixels.clone();\n\t\treturn img;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImagePtr<T_Type, T_NumComp>\t\tImage<T_Type, T_NumComp>::clonePtr(void) const {\n\t\tImagePtr<T_Type, T_NumComp> img(new Image<T_Type, T_NumComp>());\n\t\timg->_pixels = _pixels.clone();\n\t\treturn img;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tbool\t\tImage<T_Type, T_NumComp>::load(const std::string& filename, bool verbose, bool warning_if_not_found) {\n\t\tif (verbose)\n\t\t\tSIBR_LOG << \"Loading image file '\" << filename << \"'.\" << std::endl;\n\t\telse\n\t\t\tstd::cerr << \".\";\n\t\tcv::Mat img = cv::imread(filename, cv::IMREAD_UNCHANGED | cv::IMREAD_ANYDEPTH | cv::IMREAD_ANYCOLOR);\n\t\tif (img.data == nullptr)\n\t\t{\n\t\t\toperator =(Image<T_Type, T_NumComp>()); // reset mat\n\n\t\t\tif (warning_if_not_found) {\n\t\t\t\tSIBR_WRG << \"Image file not found '\" << filename << \"'.\" << std::endl;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t\topencv::convertBGR2RGB(img);\n\t\tfromOpenCV(img);\n\t\treturn true;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tbool\t\tImage<T_Type, T_NumComp>::loadByteStream(const std::string& filename, bool verbose) {\n\t\tif (verbose)\n\t\t\tSIBR_LOG << \"Loading image file '\" << filename << \"'.\" << std::endl;\n\t\telse\n\t\t\tstd::cerr << \".\";\n\n\n\t\tcv::Vec<T_Type, T_NumComp> p;\n\n\t\tsibr::ByteStream bs;\n\t\tif (!bs.load(filename))\n\t\t\tSIBR_WRG << \"Image file not found '\" << filename << \"'.\" << std::endl;\n\n\t\tint wIm;\n\t\tint hIm;\n\t\tbs >> wIm >> hIm;\n\n\t\t_pixels = cv::Mat(hIm, wIm, opencvType());\n\t\tfor (int y = 0; y < hIm; ++y)\n\t\t{\n\t\t\tfor (int x = 0; x < wIm; ++x)\n\t\t\t{\n\t\t\t\tuint i;\n\t\t\t\tfor (i = 0; i < T_NumComp; ++i)\n\t\t\t\t\tbs >> p[i];\n\n\t\t\t\t_pixels.at<cv::Vec<T_Type, T_NumComp>>(y, x) = p;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::save(const std::string& filename, bool verbose) const {\n\t\t{ // Create the output dir if doesn't exists\n\t\t\tboost::filesystem::path outdir = boost::filesystem::path(filename).parent_path();\n\t\t\tif (outdir.empty() == false)\n\t\t\t\tboost::filesystem::create_directories(outdir);\n\t\t}\n\n\t\t// Important Note:\n\t\t// If you have a problem when saving an image (e.g. black image) then\n\t\t// check the targeted image file format manages correctly the T_Type and\n\t\t// T_NumpComp you provide.\n\t\t// OpenCV doesn't seem to check always for such incompatibility (and just\n\t\t// save empty pixels)\n\n\t\tif (verbose)\n\t\t\tSIBR_LOG << \"Saving image file save'\" << filename << \"'.\" << std::endl;\n\n\t\tcv::Mat img;\n\t\tif (T_NumComp == 1) {\n\t\t\tcv::cvtColor(toOpenCVBGR(), img, cv::COLOR_GRAY2BGR);\n\t\t} /// \\todo TODO: support for 2 channels images.\n\t\telse {\n\t\t\t// For 3 and 4 channels, leave the image untouched.\n\t\t\timg = toOpenCVBGR();\n\t\t}\n\n\t\tcv::Mat finalImage;\n\t\tif (T_NumComp == 4) {\n\t\t\tcv::Mat4b imageF_8UC4;\n\t\t\tdouble scale = 255.0 / (double)opencv::imageTypeRange<T_Type>();\n\t\t\timg.convertTo(imageF_8UC4, CV_8UC4, scale);\n\t\t\tfinalImage = imageF_8UC4;\n\t\t}\n\t\telse {\n\t\t\tcv::Mat3b imageF_8UC3;\n\t\t\tdouble scale = 255.0 / (double)opencv::imageTypeRange<T_Type>();\n\t\t\timg.convertTo(imageF_8UC3, CV_8UC3, scale);\n\t\t\tfinalImage = imageF_8UC3;\n\t\t}\n\n\t\tif (img.cols > 0 && img.rows > 0)\n\t\t{\n\t\t\tif (cv::imwrite(filename, finalImage) == false)\n\t\t\t\tSIBR_ERR << \"unknown error while saving image '\" << filename << \"'\"\n\t\t\t\t<< \" (do the targeted file format manages correctly the bpc ?)\" << std::endl;\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"failed to save (image is empty)\" << std::endl;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::saveHDR(const std::string& filename, bool verbose) const {\n\t\t{ // Create the output dir if doesn't exists\n\t\t\tboost::filesystem::path outdir = boost::filesystem::path(filename).parent_path();\n\t\t\tif (outdir.empty() == false)\n\t\t\t\tboost::filesystem::create_directories(outdir);\n\t\t}\n\n\t\tif (verbose)\n\t\t\tSIBR_LOG << \"Saving image file hdr'\" << filename << \"'.\" << std::endl;\n\n\t\tcv::Mat img;\n\t\tif (T_NumComp == 1) {\n\t\t\tcv::cvtColor(toOpenCVBGR(), img, cv::COLOR_GRAY2BGR);\n\t\t} /// \\todo TODO: support for 2 channels images.\n\t\telse {\n\t\t\t// For 3 and 4 channels, leave the image untouched.\n\t\t\timg = toOpenCVBGR();\n\t\t}\n\n\t\tcv::Mat finalImage;\n\t\tif (T_NumComp == 4) {\n\t\t\tcv::Mat4f imageF_32FC4;\n\t\t\tdouble scale = 1.0 / (double)opencv::imageTypeRange<T_Type>();\n\t\t\timg.convertTo(imageF_32FC4, CV_32FC4, scale);\n\t\t\tfinalImage = imageF_32FC4;\n\t\t}\n\t\telse {\n\t\t\tcv::Mat3f imageF_32FC3;\n\t\t\tdouble scale = 1.0 / (double)opencv::imageTypeRange<T_Type>();\n\t\t\timg.convertTo(imageF_32FC3, CV_32FC3, scale);\n\t\t\tfinalImage = imageF_32FC3;\n\t\t}\n\n\t\tif (img.cols > 0 && img.rows > 0)\n\t\t{\n\t\t\tif (cv::imwrite(filename, finalImage) == false)\n\t\t\t\tSIBR_ERR << \"unknown error while saving image '\" << filename << \"'\"\n\t\t\t\t<< \" (do the targeted file format manages correctly the bpc ?)\" << std::endl;\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"failed to save (image is empty)\" << std::endl;\n\t}\n\n\ttemplate <typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::saveByteStream(const std::string& filename, bool verbose) const {\n\t\t{ // Create the output dir if doesn't exists\n\t\t\tboost::filesystem::path outdir = boost::filesystem::path(filename).parent_path();\n\t\t\tif (outdir.empty() == false)\n\t\t\t\tboost::filesystem::create_directories(outdir);\n\t\t}\n\t\tif (verbose)\n\t\t\tSIBR_LOG << \"Saving image file byte'\" << filename << \"'.\" << std::endl;\n\n\t\tsibr::ByteStream bs;\n\n\t\tint wIm = w();\n\t\tint hIm = h();\n\n\t\tif (wIm > 0 && hIm > 0) {\n\t\t\tbs << wIm << hIm;\n\t\t\tfor (int j = 0; j < hIm; j++) {\n\t\t\t\tfor (int i = 0; i < wIm; i++) {\n\t\t\t\t\tfor (int k = 0; k < T_NumComp; k++) {\n\t\t\t\t\t\tbs << _pixels.at<cv::Vec<T_Type, T_NumComp>>(j, i)[k];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbs.saveToFile(filename);\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"failed to save (image is empty)\" << std::endl;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline const typename Image<T_Type, T_NumComp>::Pixel&\t\tImage<T_Type, T_NumComp>::operator()(uint x, uint y) const {\n#ifndef NDEBUG\n\t\tif (!(x < w() && y < h())) {\n\t\t\tstd::cout << \" access (\" << x << \" , \" << y << \") while size is \" << w() << \" x \" << h() << std::endl;\n}\n#endif\n\t\tSIBR_ASSERT(x < w() && y < h());\n\t\treturn _pixels.at<typename Image<T_Type, T_NumComp>::Pixel>(y, x);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline const typename Image<T_Type, T_NumComp>::Pixel & ImagePtr<T_Type, T_NumComp>::operator()(uint x, uint y) const\n\t{\n\t\treturn (*imPtr)(x, y);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline typename Image<T_Type, T_NumComp>::Pixel&\t\tImage<T_Type, T_NumComp>::operator()(uint x, uint y) {\n#ifndef NDEBUG\n\t\tif (!(x < w() && y < h())) {\n\t\t\tstd::cout << \" access (\" << x << \" , \" << y << \") while size is \" << w() << \" x \" << h() << std::endl;\n\t\t}\n#endif\n\t\tSIBR_ASSERT(x < w() && y < h());\n\t\treturn _pixels.at<typename Image<T_Type, T_NumComp>::Pixel>(y, x);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline typename Image<T_Type, T_NumComp>::Pixel & ImagePtr<T_Type, T_NumComp>::operator()(uint x, uint y)\n\t{\n\t\treturn (*imPtr)(x, y);\n\t}\n\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline const typename Image<T_Type, T_NumComp>::Pixel& Image<T_Type, T_NumComp>::operator()(const sibr::Vector2i & xy) const {\n\t\treturn operator()(xy[0], xy[1]);\n\t}\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline const typename Image<T_Type, T_NumComp>::Pixel & ImagePtr<T_Type, T_NumComp>::operator()(const sibr::Vector2i & xy) const\n\t{\n\t\treturn (*imPtr)(xy[0], xy[1]);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline typename Image<T_Type, T_NumComp>::Pixel& Image<T_Type, T_NumComp>::operator()(const sibr::Vector2i & xy) {\n\t\treturn operator()(xy[0], xy[1]);\n\t}\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline typename Image<T_Type, T_NumComp>::Pixel & ImagePtr<T_Type, T_NumComp>::operator()(const sibr::Vector2i & xy)\n\t{\n\t\treturn (*imPtr)(xy[0], xy[1]);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline typename Image<T_Type, T_NumComp>::Pixel& Image<T_Type, T_NumComp>::operator()(const sibr::Vector2f & xy) {\n\t\treturn operator()((int)xy[0], (int)xy[1]);\n\t}\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tinline const typename Image<T_Type, T_NumComp>::Pixel& Image<T_Type, T_NumComp>::operator() (const sibr::Vector2f & xy) const {\n\t\treturn operator()((int)xy[0], (int)xy[1]);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tColorRGBA\tImage<T_Type, T_NumComp>::color(uint x, uint y) const {\n\t\tSIBR_ASSERT(x < w() && y < h());\n\t\tfloat scale = 1.f / opencv::imageTypeRange<T_Type>();\n\t\tcv::Vec<T_Type, T_NumComp> v = _pixels.at<cv::Vec<T_Type, T_NumComp>>(y, x);\n\n\t\treturn ColorRGBA(v.val[0] * scale, v.val[1] * scale, v.val[2] * scale,\n\t\t\t(T_NumComp > 3) ? v.val[3] * scale : 1.f);\n\t}\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::color(uint x, uint y, const ColorRGBA& rgba) {\n\t\tSIBR_ASSERT(x < w() && y < h());\n\t\tfloat scale = opencv::imageTypeRange<T_Type>();\n\t\tcv::Vec<T_Type, T_NumComp> v;//(p.data(), T_NumComp);\n\t\tfor (uint i = 0; i < T_NumComp; ++i) v[i] = T_Type(rgba[i] * scale);\n\t\t_pixels.at<cv::Vec<T_Type, T_NumComp>>(y, x) = v;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>\tImage<T_Type, T_NumComp>::resized(int width, int height, int cv_interpolation_method) const\n\t{\n\t\tif (width == w() && height == h())\n\t\t\treturn clone();\n\t\tImage dst;\n\t\tcv::resize(toOpenCV(), dst._pixels, cv::Size(width, height), 0, 0, cv_interpolation_method);\n\t\treturn dst;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tImage<T_Type, T_NumComp>\t\tImage<T_Type, T_NumComp>::resizedMax(int maxlen) const\n\t{\n\t\tfloat newWidth = (w() >= h()) ? maxlen : maxlen * ((float)w() / (float)h());\n\t\tfloat newHeight = (h() >= w()) ? maxlen : maxlen * ((float)h() / (float)w());\n\n\t\treturn resized((int)newWidth, (int)newHeight);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\ttypename Image<T_Type, T_NumComp>::Pixel Image<T_Type, T_NumComp>::color(const ColorRGBA& rgba) {\n\t\tfloat scale = opencv::imageTypeRange<T_Type>();\n\t\tPixel v;//(p.data(), T_NumComp);\n\t\tfor (uint i = 0; i < T_NumComp; ++i) v[i] = T_Type(rgba[i] * scale);\n\t\treturn v;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tstd::string Image<T_Type, T_NumComp>::pixelStr(const ::sibr::Vector2i & xy)  const {\n\t\tif (isInRange(xy)) {\n\t\t\tstd::stringstream ss;\n//\t\t\tss << \"( \" << operator()(xy).cast<std::conditional<std::is_same_v<T_Type, uchar>, int, T_Type>::type>().transpose() << \" )\";\nstd::cerr << \"PIXEL STR PB\" << std::endl;\nexit(1);\n\t\t\treturn  ss.str();\n\t\t}\n\t\treturn \"\";\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint\t\tImage<T_Type, T_NumComp>::w(void) const {\n\t\treturn _pixels.cols;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint\t\tImage<T_Type, T_NumComp>::h(void) const {\n\t\treturn _pixels.rows;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tsibr::Vector2u\tImage<T_Type, T_NumComp>::size(void) const {\n\t\treturn sibr::Vector2u(w(), h());\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint\t\tImage<T_Type, T_NumComp>::numComp(void) const {\n\t\treturn T_NumComp;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint\t\tImage<T_Type, T_NumComp>::sizeOfComp(void) const {\n\t\treturn sizeof(T_Type)*T_NumComp;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::flipH(void) {\n\t\tcv::flip(_pixels, _pixels, 0 /*!=0 means horizontal*/);\n\t}\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::flipV(void) {\n\t\tcv::flip(_pixels, _pixels, 1 /*!=1 means vertical*/);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid Image<T_Type, T_NumComp>::findMinMax(Pixel& minImage, Pixel& maxImage) {\n\t\tfor (uint c = 0; c < T_NumComp; ++c) {\n\t\t\tminImage[c] = T_Type(opencv::imageTypeRange<Type>());\n\t\t\tmaxImage[c] = T_Type(-opencv::imageTypeRange<Type>());\n\t\t}\n\n\t\tPixel p;\n\t\tfor (uint y = 0; y < h(); ++y) {\n\t\t\tfor (uint x = 0; x < w(); ++x) {\n\t\t\t\tPixel v = operator()(x, y);\n\t\t\t\tfor (uint c = 0; c < T_NumComp; ++c) {\n\t\t\t\t\tminImage[c] = std::min(v[c], minImage[c]);\n\t\t\t\t\tmaxImage[c] = std::max(v[c], maxImage[c]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid\t\tImage<T_Type, T_NumComp>::remap(const Pixel& minVal, const Pixel& maxVal) {\n\t\tPixel minImage;\n\t\tPixel maxImage;\n\t\tfindMinMax(minImage, maxImage);\n\n\t\tPixel p;\n\t\tfor (uint y = 0; y < h(); ++y) {\n\t\t\tfor (uint x = 0; x < w(); ++x) {\n\t\t\t\tPixel v = operator()(x, y);\n\t\t\t\tfor (uint i = 0; i < T_NumComp; ++i)\n\t\t\t\t\tp[i] = minVal[i] + ((maxVal[i] - minVal[i])*(v[i] - minImage[i])) / (maxImage[i] - minImage[i]);\n\t\t\t\toperator()(x, y) = p;\n\t\t\t}\n\t\t}\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tEigen::Matrix<T_Type, T_NumComp, 1, Eigen::DontAlign> Image<T_Type, T_NumComp>::bilinear(const sibr::Vector2f & queryPosition) const\n\t{\n\t\tif (w() < 2 || h() < 2) {\n\t\t\treturn Eigen::Matrix<T_Type, T_NumComp, 1, Eigen::DontAlign>();\n\t\t}\n\n\t\tconst sibr::Vector2i cornerPixel = sibr::Vector2f((queryPosition - 0.5f*sibr::Vector2f(1, 1)).unaryExpr([](float f) { return std::floor(f); })).template cast<int>();\n\n\t\tconst sibr::Vector2f ts = queryPosition - (cornerPixel.cast<float>() + 0.5f*sibr::Vector2f(1, 1));\n\n\t\tconst sibr::Vector2i topLeft(0, 0), bottomRight(w() - 1, h() - 1);\n\n\t\tconst sibr::Vector2i mm = sibr::clamp<int, 2>(cornerPixel + sibr::Vector2i(0, 0), topLeft, bottomRight);\n\t\tconst sibr::Vector2i pm = sibr::clamp<int, 2>(cornerPixel + sibr::Vector2i(1, 0), topLeft, bottomRight);\n\t\tconst sibr::Vector2i mp = sibr::clamp<int, 2>(cornerPixel + sibr::Vector2i(0, 1), topLeft, bottomRight);\n\t\tconst sibr::Vector2i pp = sibr::clamp<int, 2>(cornerPixel + sibr::Vector2i(1, 1), topLeft, bottomRight);\n\t\treturn (\n\t\t\toperator()(mm).template cast<float>() * (1.0f - ts[0]) * (1.0f - ts[1]) +\n\t\t\toperator()(pm).template cast<float>() * ts[0] * (1.0f - ts[1]) +\n\t\t\toperator()(mp).template cast<float>() * (1.0f - ts[0]) * ts[1] +\n\t\t\toperator()(pp).template cast<float>() * ts[0] * ts[1]\n\t\t\t).template cast<T_Type>();\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tEigen::Matrix<float, T_NumComp, 1, Eigen::DontAlign> Image<T_Type, T_NumComp>::monoCubic(float t, const Eigen::Matrix<float, T_NumComp, 4, Eigen::DontAlign> & colors)\n\t{\n\t\tstatic const Eigen::Matrix<float, 4, 4> M = 0.5f* (Eigen::Matrix<float, 4, 4>() <<\n\t\t\t0, 2, 0, 0,\n\t\t\t-1, 0, 1, 0,\n\t\t\t2, -5, 4, -1,\n\t\t\t-1, 3, -3, 1\n\t\t\t).finished().transpose();\n\n\t\treturn colors * (M*Eigen::Matrix<float, 4, 1>(1, t, t*t, t*t*t));\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tEigen::Matrix<T_Type, T_NumComp, 1, Eigen::DontAlign> Image<T_Type, T_NumComp>::bicubic(const sibr::Vector2f & queryPosition) const\n\t{\n\t\tstatic const std::vector<std::vector<sibr::Vector2i> > offsets = {\n\t\t\t{ { -1,-1 },{ 0,-1 } ,{ 1,-1 },{ 2,-1 } },\n\t\t\t{ { -1,0 },{ 0,0 } ,{ 1,0 },{ 2,0 } },\n\t\t\t{ { -1,1 },{ 0,1 } ,{ 1,1 },{ 2,1 } },\n\t\t\t{ { -1,2 },{ 0,2 } ,{ 1,2 },{ 2,2 } }\n\t\t};\n\n\t\ttypedef Eigen::Matrix<float, T_NumComp, 4, Eigen::DontAlign> ColorStack;\n\n\t\tif (w() < 4 || h() < 4) {\n\t\t\treturn Vector<T_Type, T_NumComp>();\n\t\t}\n\n\t\tconst sibr::Vector2i cornerPixel = (queryPosition - 0.5f*sibr::Vector2f(1, 1)).unaryExpr([](float f) { return std::floor(f); }).template cast<int>();\n\t\tconst sibr::Vector2f ts = queryPosition - (cornerPixel.cast<float>() + 0.5f*sibr::Vector2f(1, 1));\n\n\t\tColorStack colorsGrid[4];\n\t\tconst sibr::Vector2i topLeft(0, 0), bottomRight(w() - 1, h() - 1);\n\t\tfor (int i = 0; i < 4; ++i) {\n\t\t\tfor (int j = 0; j < 4; ++j) {\n\t\t\t\tconst sibr::Vector2i pixelPosition = cornerPixel + offsets[i][j];\n\t\t\t\tcolorsGrid[i].col(j) = operator()(sibr::clamp(pixelPosition, topLeft, bottomRight)).template cast<float>();\n\t\t\t}\n\t\t}\n\n\t\tColorStack bs;\n\t\tfor (int i = 0; i < 4; ++i) {\n\t\t\tbs.col(i) = monoCubic(ts[0], colorsGrid[i]);\n\t\t}\n\n\t\tVector<float, T_NumComp> resultFloat = monoCubic(ts[1], bs);\n\t\treturn (resultFloat.unaryExpr([](float f) { return sibr::clamp(f, 0.0f, sibr::opencv::imageTypeRange<T_Type>()); })).template cast<T_Type>();\n\t}\n\n\ttemplate <typename sibr_T, typename openCV_T, int N>\n\tinline Vector<sibr_T, N> fromOpenCV(const cv::Vec<openCV_T, N> & vec) {\n\t\tVector<sibr_T, N> out;\n\t\tfor (int i = 0; i < N; ++i) {\n\t\t\tout[i] = static_cast<sibr_T>(vec[i]);\n\t\t}\n\t\treturn out;\n\t}\n\n\ttemplate <typename openCV_T, typename sibr_T, int N>\n\tinline cv::Vec<openCV_T, N> toOpenCV(const Vector<sibr_T, N> & vec) {\n\t\tcv::Vec<openCV_T, N> out;\n\t\tfor (int i = 0; i < N; ++i) {\n\t\t\tout[i] = static_cast<openCV_T>(vec[i]);\n\t\t}\n\t\treturn out;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Input.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/Input.hpp\"\n\nnamespace sibr\n{\n\n\t/*static*/ Input&\tInput::global( void )\n\t{\n\t\t/// \\todo TODO: add warning if no windows have been created\n\t\tstatic Input\tinstance;\n\t\tinstance._empty = false;\n\t\treturn instance;\n\t}\n\n\t/*static*/ void\t\tInput::poll( void )\n\t{\n\t\tsibr::Input::global().swapStates();\n\t\tglfwPollEvents();\n\t}\n\n\tInput Input::subInput(const sibr::Input & global, const sibr::Viewport & viewport, const bool mouseOutsideDisablesKeyboard)\n\t{\n\t\tInput sub = global;\n\t\tsub._mousePrevPos -= sibr::Vector2i(viewport.finalLeft(), viewport.finalTop());\n\t\tsub._mousePos -= sibr::Vector2i(viewport.finalLeft(), viewport.finalTop());\n\n\t\tif (!global.isInsideViewport(viewport)) {\n\t\t\tsub._mouseButton = MouseButton();\n\t\t\tsub._mouseScroll = 0;\n\n\t\t\tif (mouseOutsideDisablesKeyboard) {\n\t\t\t\tsub._keyboard = Keyboard();\t\t\t\t\n\t\t\t} \n\t\t\treturn sub;\t\t\n\t\t} \n\t\t\n\t\treturn sub;\t\n\t}\n\n\tbool Input::isInsideViewport(const sibr::Viewport & viewport) const\n\t{\n\t\tEigen::AlignedBox2i subBox;\n\t\tsubBox.extend(Vector2i(viewport.finalLeft(), viewport.finalTop()));\n\t\tsubBox.extend(Vector2i(viewport.finalRight(), viewport.finalBottom()));\n\n\t\treturn subBox.contains(mousePosition());\n\t}\n\n\tKeyCombination::KeyCombination() : numKeys(0), isTrue(true) { }\n\tKeyCombination::KeyCombination(int n, bool b) : numKeys(n), isTrue(b) { }\n\n\tKeyCombination::operator bool() const\n\t{\n\t\treturn isTrue && ( numKeys == sibr::Input::global().key().getNumActivated() );\n\t}\t\n\n\tKeyCombination operator&& ( const KeyCombination & combA, const KeyCombination & combB)\n\t{\n\t\treturn KeyCombination(combA.numKeys + combB.numKeys, combA.isTrue && combB.isTrue); \n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Input.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <array>\r\n\r\n//#define GLEW_STATIC\r\n#include <GL/glew.h>\r\n# define GLFW_INCLUDE_GLU\r\n# include <GLFW/glfw3.h>\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n# include \"core/graphics/Viewport.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\tnamespace Key\r\n\t{\r\n\t\t/** Key codes (based on GLFW codes). */\r\n\t\tenum Code\r\n\t\t{\r\n\t\t\tUnknown = 0 /*GLFW_KEY_UNKNOWN*/,   \r\n\t\t\tSpace = GLFW_KEY_SPACE,   \r\n\t\t\tApostrophe = GLFW_KEY_APOSTROPHE,   \r\n\t\t\tComma = GLFW_KEY_COMMA,   \r\n\t\t\tMinus = GLFW_KEY_MINUS,   \r\n\t\t\tPeriod = GLFW_KEY_PERIOD,   \r\n\t\t\tSlash = GLFW_KEY_SLASH,   \r\n\t\t\tNum0 = GLFW_KEY_0,   \r\n\t\t\tNum1 = GLFW_KEY_1,   \r\n\t\t\tNum2 = GLFW_KEY_2,   \r\n\t\t\tNum3 = GLFW_KEY_3,   \r\n\t\t\tNum4 = GLFW_KEY_4,   \r\n\t\t\tNum5 = GLFW_KEY_5,   \r\n\t\t\tNum6 = GLFW_KEY_6,   \r\n\t\t\tNum7 = GLFW_KEY_7,   \r\n\t\t\tNum8 = GLFW_KEY_8,   \r\n\t\t\tNum9 = GLFW_KEY_9,   \r\n\t\t\tSemicolon = GLFW_KEY_SEMICOLON,   \r\n\t\t\tEqual = GLFW_KEY_EQUAL,   \r\n\t\t\tA = GLFW_KEY_A,   \r\n\t\t\tB = GLFW_KEY_B,   \r\n\t\t\tC = GLFW_KEY_C,   \r\n\t\t\tD = GLFW_KEY_D,   \r\n\t\t\tE = GLFW_KEY_E,   \r\n\t\t\tF = GLFW_KEY_F,   \r\n\t\t\tG = GLFW_KEY_G,   \r\n\t\t\tH = GLFW_KEY_H,   \r\n\t\t\tI = GLFW_KEY_I,   \r\n\t\t\tJ = GLFW_KEY_J,   \r\n\t\t\tK = GLFW_KEY_K,   \r\n\t\t\tL = GLFW_KEY_L,   \r\n\t\t\tM = GLFW_KEY_M,   \r\n\t\t\tN = GLFW_KEY_N,   \r\n\t\t\tO = GLFW_KEY_O,   \r\n\t\t\tP = GLFW_KEY_P,   \r\n\t\t\tQ = GLFW_KEY_Q,   \r\n\t\t\tR = GLFW_KEY_R,   \r\n\t\t\tS = GLFW_KEY_S,   \r\n\t\t\tT = GLFW_KEY_T,   \r\n\t\t\tU = GLFW_KEY_U,   \r\n\t\t\tV = GLFW_KEY_V,   \r\n\t\t\tW = GLFW_KEY_W,   \r\n\t\t\tX = GLFW_KEY_X,   \r\n\t\t\tY = GLFW_KEY_Y,   \r\n\t\t\tZ = GLFW_KEY_Z,   \r\n\t\t\tLeftBracket = GLFW_KEY_LEFT_BRACKET,   \r\n\t\t\tBackslash = GLFW_KEY_BACKSLASH,   \r\n\t\t\tRightBracket = GLFW_KEY_RIGHT_BRACKET,   \r\n\t\t\tGraveAccent = GLFW_KEY_GRAVE_ACCENT,   \r\n\t\t\tWorld1 = GLFW_KEY_WORLD_1,   \r\n\t\t\tWorld2 = GLFW_KEY_WORLD_2,   \r\n\t\t\tEscape = GLFW_KEY_ESCAPE,   \r\n\t\t\tEnter = GLFW_KEY_ENTER,   \r\n\t\t\tTab = GLFW_KEY_TAB,   \r\n\t\t\tBackspace = GLFW_KEY_BACKSPACE,   \r\n\t\t\tInsert = GLFW_KEY_INSERT,   \r\n\t\t\tDelete = GLFW_KEY_DELETE,   \r\n\t\t\tRight = GLFW_KEY_RIGHT,   \r\n\t\t\tLeft = GLFW_KEY_LEFT,   \r\n\t\t\tDown = GLFW_KEY_DOWN,   \r\n\t\t\tUp = GLFW_KEY_UP,   \r\n\t\t\tPage_up = GLFW_KEY_PAGE_UP,   \r\n\t\t\tPage_down = GLFW_KEY_PAGE_DOWN,   \r\n\t\t\tHome = GLFW_KEY_HOME,   \r\n\t\t\tEnd = GLFW_KEY_END,   \r\n\t\t\tCapsLock = GLFW_KEY_CAPS_LOCK,   \r\n\t\t\tScrollLock = GLFW_KEY_SCROLL_LOCK,   \r\n\t\t\tNumLock = GLFW_KEY_NUM_LOCK,   \r\n\t\t\tPrintScreen = GLFW_KEY_PRINT_SCREEN,   \r\n\t\t\tPause = GLFW_KEY_PAUSE,   \r\n\t\t\tF1 = GLFW_KEY_F1,   \r\n\t\t\tF2 = GLFW_KEY_F2,   \r\n\t\t\tF3 = GLFW_KEY_F3,   \r\n\t\t\tF4 = GLFW_KEY_F4,   \r\n\t\t\tF5 = GLFW_KEY_F5,   \r\n\t\t\tF6 = GLFW_KEY_F6,   \r\n\t\t\tF7 = GLFW_KEY_F7,   \r\n\t\t\tF8 = GLFW_KEY_F8,   \r\n\t\t\tF9 = GLFW_KEY_F9,   \r\n\t\t\tF10 = GLFW_KEY_F10,   \r\n\t\t\tF11 = GLFW_KEY_F11,   \r\n\t\t\tF12 = GLFW_KEY_F12,   \r\n\t\t\tF13 = GLFW_KEY_F13,   \r\n\t\t\tF14 = GLFW_KEY_F14,   \r\n\t\t\tF15 = GLFW_KEY_F15,   \r\n\t\t\tF16 = GLFW_KEY_F16,   \r\n\t\t\tF17 = GLFW_KEY_F17,   \r\n\t\t\tF18 = GLFW_KEY_F18,   \r\n\t\t\tF19 = GLFW_KEY_F19,   \r\n\t\t\tF20 = GLFW_KEY_F20,   \r\n\t\t\tF21 = GLFW_KEY_F21,   \r\n\t\t\tF22 = GLFW_KEY_F22,   \r\n\t\t\tF23 = GLFW_KEY_F23,   \r\n\t\t\tF24 = GLFW_KEY_F24,   \r\n\t\t\tF25 = GLFW_KEY_F25,   \r\n\t\t\tKPNum0 = GLFW_KEY_KP_0,   \r\n\t\t\tKPNum1 = GLFW_KEY_KP_1,   \r\n\t\t\tKPNum2 = GLFW_KEY_KP_2,   \r\n\t\t\tKPNum3 = GLFW_KEY_KP_3,   \r\n\t\t\tKPNum4 = GLFW_KEY_KP_4,   \r\n\t\t\tKPNum5 = GLFW_KEY_KP_5,   \r\n\t\t\tKPNum6 = GLFW_KEY_KP_6,   \r\n\t\t\tKPNum7 = GLFW_KEY_KP_7,   \r\n\t\t\tKPNum8 = GLFW_KEY_KP_8,   \r\n\t\t\tKPNum9 = GLFW_KEY_KP_9,   \r\n\t\t\tKPDecimal = GLFW_KEY_KP_DECIMAL,   \r\n\t\t\tKPDivide = GLFW_KEY_KP_DIVIDE,   \r\n\t\t\tKPMultiply = GLFW_KEY_KP_MULTIPLY,   \r\n\t\t\tKPSubtract = GLFW_KEY_KP_SUBTRACT,   \r\n\t\t\tKPAdd = GLFW_KEY_KP_ADD,   \r\n\t\t\tKPEnter = GLFW_KEY_KP_ENTER,   \r\n\t\t\tKPEqual = GLFW_KEY_KP_EQUAL,   \r\n\t\t\tLeftShift = GLFW_KEY_LEFT_SHIFT,   \r\n\t\t\tLeftControl = GLFW_KEY_LEFT_CONTROL,   \r\n\t\t\tLeftAlt = GLFW_KEY_LEFT_ALT,   \r\n\t\t\tLeftSuper = GLFW_KEY_LEFT_SUPER,   \r\n\t\t\tRightShift = GLFW_KEY_RIGHT_SHIFT,   \r\n\t\t\tRightControl = GLFW_KEY_RIGHT_CONTROL,   \r\n\t\t\tRightAlt = GLFW_KEY_RIGHT_ALT,   \r\n\t\t\tRightSuper = GLFW_KEY_RIGHT_SUPER,   \r\n\t\t\tMenu = GLFW_KEY_MENU,  \r\n\r\n\t\t\tcount // this one is a 'tricks' to automatically get the number\r\n\t\t\t// of elements in this enum (just type sibr::Key::count).\r\n\t\t};\r\n\t} // namespace Key\r\n\r\n\tnamespace Mouse\r\n\t{\r\n\t\t/** Mouse button codes (based on GLFW codes). */\r\n\t\tenum Code\r\n\t\t{\r\n\t\t\tButton1 = GLFW_MOUSE_BUTTON_1, \r\n\t\t\tButton2 = GLFW_MOUSE_BUTTON_2,\r\n\t\t\tButton3 = GLFW_MOUSE_BUTTON_3,\r\n\t\t\tButton4 = GLFW_MOUSE_BUTTON_4,\r\n\t\t\tButton5 = GLFW_MOUSE_BUTTON_5,\r\n\t\t\tButton6 = GLFW_MOUSE_BUTTON_6,\r\n\t\t\tButton7 = GLFW_MOUSE_BUTTON_7,\r\n\t\t\tButton8 = GLFW_MOUSE_BUTTON_8,\r\n\t\t\tLast = GLFW_MOUSE_BUTTON_LAST,\r\n\r\n\t\t\tLeft = GLFW_MOUSE_BUTTON_LEFT,\r\n\t\t\tMiddle = GLFW_MOUSE_BUTTON_MIDDLE,\r\n\t\t\tRight = GLFW_MOUSE_BUTTON_RIGHT,\r\n\r\n\t\t\tUnknown,\r\n\t\t\tcount\r\n\t\t};\r\n\t} // namespace Mouse\r\n\r\n\t/** Helper keeping track of the number of keys currently pressed. */\r\n\tstruct SIBR_GRAPHICS_EXPORT KeyCombination \r\n\t{\r\n\t\t/// Default constructor.\r\n\t\tKeyCombination();\r\n\r\n\t\t/** Constructor.\r\n\t\t\\param n number of keys pressed\r\n\t\t\\param b are they active or not\r\n\t\t*/\r\n\t\tKeyCombination(int n, bool b); \r\n\t\t\r\n\t\t/** \\return true if they are numKeys pressed keys and their combination is active. */\r\n\t\toperator bool() const; \r\n\r\n\t\tint numKeys; ///< Number of pressed keys.\r\n\t\tbool isTrue; ///< Activations status.\r\n\t};\r\n\t\r\n\t/** Merge two set of pressed keys.\r\n\t\\param combA  first set\r\n\t\\param combB second set\r\n\t\\return the union set\r\n\t*/\r\n\tKeyCombination SIBR_GRAPHICS_EXPORT operator&&( const KeyCombination & combA, const KeyCombination & combB);\r\n\r\n\t/** Keep track of the pressed/active/released state of a set of keys/buttons.\r\n\t\\sa Key::Code, Mouse::Code\r\n\t*/\r\n\ttemplate <int TNbState, typename TEnum>\r\n\tclass InputState\r\n\t{\r\n\tpublic:\r\n\r\n\t\t/** Is an item currently active.\r\n\t\t\\param code the item code (key or mouse)\r\n\t\t\\return true if the item is active at this frame\r\n\t\t*/\r\n\t\tbool\tisActivated( TEnum code ) const {\r\n\t\t\treturn _currentStates[(size_t)code];\r\n\t\t}\r\n\r\n\t\t/** Is an item released (lasts one frame).\r\n\t\t\\param code the item code (key or mouse)\r\n\t\t\\return true if the item is released at this frame\r\n\t\t*/\r\n\t\tbool\tisReleased( TEnum code ) const {\r\n\t\t\treturn _lastStates[(size_t)code] \\\r\n\t\t\t\t&& !_currentStates[(size_t)code];\r\n\t\t}\r\n\r\n\t\t/** Is an item pressed at this frame (lasts one frame).\r\n\t\t\\sa isActivated\r\n\t\t\\param code the item code (key or mouse)\r\n\t\t\\return true if the item is pressed at this frame\r\n\t\t*/\r\n\t\tbool\tisPressed( TEnum code ) const {\r\n\t\t\treturn !_lastStates[(size_t)code] \\\r\n\t\t\t\t&& _currentStates[(size_t)code];\r\n\t\t}\r\n\t\t\r\n\t\t/** Is an item currently pressed and only this one (lasts one frame).\r\n\t\t\\sa isActivated\r\n\t\t\\param code the item code (key or mouse)\r\n\t\t\\return true if the item is the only one pressed\r\n\t\t*/\r\n\t\tKeyCombination isPressedOnly( TEnum code ) const {\r\n\t\t\treturn KeyCombination(1,isPressed(code));\r\n\t\t}\r\n\r\n\t\t/** Is an item currently active and only this one.\r\n\t\t\\param code the item code (key or mouse)\r\n\t\t\\return true if the item is the only one active\r\n\t\t*/\r\n\t\tKeyCombination isActivatedOnly( TEnum code ) const {\r\n\t\t\treturn KeyCombination(1,isActivated(code));\r\n\t\t}\r\n\r\n\t\t/** Declare an item as pressed at this frame.\r\n\t\t\\param code the item code (Key or Mouse).\r\n\t\t*/\r\n\t\tvoid\tpress( TEnum code ) {\r\n\t\t\t_currentStates[(size_t)code] = true;\r\n\t\t}\r\n\r\n\t\t/** Declare an item as released at this frame.\r\n\t\t\\param code the item code (Key or Mouse).\r\n\t\t*/\r\n\t\tvoid\trelease( TEnum code ) {\r\n\t\t\t_currentStates[(size_t)code] = false;\r\n\t\t\t_lastStates[(size_t)code] = true;\r\n\t\t}\r\n\r\n\t\t/** Mute an item.\r\n\t\t\\param code the item code (Key or Mouse).\r\n\t\t*/\r\n\t\tvoid\tsilent( TEnum code ) {\r\n\t\t\t_currentStates[(size_t)code] = \\\r\n\t\t\t\t_lastStates[(size_t)code] = false;\r\n\t\t}\r\n\r\n\t\t/** Reset all items state.\r\n\t\t*/\r\n\t\tvoid\tclearStates( void ) {\r\n\t\t\tstd::fill(_currentStates.begin(), _currentStates.end(), false);\r\n\t\t\tstd::fill(_lastStates.begin(), _lastStates.end(), false);\r\n\t\t}\r\n\r\n\t\t/** Update previous frame states with the current frame ones. */\r\n\t\tvoid\tswapStates( void ) {\r\n\t\t\t_lastStates = _currentStates;\r\n\t\t}\r\n\r\n\t\t/** \\return the number of keys currently activated. */\r\n\t\tint getNumActivated( void ) const {\r\n\t\t\tint n=0;\r\n\t\t\tfor(int i=0; i<TNbState; ++i){\r\n\t\t\t\tn += (int)_currentStates[i];\r\n\t\t\t}\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\tprivate:\r\n\t\tstd::array<bool, TNbState>\t\t\t_currentStates; ///< Current frame state.\r\n\t\tstd::array<bool, TNbState>\t\t\t_lastStates; ///< Last frame state.\r\n\t};\r\n\r\n\t/** Maintain the complete state of user interactions (mouse, keyboard) for a given view or window.\r\n\tAll coordinates are recaled with respect to the associated view.\r\n\tTo check if the B key is currently held:\r\n\t\tinput.key().isActivated(Key::B);\r\n\tTo check if the right mouse was just released:\r\n\t\tinput.mouseButton().isReleased(Mouse::Right);\r\n\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tclass SIBR_GRAPHICS_EXPORT Input\r\n\t{\r\n\tpublic:\r\n\t\ttypedef InputState<Key::count, Key::Code>\t\tKeyboard;\r\n\t\ttypedef InputState<Mouse::count, Mouse::Code>\tMouseButton;\r\n\r\n\tpublic:\r\n\r\n\t\t/** \\return the global system input (all others are derived from this one) */\r\n\t\tstatic Input&\tglobal( void );\r\n\r\n\t\t/** Generate a new Input object based on a parent one and a viewport. Events (clicks) happening outside \r\n\t\t\tthe viewport will be ignored, mouse coordinates will be recentered with respect to the viewport.\r\n\t\t\t\\param global the parent input\r\n\t\t\t\\param viewport the viewport to retrict the input to\r\n\t\t\t\\param mouseOutsideDisablesKeyboard if set to true, keyboard inputs are ignored when the mouse is outside the viewport\r\n\t\t\t\\return the new restricted input\r\n\t\t*/\r\n\t\tstatic Input subInput(const sibr::Input & global, const sibr::Viewport & viewport, const bool mouseOutsideDisablesKeyboard = true);\r\n\r\n\t\t/** Is the mouse inside a given viewport.\r\n\t\t\\param viewport the viewport to test against\r\n\t\t\\return true if the mouse is inside.\r\n\t\t*/\r\n\t\tbool isInsideViewport(const sibr::Viewport & viewport) const;\r\n\r\n\t\t/** Update internal state based on GLFW, call once per frame. */\r\n\t\tstatic void\t\tpoll( void );\r\n\r\n\t\t/** \\return the keyboard state. */\r\n\t\tconst Keyboard&\tkey( void ) const {\r\n\t\t\treturn _keyboard;\r\n\t\t}\r\n\r\n\t\t/** \\return the keyboard state. */\r\n\t\tKeyboard&\tkey( void ) {\r\n\t\t\treturn _keyboard;\r\n\t\t}\r\n\r\n\t\t/** \\return the mouse buttons state. */\r\n\t\tconst MouseButton&\tmouseButton( void ) const {\r\n\t\t\treturn _mouseButton;\r\n\t\t}\r\n\r\n\t\t/** \\return the mouse buttons state. */\r\n\t\tMouseButton&\tmouseButton( void ) {\r\n\t\t\treturn _mouseButton;\r\n\t\t}\r\n\r\n\t\t/** \\return the current mouse position */\r\n\t\tconst Vector2i&\tmousePosition( void ) const {\r\n\t\t\treturn _mousePos;\r\n\t\t}\r\n\r\n\t\t/** Set the current mouse position.\r\n\t\t\\param mousePos the new position\r\n\t\t*/\r\n\t\tvoid mousePosition( Vector2i mousePos ) {\r\n\t\t\t_mousePos = mousePos;\r\n\t\t}\r\n\r\n\t\t/** \\return the change in mouse position since last frame. */\r\n\t\tVector2i mouseDeltaPosition( void ) const {\r\n\t\t\treturn _mousePrevPos-_mousePos;\r\n\t\t}\r\n\t\t\r\n\t\t/** If any number key is pressed, return the lowest one.\r\n\t\t\\return the smallest pressed number, or -1 if none is pressed.\r\n\t\t*/\r\n\t\tint pressedNumber() const {\r\n\t\t\tstatic const std::vector<sibr::Key::Code> keys = {\r\n\t\t\t\tKey::Num0, Key::Num1, Key::Num2, Key::Num3, Key::Num4,\r\n\t\t\t\tKey::Num5, Key::Num6, Key::Num7, Key::Num8, Key::Num9\r\n\t\t\t};\r\n\t\t\r\n\t\t\tfor (int i = 0; i < 10; ++i){\r\n\t\t\t\tif (_keyboard.isPressed(keys[i]) ){\r\n\t\t\t\t\treturn i;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn -1;\r\n\t\t}\r\n\r\n\t\t/** Update last frame state with the current one. Call at the end of each frame. */\r\n\t\tvoid swapStates( void ) {\r\n\t\t\tkey().swapStates();\r\n\t\t\tmouseButton().swapStates();\r\n\t\t\t_mousePrevPos = _mousePos;\r\n\t\t\t_mouseScroll = 0.0;\r\n\t\t}\r\n\r\n\t\t/** \\return the scroll amount along the vertical axis. */\r\n\t\tdouble\t\t\tmouseScroll( void ) const {\r\n\t\t\treturn _mouseScroll;\r\n\t\t}\r\n\r\n\t\t/** Set the scroll amount.\r\n\t\t\\param  v the scroll amount.\r\n\t\t*/\r\n\t\tvoid\t\t\tmouseScroll(double v) {\r\n\t\t\t_mouseScroll = v;\r\n\t\t}\r\n\r\n\t\t/** \\return true if the input is associated to an empty view/window. */\r\n\t\tbool empty() const {\r\n\t\t\treturn _empty;\r\n\t\t}\r\n\r\n\tprivate:\r\n\r\n\t\tKeyboard\t\t\t_keyboard; ///< Keyboard state.\r\n\t\tMouseButton\t\t\t_mouseButton; ///< Mouse state.\r\n\r\n\t\tVector2i\t\t\t_mousePos = {0, 0}; ///< Current mouse  position.\r\n\t\tVector2i\t\t\t_mousePrevPos = { 0, 0 }; ///< Previous mouse position.\r\n\t\tdouble\t\t\t\t_mouseScroll = 0.0; ///< Current scroll amount.\r\n\t\tbool\t\t\t\t_empty = true; ///< Is the input associated to an empty view/window.\r\n\r\n\t};\r\n\r\n\t///// DEFINITIONS /////\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/MaterialMesh.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include <fstream>\r\n#include <memory>\r\n#include <map>\r\n#include <queue>\r\n\r\n#include <assimp/Importer.hpp> // C++ importer interface\r\n#include <assimp/scene.h> // Output data structure\r\n#include <assimp/postprocess.h> // Post processing flags\r\n#include <assimp/cexport.h>\r\n#include <assimp/Exporter.hpp>\r\n\r\n#include \"core/system/ByteStream.hpp\"\r\n#include \"core/graphics/MaterialMesh.hpp\"\r\n#include \"core/system/Transform3.hpp\"\r\n#include \"boost/filesystem.hpp\"\r\n#include \"core/system/XMLTree.h\"\r\n#include \"core/system/Matrix.hpp\"\r\n#include <set>\r\n#include <boost/variant/detail/substitute.hpp>\r\n\r\nnamespace sibr\r\n{\r\n\tbool\tMaterialMesh::load(const std::string& filename)\r\n\t{\r\n\r\n\t\tsrand(static_cast <unsigned> (time(0)));\r\n\t\tAssimp::Importer\timporter;\r\n\t\timporter.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true);\r\n\t\t// cause Assimp to remove all degenerated faces as soon as they are detected\r\n\t\tconst aiScene* scene = importer.ReadFile(filename,\r\n\t\t\taiProcess_Triangulate | aiProcess_JoinIdenticalVertices |\r\n\t\t\taiProcess_FindDegenerates);\r\n\r\n\t\tif (!scene)\r\n\t\t{\r\n\t\t\tSIBR_WRG << \"error: can't load mesh '\" << filename\r\n\t\t\t\t<< \"' (\" << importer.GetErrorString() << \").\" << std::endl;\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif (scene->mNumMeshes == 0)\r\n\t\t{\r\n\t\t\tSIBR_WRG << \"error: the loaded model file ('\" << filename\r\n\t\t\t\t<< \"') contains zero or more than one mesh. Number of meshes : \" <<\r\n\t\t\t\tscene->mNumMeshes << std::endl;\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tauto convertVec = [](const aiVector3D& v) {\r\n\t\t\treturn Vector3f(v.x, v.y, v.z); };\r\n\t\t_triangles.clear();\r\n\r\n\t\tuint offsetVertices = 0;\r\n\t\tuint offsetFaces = 0;\r\n\t\tuint matId = 0; // Material\r\n\t\tstd::map<std::string, int> matName2Id; // Material\r\n\r\n\t\t_maxMeshId = size_t(int(scene->mNumMeshes) - 1);\r\n\r\n\t\tSIBR_LOG << \"Mesh with \" << scene->mNumMeshes << \" elements.\" << std::endl;\r\n\t\tfor (uint meshId = 0; meshId < scene->mNumMeshes; ++meshId) {\r\n\t\t\tconst aiMesh* mesh = scene->mMeshes[meshId];\r\n\r\n\t\t\t_vertices.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\t_meshIds.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i) {\r\n\t\t\t\t_vertices[offsetVertices + i] = convertVec(mesh->mVertices[i]);\r\n\t\t\t\t_meshIds[offsetVertices + i] = meshId;\r\n\t\t\t}\r\n\r\n\t\t\tif (mesh->HasVertexColors(0) && mesh->mColors[0])\r\n\t\t\t{\r\n\t\t\t\t_colors.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i)\r\n\t\t\t\t{\r\n\t\t\t\t\t_colors[offsetVertices + i] = Vector3f(\r\n\t\t\t\t\t\tmesh->mColors[0][i].r,\r\n\t\t\t\t\t\tmesh->mColors[0][i].g,\r\n\t\t\t\t\t\tmesh->mColors[0][i].b);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (mesh->HasVertexColors(0) && mesh->mColors[0])\r\n\t\t\t{\r\n\t\t\t\t_colors.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i)\r\n\t\t\t\t{\r\n\t\t\t\t\t_colors[offsetVertices + i] = Vector3f(\r\n\t\t\t\t\t\tmesh->mColors[0][i].r,\r\n\t\t\t\t\t\tmesh->mColors[0][i].g,\r\n\t\t\t\t\t\tmesh->mColors[0][i].b);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (mesh->HasNormals())\r\n\t\t\t{\r\n\t\t\t\t_normals.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i) {\r\n\t\t\t\t\t_normals[offsetVertices + i] = convertVec(mesh->mNormals[i]);\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tbool randomUV = true;\r\n\r\n\t\t\tif (mesh->HasTextureCoords(0))\r\n\t\t\t{\r\n\t\t\t\t_texcoords.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i) {\r\n\t\t\t\t\t_texcoords[offsetVertices + i] =\r\n\t\t\t\t\t\tconvertVec(mesh->mTextureCoords[0][i]).xy();\r\n\t\t\t\t\tif (convertVec(mesh->mTextureCoords[0][i]).xy().x() != 0.f ||\r\n\t\t\t\t\t\tconvertVec(mesh->mTextureCoords[0][i]).xy().y() != 0.f)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\trandomUV = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tif (randomUV) {\r\n\t\t\t\tSIBR_LOG << \"using random UVs.\" << std::endl;\r\n\t\t\t\t_texcoords.resize(offsetVertices + mesh->mNumVertices);\r\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i) {\r\n\t\t\t\t\tfloat u = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\t\t\t\t\tfloat v = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\t\t\t\t\t_texcoords[offsetVertices + i] =\r\n\t\t\t\t\t\tVector2f(u * 5.f, v * 5.f);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\r\n\t\t\tif (meshId == 0) {\r\n\t\t\t\tSIBR_LOG << \"Mesh contains: colors: \" << mesh->HasVertexColors(0)\r\n\t\t\t\t\t<< \", normals: \" << mesh->HasNormals()\r\n\t\t\t\t\t<< \", texcoords: \" << mesh->HasTextureCoords(0) << std::endl;\r\n\t\t\t}\r\n\r\n\t\t\t//----------------------------- FULL MATERIAL ------------------------\r\n\t\t\tuint currentMatId = matId;\r\n\t\t\taiString aiMatName;\r\n\r\n\t\t\tif (AI_SUCCESS != scene->mMaterials[mesh->mMaterialIndex]->\r\n\t\t\t\tGet(AI_MATKEY_NAME, aiMatName)) {\r\n\t\t\t\tSIBR_LOG << \"material not found \" << mesh->mMaterialIndex\r\n\t\t\t\t\t<< std::endl;\r\n\t\t\t}\r\n\r\n\t\t\tstd::string matName = aiMatName.C_Str();\r\n\r\n\t\t\tif (matName2Id.find(matName) == matName2Id.end()) {\r\n\r\n\t\t\t\tmatName2Id[matName] = matId;\r\n\t\t\t\t_matId2Name.push_back(matName);\r\n\r\n\t\t\t\tmatId++;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tcurrentMatId = matName2Id[matName];\r\n\t\t\t}\r\n\r\n\t\t\t//-------------------------- END FULL MATERIAL ---------------------\r\n\r\n\t\t\t_triangles.reserve(offsetFaces + mesh->mNumFaces);\r\n\t\t\t_matIds.reserve(offsetFaces + mesh->mNumFaces); //material\r\n\t\t\t_matIdsVertices.resize(_vertices.size());\r\n\t\t\tfor (uint i = 0; i < mesh->mNumFaces; ++i)\r\n\t\t\t{\r\n\t\t\t\tconst aiFace* f = &mesh->mFaces[i];\r\n\t\t\t\tif (f->mNumIndices != 3)\r\n\t\t\t\t\tSIBR_WRG << \"Discarding a face (not a triangle, num indices: \"\r\n\t\t\t\t\t<< f->mNumIndices << \")\" << std::endl;\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tVector3u tri = Vector3u(offsetVertices + f->mIndices[0], offsetVertices + f->mIndices[1], offsetVertices + f->mIndices[2]);\r\n\t\t\t\t\tif (tri[0] < 0 || tri[0] >= _vertices.size()\r\n\t\t\t\t\t\t|| tri[1] < 0 || tri[1] >= _vertices.size()\r\n\t\t\t\t\t\t|| tri[2] < 0 || tri[2] >= _vertices.size())\r\n\t\t\t\t\t\tSIBR_WRG << \"Face num [\" << i << \"] contains invalid vertex id(s)\" << std::endl;\r\n\t\t\t\t\telse {\r\n\t\t\t\t\t\t_triangles.push_back(tri);\r\n\t\t\t\t\t\t_matIds.push_back(currentMatId);  //material\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\toffsetFaces = (uint)_triangles.size();\r\n\t\t\toffsetVertices = (uint)_vertices.size();\r\n\r\n\t\t}\r\n\r\n\t\tSIBR_LOG << \"Mesh '\" << filename << \" successfully loaded. \" << scene->mNumMeshes << \" meshes were loaded with a total of \"\r\n\t\t\t<< \" (\" << _triangles.size() << \") faces and \"\r\n\t\t\t<< \" (\" << _vertices.size() << \") vertices detected.\" << std::endl;\r\n\t\tSIBR_LOG << \"Init material part complete.\" << std::endl;\r\n\r\n\t\t_gl.dirtyBufferGL = true;\r\n\t\treturn true;\r\n\t}\r\n\r\n\tsibr::Matrix4f parseTransform(const rapidxml::xml_node<>* nodeTrans)\r\n\t{\r\n\t\tsibr::Matrix4f objectToWorld = sibr::Matrix4f::Identity();\r\n\t\tif (nodeTrans)\r\n\t\t{\r\n\t\t\t// Helper functions\r\n\t\t\tconst auto getfValue = [&](rapidxml::xml_node<>* n, std::string const& name, float& value) {\r\n\t\t\t\tif (n && n->first_attribute(name.c_str())) {\r\n\t\t\t\t\tvalue = std::stof(n->first_attribute(name.c_str())->value());\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\tconst auto getVector = [&](rapidxml::xml_node<>* n, std::string const& name, sibr::Vector3f& vec) {\r\n\t\t\t\tconst auto isNonFloat = [](const char c) {\r\n\t\t\t\t\treturn c == ',' || c == ' ' || c == '\\t' || c == '\\r' || c == '\\n' || c == '\"' || c == 0;\r\n\t\t\t\t};\r\n\t\t\t\tconst auto eatNonFloat = [&](const char* str, int& i) {\r\n\t\t\t\t\twhile (isNonFloat(str[i])) ++i;\r\n\t\t\t\t};\r\n\t\t\t\tconst auto eatFloat = [&](const char* str, int& i) {\r\n\t\t\t\t\twhile (!isNonFloat(str[i])) ++i;\r\n\t\t\t\t};\r\n\t\t\t\tif (n && n->first_attribute(name.c_str())) {\r\n\t\t\t\t\tint index = 0;\r\n\t\t\t\t\tconst char* value = n->first_attribute(name.c_str())->value();\r\n\t\t\t\t\tfor (int i = 0; i < vec.size(); ++i) {\r\n\t\t\t\t\t\teatNonFloat(value, index);\r\n\t\t\t\t\t\tvec[i] = std::atof(value + index);\r\n\t\t\t\t\t\teatFloat(value, index);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\tconst auto getAxesValues = [&](rapidxml::xml_node<>* n, float& x, float& y, float& z) {\r\n\t\t\t\tgetfValue(n, \"x\", x);\r\n\t\t\t\tgetfValue(n, \"y\", y);\r\n\t\t\t\tgetfValue(n, \"z\", z);\r\n\t\t\t};\r\n\r\n\t\t\t// Loop through all the transform commands,\r\n\t\t\t\t\t\t\t// and \"accumulate\" the transform matrices into \"objectToWorld\"\r\n\t\t\tfor (rapidxml::xml_node<>* node = nodeTrans->first_node(); node; node = node->next_sibling()) {\r\n\t\t\t\tsibr::Matrix4f nodeMatrix = sibr::Matrix4f::Identity();\r\n\t\t\t\tconst char* transformType = node->name();\r\n\t\t\t\tif (strcmp(transformType, \"matrix\") == 0) {\r\n\t\t\t\t\tstd::string matrixValue = node->first_attribute(\"value\")->value();\r\n\t\t\t\t\tstd::istringstream issMatrix(matrixValue);\r\n\t\t\t\t\tstd::vector<std::string> split(\r\n\t\t\t\t\t\tstd::istream_iterator<std::string>{issMatrix},\r\n\t\t\t\t\t\tstd::istream_iterator<std::string>());\r\n\t\t\t\t\tnodeMatrix <<\r\n\t\t\t\t\t\tstd::stof(split[0]), std::stof(split[1]), std::stof(split[2]), std::stof(split[3]),\r\n\t\t\t\t\t\tstd::stof(split[4]), std::stof(split[5]), std::stof(split[6]), std::stof(split[7]),\r\n\t\t\t\t\t\tstd::stof(split[8]), std::stof(split[9]), std::stof(split[10]), std::stof(split[11]),\r\n\t\t\t\t\t\tstd::stof(split[12]), std::stof(split[13]), std::stof(split[14]), std::stof(split[15]);\r\n\t\t\t\t}\r\n\t\t\t\telse if (strcmp(transformType, \"translate\") == 0) {\r\n\t\t\t\t\tgetAxesValues(node, nodeMatrix(0, 3), nodeMatrix(1, 3), nodeMatrix(2, 3));\r\n\t\t\t\t}\r\n\t\t\t\telse if (strcmp(transformType, \"scale\") == 0) {\r\n\t\t\t\t\tfloat scale = 1.f;\r\n\t\t\t\t\tgetfValue(node, \"value\", scale);\r\n\t\t\t\t\tgetAxesValues(node, nodeMatrix(0, 0), nodeMatrix(1, 1), nodeMatrix(2, 2));\r\n\t\t\t\t\tnodeMatrix(0, 0) *= scale;\r\n\t\t\t\t\tnodeMatrix(1, 1) *= scale;\r\n\t\t\t\t\tnodeMatrix(2, 2) *= scale;\r\n\t\t\t\t}\r\n\t\t\t\telse if (strcmp(transformType, \"rotate\") == 0) {\r\n\t\t\t\t\tfloat rotateX = 0.f, rotateY = 0.f, rotateZ = 0.f;\r\n\t\t\t\t\tfloat angleDegrees = 0.f;\r\n\t\t\t\t\tgetAxesValues(node, rotateX, rotateY, rotateZ);\r\n\t\t\t\t\tgetfValue(node, \"angle\", angleDegrees);\r\n\t\t\t\t\tfloat angleRadians = angleDegrees * (float)M_PI / 180.f;\r\n\t\t\t\t\tsibr::Transform3<float> transform;\r\n\t\t\t\t\ttransform.rotate(Eigen::Quaternionf(\r\n\t\t\t\t\t\tEigen::AngleAxisf(angleRadians,\r\n\t\t\t\t\t\t\tEigen::Vector3f(rotateX, rotateY, rotateZ))));\r\n\t\t\t\t\tnodeMatrix = transform.matrix();\r\n\t\t\t\t}\r\n\t\t\t\telse if (strcmp(transformType, \"lookat\") == 0) {\r\n\t\t\t\t\tsibr::Vector3f eye{ 0, 0, 0 }, target{ 0, 0, 1 }, up{ 0, 1, 0 };\r\n\t\t\t\t\tgetVector(node, \"origin\", eye);\r\n\t\t\t\t\tgetVector(node, \"target\", target);\r\n\t\t\t\t\tgetVector(node, \"up\", up);\r\n\t\t\t\t\t// WTF why inverse the lookat???\r\n\t\t\t\t\tnodeMatrix = sibr::lookAt(eye, target, up).inverse();\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\tthrow std::runtime_error(std::string(\"Mitsuba xml parser: Unknown tranform type: \") + transformType);\r\n\t\t\t\t}\r\n\t\t\t\tobjectToWorld = nodeMatrix * objectToWorld;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn objectToWorld;\r\n\t}\r\n\r\n\t// Extracts Name from (can accept node == nullptr)\r\n\t// <ref id=\"Name\"/>\r\n\tstd::string parseMatID(const rapidxml::xml_node<>* node)\r\n\t{\r\n\t\tstd::string res;\r\n\t\tif (node)\r\n\t\t{\r\n\t\t\tconst rapidxml::xml_attribute<>* id = node->first_attribute(\"id\");\r\n\t\t\tres = id->value();\r\n\t\t}\r\n\t\treturn res;\r\n\t}\r\n\r\n\tbool shouldFlipNormals(const rapidxml::xml_node<>* shape)\r\n\t{\r\n\t\tassert(shape != nullptr);\r\n\t\tfor (const rapidxml::xml_node<>* node = shape->first_node(\"boolean\"); node; node = node->next_sibling(\"boolean\"))\r\n\t\t{\r\n\t\t\tif (node->first_attribute(\"name\")->value() == std::string(\"flipNormals\"))\r\n\t\t\t{\r\n\t\t\t\treturn node->first_attribute(\"value\")->value() == std::string(\"true\");\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tstd::string parseFilename(const rapidxml::xml_node<>* shape)\r\n\t{\r\n\t\tassert(shape);\r\n\t\tfor (const rapidxml::xml_node<>* node = shape->first_node(\"string\"); node; node = node->next_sibling(\"string\"))\r\n\t\t{\r\n\t\t\tif (node->first_attribute(\"name\")->value() == std::string(\"filename\"))\r\n\t\t\t{\r\n\t\t\t\treturn node->first_attribute(\"value\")->value();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn \"\";\r\n\t}\r\n\r\n\tbool\tMaterialMesh::loadMtsXML(const std::string& xmlFile, bool loadTextures)\r\n\t{\r\n\t\tsrand(static_cast <unsigned> (time(0)));\r\n\t\tbool allLoaded = true;\r\n\t\tstd::string pathFolder = boost::filesystem::path(xmlFile).parent_path()\r\n\t\t\t.string();\r\n\t\tsibr::XMLTree doc(xmlFile);\r\n\t\tstd::map<std::string, sibr::MaterialMesh> meshes;\r\n\r\n\t\tstruct ShapeGroup {\r\n\t\t\tstruct Shape\r\n\t\t\t{\r\n\t\t\t\tstd::string filename;\r\n\t\t\t\tstd::string matname;\r\n\t\t\t\tsibr::Matrix4f toWorld = sibr::Matrix4f::Identity();\r\n\t\t\t\tShape& operator=(Shape const&) = default;\r\n\t\t\t\tbool flipNormals = false;\r\n\t\t\t};\r\n\t\t\tstd::vector<Shape> shapes;\r\n\t\t\tsibr::Matrix4f objectToWorld = sibr::Matrix4f::Identity();\r\n\t\t};\r\n\r\n\t\tstd::map<std::string, ShapeGroup> idToShapegroups;\r\n\r\n\t\trapidxml::xml_node<> *nodeScene = doc.first_node(\"scene\");\r\n\r\n\t\t// First parse all the shapegroups\r\n\t\tfor (rapidxml::xml_node<> *node = nodeScene->first_node(\"shape\");\r\n\t\t\tnode; node = node->next_sibling(\"shape\"))\r\n\t\t{\r\n\t\t\tif (strcmp(node->first_attribute()->name(), \"type\") == 0 &&\r\n\t\t\t\tstrcmp(node->first_attribute()->value(), \"shapegroup\") == 0) {\r\n\r\n\t\t\t\t//std::cout << \"Found : \" << node->first_attribute(\"id\")->value() << std::endl;\r\n\r\n\t\t\t\tstd::string id = node->first_attribute(\"id\")->value();\r\n\t\t\t\tShapeGroup shapeGroup;\r\n\t\t\t\tfor (rapidxml::xml_node<>* shapeNode = node->first_node(\"shape\");shapeNode;shapeNode=shapeNode->next_sibling())\r\n\t\t\t\t{\r\n\t\t\t\t\tShapeGroup::Shape shape;\r\n\t\t\t\t\tshape.filename = parseFilename(shapeNode);\r\n\t\t\t\t\tshape.toWorld = parseTransform(shapeNode->first_node(\"transform\"));\r\n\t\t\t\t\tshape.matname = parseMatID(shapeNode->first_node(\"ref\"));\r\n\t\t\t\t\tshape.flipNormals = shouldFlipNormals(shapeNode);\r\n\t\t\t\t\tshapeGroup.shapes.push_back(shape);\r\n\t\t\t\t}\r\n\t\t\t\trapidxml::xml_node<>* tNode = node->first_node(\"transform\");\r\n\t\t\t\tshapeGroup.objectToWorld = parseTransform(tNode);\r\n\t\t\t\tidToShapegroups[id] = shapeGroup;\r\n\t\t\t}\r\n\t\t}\r\n\t\t// Second: Create all the actual shapes\r\n\t\tfor (rapidxml::xml_node<> *node = nodeScene->first_node(\"shape\");\r\n\t\t\tnode; node = node->next_sibling(\"shape\"))\r\n\t\t{\r\n\t\t\tfor (rapidxml::xml_attribute<> *browserAttributes = node->\r\n\t\t\t\tfirst_attribute();\r\n\t\t\t\tbrowserAttributes;\r\n\t\t\t\tbrowserAttributes = browserAttributes->next_attribute()) {\r\n\t\t\t\t// Create the instances of the shapegroups\r\n\t\t\t\tif (strcmp(browserAttributes->name(), \"type\") == 0 &&\r\n\t\t\t\t\tstrcmp(browserAttributes->value(), \"instance\") == 0\r\n\t\t\t\t\t) {\r\n\t\t\t\t\trapidxml::xml_node<>* nodeRef = node->first_node(\"ref\");\r\n\t\t\t\t\tconst std::string _id = nodeRef->first_attribute(\"id\")->value();\r\n\t\t\t\t\tSIBR_LOG << \"Instancing \" << _id << std::endl;\r\n\t\t\t\t\tif (idToShapegroups.find(_id) == idToShapegroups.end())\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tSIBR_WRG << \"Could not find shapegroup \" << _id << \"!!!\" << std::endl;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tconst ShapeGroup& shapeGroup = idToShapegroups[_id];\r\n\t\t\t\t\trapidxml::xml_node<>* nodeTrans = node->first_node(\"transform\");\r\n\t\t\t\t\t// I am not 100% sure of the order of the matrices\r\n\t\t\t\t\tsibr::Matrix4f objectToWorld = shapeGroup.objectToWorld * parseTransform(nodeTrans);\r\n\t\t\t\t\tsibr::MaterialMesh instance;\r\n\t\t\t\t\tfor (const auto& shape : shapeGroup.shapes)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstd::cout << shape.filename;\r\n\t\t\t\t\t\tconst std::string meshPath = pathFolder + \"/\" + shape.filename;\r\n\r\n\t\t\t\t\t\tif (meshes.find(meshPath) == meshes.end())\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tmeshes[meshPath] = MaterialMesh();\r\n\t\t\t\t\t\t\tmeshes[meshPath].load(meshPath);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tsibr::MaterialMesh toWorldMesh = meshes[meshPath];\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (shape.flipNormals && toWorldMesh.hasNormals())\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tconst auto& refNormals = toWorldMesh.normals();\r\n\t\t\t\t\t\t\tsibr::Mesh::Normals normals(refNormals.size());\r\n\t\t\t\t\t\t\tfor (int nid = 0; nid < refNormals.size(); ++nid)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tnormals[nid] = -refNormals[nid];\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\ttoWorldMesh.normals(normals);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t// Convert to world coordinates\r\n\t\t\t\t\t\tsibr::Matrix4f matrix = shape.toWorld * objectToWorld;\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tsibr::MaterialMesh::Vertices vertices(toWorldMesh.vertices().size());\r\n\t\t\t\t\t\t\tfor (int v = 0; v < toWorldMesh.vertices().size(); v++) {\r\n\t\t\t\t\t\t\t\tsibr::Vector4f v4(toWorldMesh.vertices()[v].x(),\r\n\t\t\t\t\t\t\t\t\ttoWorldMesh.vertices()[v].y(),\r\n\t\t\t\t\t\t\t\t\ttoWorldMesh.vertices()[v].z(), 1.0);\r\n\t\t\t\t\t\t\t\tvertices[v] = (matrix* v4).xyz();\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\ttoWorldMesh.vertices(vertices);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t// If the mesh has normals, we should transform them also.\r\n\t\t\t\t\t\tif (toWorldMesh.hasNormals()) {\r\n\t\t\t\t\t\t\tsibr::Mesh::Normals normals(toWorldMesh.normals().size());\r\n\t\t\t\t\t\t\tconst sibr::Matrix3f normalTMatrix = matrix.block(0, 0, 3, 3).inverse().transpose();\r\n\t\t\t\t\t\t\tfor (int v = 0; v < toWorldMesh.normals().size(); v++) {\r\n\t\t\t\t\t\t\t\tconst sibr::Vector3f& ln = toWorldMesh.normals()[v];\r\n\t\t\t\t\t\t\t\tnormals[v] = (normalTMatrix * ln).xyz();\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\ttoWorldMesh.normals(normals);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (!shape.matname.empty())\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\ttoWorldMesh.matId2Name({ shape.matname });\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tinstance.merge(toWorldMesh);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmerge(instance);\r\n\t\t\t\t}\r\n\t\t\t\t// Create the \"unique\" shapes\r\n\t\t\t\telse if (strcmp(browserAttributes->name(), \"type\") == 0 &&\r\n\t\t\t\t\t(strcmp(browserAttributes->value(), \"obj\") == 0 ||\r\n\t\t\t\t\t\tstrcmp(browserAttributes->value(), \"ply\") == 0)) {\r\n\r\n\t\t\t\t\trapidxml::xml_node<> *nodeRef = node->first_node(\"string\");\r\n\t\t\t\t\tconst std::string filename = nodeRef->first_attribute(\"value\")\r\n\t\t\t\t\t\t->value();\r\n\t\t\t\t\tconst std::string meshPath = pathFolder + \"/\" + filename;\r\n\t\t\t\t\t// Search for any normal options:\r\n\t\t\t\t\trapidxml::xml_node<> *nodeOpt = node->first_node(\"boolean\");\r\n\t\t\t\t\tbool flipNormals = false;\r\n\t\t\t\t\twhile (nodeOpt) {\r\n\t\t\t\t\t\tconst std::string name = nodeOpt->first_attribute(\"name\")->value();\r\n\t\t\t\t\t\tconst std::string value = nodeOpt->first_attribute(\"value\")->value();\r\n\t\t\t\t\t\tif (name == \"flipNormals\" && value == \"true\") {\r\n\t\t\t\t\t\t\tflipNormals = true;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tnodeOpt = nodeOpt->next_sibling(\"boolean\");\r\n\t\t\t\t\t}\r\n\r\n\r\n\t\t\t\t\tif (meshes.find(filename) == meshes.end()) {\r\n\t\t\t\t\t\tmeshes[filename] = sibr::MaterialMesh();\r\n\t\t\t\t\t\tif (!meshes[filename].load(meshPath)) {\r\n\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (meshes[filename].matIds().empty())\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tSIBR_WRG << \"Material (\" << filename << \") not present ...\" << std::endl;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tSIBR_LOG << \"Adding one instance of: \" << filename\r\n\t\t\t\t\t\t<< std::endl;\r\n\r\n\t\t\t\t\trapidxml::xml_node<> *nodeRefMat = node->first_node(\"ref\");\r\n\t\t\t\t\tif (nodeRefMat) {\r\n\t\t\t\t\t\tconst std::string matName = nodeRefMat\r\n\t\t\t\t\t\t\t->first_attribute(\"id\")->value();\r\n\r\n\t\t\t\t\t\tMatId2Name newmatIdtoName;\r\n\t\t\t\t\t\tnewmatIdtoName.push_back(matName);\r\n\t\t\t\t\t\tmeshes[filename].matId2Name(newmatIdtoName);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\trapidxml::xml_node<> *nodeTrans = node\r\n\t\t\t\t\t\t->first_node(\"transform\");\r\n\r\n\t\t\t\t\tsibr::Matrix4f objectToWorld = parseTransform(nodeTrans);\r\n\r\n\t\t\t\t\tsibr::MaterialMesh toWorldMesh = meshes[filename];\r\n\r\n\t\t\t\t\t// Apply normals transformation if needed.\r\n\t\t\t\t\tif (flipNormals && toWorldMesh.hasNormals()) {\r\n\t\t\t\t\t\tconst auto & refNormals = toWorldMesh.normals();\r\n\t\t\t\t\t\tsibr::Mesh::Normals normals(refNormals.size());\r\n\t\t\t\t\t\tfor (int nid = 0; nid < refNormals.size(); nid++) {\r\n\t\t\t\t\t\t\tnormals[nid] = -refNormals[nid];\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\ttoWorldMesh.normals(normals);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (nodeTrans) {\r\n\t\t\t\t\t\t// Transform the vertices position\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tsibr::Mesh::Vertices vertices(toWorldMesh.vertices().size());\r\n\t\t\t\t\t\t\tfor (int v = 0; v < toWorldMesh.vertices().size(); v++) {\r\n\t\t\t\t\t\t\t\tsibr::Vector4f v4(toWorldMesh.vertices()[v].x(),\r\n\t\t\t\t\t\t\t\t\ttoWorldMesh.vertices()[v].y(),\r\n\t\t\t\t\t\t\t\t\ttoWorldMesh.vertices()[v].z(), 1.0);\r\n\t\t\t\t\t\t\t\tvertices[v] = (objectToWorld*v4).xyz();\r\n\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\ttoWorldMesh.vertices(vertices);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t// Transform the normals too\r\n\t\t\t\t\t\tif (toWorldMesh.hasNormals()) {\r\n\t\t\t\t\t\t\tsibr::Mesh::Normals normals(toWorldMesh.normals().size());\r\n\t\t\t\t\t\t\tconst sibr::Matrix3f normalTMatrix = objectToWorld.block(0, 0, 3, 3).inverse().transpose();\r\n\t\t\t\t\t\t\tfor (int v = 0; v < toWorldMesh.normals().size(); v++) {\r\n\t\t\t\t\t\t\t\tconst sibr::Vector3f& ln = toWorldMesh.normals()[v];\r\n\t\t\t\t\t\t\t\tnormals[v] = (normalTMatrix * ln);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\ttoWorldMesh.normals(normals);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tmerge(toWorldMesh);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tSIBR_LOG << \"Loaded mesh: \" << vertices().size() << \" verts, \" << meshIds().size() << \" ids.\" << std::endl;\r\n\t\t// Load all the materials\r\n\t\tfor (rapidxml::xml_node<> *node = nodeScene->first_node(\"bsdf\");\r\n\t\t\tnode; node = node->next_sibling(\"bsdf\"))\r\n\t\t{\r\n\t\t\t//getting id \r\n\t\t\trapidxml::xml_attribute<> *attribute = node->first_attribute(\"id\");\r\n\t\t\tif (attribute != nullptr) {\r\n\r\n\t\t\t\tstd::string nameMat = attribute->value();\r\n\r\n\r\n\t\t\t\t// Check if a texture exists in our node with diffuse reflectance\r\n\t\t\t\t// If none is found, explore each BRDF until found.\r\n\t\t\t\tstd::vector <rapidxml::xml_node<>*> queue;\r\n\t\t\t\tqueue.push_back(node);\r\n\r\n\t\t\t\tbool breakBool = false;\r\n\r\n\t\t\t\twhile (!queue.empty()) {\r\n\r\n\r\n\t\t\t\t\t//Texture Case\r\n\t\t\t\t\tfor (rapidxml::xml_node<> *nodeTexture =\r\n\t\t\t\t\t\tqueue.front()->first_node(\"texture\");\r\n\t\t\t\t\t\tnodeTexture;\r\n\t\t\t\t\t\tnodeTexture = nodeTexture->next_sibling(\"texture\")) {\r\n\r\n\t\t\t\t\t\tif (strcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\t\"diffuseReflectance\") == 0 ||\r\n\t\t\t\t\t\t\tstrcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\t\t\"reflectance\") == 0 ||\r\n\t\t\t\t\t\t\tstrcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\t\t\"specularReflectance\") == 0\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t//std::cout << \"DiffuseReflectance Texture found\" << std::endl;\r\n\t\t\t\t\t\t\trapidxml::xml_node<> *firstTexture = nodeTexture->\r\n\t\t\t\t\t\t\t\tfirst_node(\"texture\");\r\n\t\t\t\t\t\t\tif (firstTexture == nullptr) {\r\n\t\t\t\t\t\t\t\tfirstTexture = nodeTexture;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tfor (rapidxml::xml_node<> *nodeString = firstTexture->\r\n\t\t\t\t\t\t\t\tfirst_node(\"string\");\r\n\t\t\t\t\t\t\t\tnodeString;\r\n\t\t\t\t\t\t\t\tnodeString = nodeString->next_sibling(\"string\"))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tstd::string textureName =\r\n\t\t\t\t\t\t\t\t\tnodeString->first_attribute(\"value\")->value();\r\n\t\t\t\t\t\t\t\tsibr::ImageRGBA::Ptr texture(new sibr::ImageRGBA());\r\n\t\t\t\t\t\t\t\t// If we skip loading the textures, still set them as empty images.\r\n\t\t\t\t\t\t\t\tif (!loadTextures || texture->load(pathFolder + \"/\" + textureName)) {\r\n\t\t\t\t\t\t\t\t\t/*std::cout << \"Diffuse \" << pathFolder + \"/\"\r\n\t\t\t\t\t\t\t\t\t\t+ textureName << std::endl;*/\r\n\t\t\t\t\t\t\t\t\t_diffuseMaps[nameMat] = texture;\r\n\t\t\t\t\t\t\t\t\tbreakBool = true;\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\t\t\tSIBR_ERR << \"Diffuse layer for: \" <<\r\n\t\t\t\t\t\t\t\t\t\tnameMat << \" not found\" << std::endl;\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (breakBool)\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\r\n\t\t\t\t\t//Color case\r\n\r\n\t\t\t\t\tif (!breakBool) {\r\n\t\t\t\t\t\tstd::list<std::string> colorsFormatList;\r\n\t\t\t\t\t\tcolorsFormatList.push_back(\"rgb\");\r\n\t\t\t\t\t\tcolorsFormatList.push_back(\"srgb\");\r\n\t\t\t\t\t\tfor (std::string colorsFormat : colorsFormatList)\r\n\t\t\t\t\t\t\tfor (rapidxml::xml_node<> *nodeTexture =\r\n\t\t\t\t\t\t\t\tqueue.front()->first_node(colorsFormat.c_str());\r\n\t\t\t\t\t\t\t\tnodeTexture;\r\n\t\t\t\t\t\t\t\tnodeTexture = nodeTexture->next_sibling(\r\n\t\t\t\t\t\t\t\t\tcolorsFormat.c_str())) {\r\n\r\n\t\t\t\t\t\t\t\tif (strcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\t\t\t\"diffuseReflectance\") == 0 ||\r\n\t\t\t\t\t\t\t\t\tstrcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\t\t\t\t\"reflectance\") == 0 ||\r\n\t\t\t\t\t\t\t\t\tstrcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\t\t\t\t\"specularReflectance\") == 0\r\n\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t/*std::cout << \"DiffuseReflectance Color found\"\r\n\t\t\t\t\t\t\t\t\t\t<< std::endl;*/\r\n\t\t\t\t\t\t\t\t\trapidxml::xml_node<> *firstTexture = nodeTexture->\r\n\t\t\t\t\t\t\t\t\t\tfirst_node(colorsFormat.c_str());\r\n\t\t\t\t\t\t\t\t\tif (firstTexture == nullptr) {\r\n\t\t\t\t\t\t\t\t\t\tfirstTexture = nodeTexture;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\tstd::string colorString =\r\n\t\t\t\t\t\t\t\t\t\tnodeTexture->first_attribute(\"value\")->value();\r\n\t\t\t\t\t\t\t\t\tsibr::Vector3f colorMaterial;\r\n\t\t\t\t\t\t\t\t\tfloat redComponent, greenComponent, blueComponent;\r\n#ifdef SIBR_OS_WINDOWS\r\n\t\t\t\t\t\t\t\t\tsscanf_s(colorString.c_str(), \"%f, %f, %f\",\r\n\t\t\t\t\t\t\t\t\t\t&redComponent, &greenComponent, &blueComponent);\r\n#else\r\n\t\t\t\t\t\t\t\t\tsscanf(colorString.c_str(), \"%f, %f, %f\",\r\n\t\t\t\t\t\t\t\t\t\t&redComponent, &greenComponent, &blueComponent);\r\n#endif\r\n\t\t\t\t\t\t\t\t\tconst sibr::ImageRGBA::Pixel color(\r\n\t\t\t\t\t\t\t\t\t\tstatic_cast<const unsigned char>(redComponent * 255),\r\n\t\t\t\t\t\t\t\t\t\tstatic_cast<const unsigned char>(greenComponent * 255),\r\n\t\t\t\t\t\t\t\t\t\tstatic_cast<const unsigned char>(blueComponent * 255),\r\n\t\t\t\t\t\t\t\t\t\t255);\r\n\t\t\t\t\t\t\t\t\tsibr::ImageRGBA::Ptr texture(new sibr::ImageRGBA(\r\n\t\t\t\t\t\t\t\t\t\t1, 1, color));\r\n\t\t\t\t\t\t\t\t\tif (texture) {\r\n\t\t\t\t\t\t\t\t\t\t/*std::cout << \"Diffuse color : \" <<\r\n\t\t\t\t\t\t\t\t\t\t\tredComponent << \", \" << blueComponent <<\r\n\t\t\t\t\t\t\t\t\t\t\t\", \" << greenComponent << \", \" << std::endl;*/\r\n\t\t\t\t\t\t\t\t\t\t_diffuseMaps[nameMat] = texture;\r\n\t\t\t\t\t\t\t\t\t\t_tagsCoveringMaps[nameMat] = nullptr;\r\n\r\n\t\t\t\t\t\t\t\t\t\tbreakBool = true;\r\n\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\t\t\t\tSIBR_ERR << \"Diffuse layer for: \" << nameMat\r\n\t\t\t\t\t\t\t\t\t\t\t<< \" not found\" << std::endl;\r\n\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\tif (breakBool)\r\n\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tqueue.erase(queue.begin());\r\n\r\n\t\t\t\t\tfor (rapidxml::xml_node<> *node = queue.front()->\r\n\t\t\t\t\t\tfirst_node(\"bsdf\");\r\n\t\t\t\t\t\tnode; node = node->next_sibling(\"bsdf\"))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tqueue.push_back(node);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\t\t\t\tif (_diffuseMaps[nameMat].get() == nullptr) {\r\n\r\n\t\t\t\t\tfloat r = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\t\t\t\t\tfloat g = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\t\t\t\t\tfloat b = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\r\n\t\t\t\t\tconst sibr::ImageRGBA::Pixel color(\r\n\t\t\t\t\t\tstatic_cast<const unsigned char>(r * 255),\r\n\t\t\t\t\t\tstatic_cast<const unsigned char>(g * 255),\r\n\t\t\t\t\t\tstatic_cast<const unsigned char>(b * 255),\r\n\t\t\t\t\t\tstatic_cast<const unsigned char> (255)\r\n\t\t\t\t\t);\r\n\t\t\t\t\tsibr::ImageRGBA::Ptr texture(new sibr::ImageRGBA(\r\n\t\t\t\t\t\t1, 1, color));\r\n\t\t\t\t\tSIBR_WRG << \"Warning: No color and no texture found for \" << nameMat << \", \" <<\r\n\t\t\t\t\t\t\"material will be chosen randomly.\" << std::endl;\r\n\t\t\t\t\t_diffuseMaps[nameMat] = texture;\r\n\r\n\t\t\t\t\t_tagsCoveringMaps[nameMat] = nullptr;\r\n\t\t\t\t\t/*if (_hasTagsCoveringFile) {\r\n\t\t\t\t\t\t_tagsCoveringMaps[nameMat] = _listCoveringImagesTags.at(\r\n\t\t\t\t\t\t\t_tagsCoveringMaps.size() % _listCoveringImagesTags.size());\r\n\t\t\t\t\t}*/\r\n\t\t\t\t}\r\n\r\n\r\n\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tbool breakBool = false;\r\n\t\tfor (rapidxml::xml_node<> *node = nodeScene->first_node(\"bsdf\");\r\n\t\t\tnode; node = node->next_sibling(\"bsdf\"))\r\n\t\t{\r\n\t\t\tif (node != nullptr && node->first_attribute(\"id\") != nullptr)\r\n\t\t\t{\r\n\t\t\t\tstd::string nameMat = node->first_attribute(\"id\")->value();\r\n\r\n\t\t\t\tbool breakBool = false;\r\n\t\t\t\tfor (rapidxml::xml_node<> *nodeTexture = node->\r\n\t\t\t\t\tfirst_node(\"texture\");\r\n\t\t\t\t\tnodeTexture; nodeTexture = nodeTexture->\r\n\t\t\t\t\tnext_sibling(\"texture\")) {\r\n\r\n\t\t\t\t\tif (strcmp(nodeTexture->first_attribute(\"name\")->value(),\r\n\t\t\t\t\t\t\"opacity\") == 0 &&\r\n\t\t\t\t\t\tnodeTexture->first_attribute(\"type\") &&\r\n\t\t\t\t\t\tstrcmp(nodeTexture->first_attribute(\"type\")->value(),\r\n\t\t\t\t\t\t\t\"scale\") == 0) {\r\n\t\t\t\t\t\t//std::cout << \"Found opacity mask:\" << nameMat << std::endl;\r\n\r\n\t\t\t\t\t\tfor (rapidxml::xml_node<> *nodeString = nodeTexture->\r\n\t\t\t\t\t\t\tfirst_node(\"texture\")->first_node(\"string\");\r\n\t\t\t\t\t\t\tnodeString; nodeString = nodeString->\r\n\t\t\t\t\t\t\tnext_sibling(\"string\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tstd::string textureName = nodeString->\r\n\t\t\t\t\t\t\t\tfirst_attribute(\"value\")->value();\r\n\t\t\t\t\t\t\tsibr::ImageRGB::Ptr texture(new sibr::ImageRGB());\r\n\t\t\t\t\t\t\tif (!loadTextures || texture->load(pathFolder + \"/\" + textureName)) {\r\n\t\t\t\t\t\t\t\t_opacityMaps[nameMat] = texture;\r\n\t\t\t\t\t\t\t\tbreakBool = true;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\t\tSIBR_ERR << \"Opacity layer for: \" <<\r\n\t\t\t\t\t\t\t\t\tnameMat << \" not found\" << std::endl;\r\n\t\t\t\t\t\t\t}\r\n\r\n\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (breakBool)\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\t\t\t\tif (!breakBool) {\r\n\t\t\t\t\tconst sibr::ImageRGB::Pixel color(255, 255, 255);\r\n\t\t\t\t\tsibr::ImageRGB::Ptr texture(new sibr::ImageRGB(1, 1, color));\r\n\t\t\t\t\t_opacityMaps[nameMat] = texture;\r\n\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tcreateSubMeshes();\r\n\t\treturn true;\r\n\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::loadCoveringTagsTexture(\r\n\t\tconst std::vector<std::string>& listFilesTags) {\r\n\r\n\t\tfor (const std::string filename : listFilesTags) {\r\n\r\n\t\t\tsibr::ImageRGB::Ptr textureTag(new sibr::ImageRGB());\r\n\t\t\tif (textureTag->load(filename)) {\r\n\t\t\t\t_listCoveringImagesTags.push_back(textureTag);\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tSIBR_ERR << \"Diffuse layer for: \" <<\r\n\t\t\t\t\tfilename << \" not found\" << std::endl;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (_listCoveringImagesTags.size() > 0)\r\n\t\t\t_hasTagsCoveringFile = true;\r\n\r\n\t\tif (_hasTagsCoveringFile) {\r\n\t\t\tunsigned int counter = 0;\r\n\t\t\tfor (auto it = matId2Name().begin(); it != matId2Name().end(); ++it) {\r\n\t\t\t\tif (_tagsCoveringMaps.find(*it) != _tagsCoveringMaps.end()) {\r\n\t\t\t\t\t_tagsCoveringMaps[*it] = _listCoveringImagesTags.at(\r\n\t\t\t\t\t\tcounter % _listCoveringImagesTags.size());\r\n\t\t\t\t\tcounter++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tvoid MaterialMesh::fillColorsWithIndexMaterials(void)\r\n\t{\r\n\t\tsibr::Mesh::Colors colorsIdsMaterials(vertices().size());\r\n\r\n\t\tsibr::Mesh::Colors randomsColors;\r\n\r\n\t\tsrand(static_cast <unsigned> (time(0)));\r\n\t\tfor (std::string material : _matId2Name) {\r\n\t\t\tfloat r = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\t\t\tfloat g = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\t\t\tfloat b = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);\r\n\r\n\t\t\trandomsColors.push_back(sibr::Vector3f(r, g, b));\r\n\t\t}\r\n\r\n\t\tfor (unsigned int i = 0; i < _matIds.size(); i++)\r\n\t\t{\r\n\t\t\tcolorsIdsMaterials.at(_triangles.at(i)[0]) = randomsColors.at(\r\n\t\t\t\t_matIds.at(i));\r\n\t\t\tcolorsIdsMaterials.at(_triangles.at(i)[1]) = randomsColors.at\r\n\t\t\t(_matIds.at(i));\r\n\t\t\tcolorsIdsMaterials.at(_triangles.at(i)[2]) = randomsColors.at\r\n\t\t\t(_matIds.at(i));\r\n\t\t}\r\n\r\n\t\tcolors(colorsIdsMaterials);\r\n\t}\r\n\r\n\r\n\tvoid MaterialMesh::fillColorsWithMatIds()\r\n\t{\r\n\t\tsibr::Mesh::Colors colorsIdsMaterials(vertices().size());\r\n\r\n\t\tfor (unsigned int i = 0; i < _matIds.size(); i++)\r\n\t\t{\r\n\t\t\tconst uint matId = uint(_matIds.at(i) + 1);\r\n\t\t\tconst sibr::Vector3u col = { uchar(matId & 0xff), uchar((matId >> 8) & 0xff) , uchar((matId >> 16) & 0xff) };\r\n\t\t\tconst sibr::Vector3f finalCol = col.cast<float>() / 255.0f;\r\n\t\t\tcolorsIdsMaterials.at(_triangles.at(i)[0]) = finalCol;\r\n\t\t\tcolorsIdsMaterials.at(_triangles.at(i)[1]) = finalCol;\r\n\t\t\tcolorsIdsMaterials.at(_triangles.at(i)[2]) = finalCol;\r\n\t\t}\r\n\r\n\t\tcolors(colorsIdsMaterials);\r\n\t}\r\n\r\n\tMesh MaterialMesh::generateSubMaterialMesh(int material) const\r\n\t{\r\n\r\n\t\tsibr::Mesh::Vertices newVertices;\r\n\t\tsibr::Mesh::Triangles newTriangles;\r\n\r\n\t\tsibr::Mesh::Colors newColors;\r\n\t\tsibr::Mesh::Normals newNormals;\r\n\t\tsibr::Mesh::UVs newTexCoords;\r\n\r\n\t\tstd::map<int, int> mapIdVert;\r\n\r\n\t\tint cmptValidVert = 0;\r\n\t\tint cmptVert = 0;\r\n\r\n\t\tsibr::Mesh::Colors oldColors;\r\n\t\tif (hasColors())\r\n\t\t\toldColors = colors();\r\n\r\n\t\tsibr::Mesh::Normals oldNormals;\r\n\t\tif (hasNormals())\r\n\t\t\toldNormals = normals();\r\n\r\n\t\tsibr::Mesh::UVs oldTexCoords;\r\n\t\tif (hasTexCoords())\r\n\t\t\toldTexCoords = texCoords();\r\n\r\n\t\tfor (int i = 0; i < matIds().size(); i++)\r\n\t\t{\r\n\t\t\tif (matIds().at(i) == material) {\r\n\r\n\t\t\t\tuint v1, v2, v3;\r\n\t\t\t\tv1 = triangles().at(i)[0];\r\n\t\t\t\tv2 = triangles().at(i)[1];\r\n\t\t\t\tv3 = triangles().at(i)[2];\r\n\r\n\t\t\t\tauto search = mapIdVert.find(v1);\r\n\t\t\t\tif (search == mapIdVert.end()) {\r\n\t\t\t\t\tnewVertices.push_back(vertices()[v1]);\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\tnewColors.push_back(oldColors[v1]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\tnewNormals.push_back(oldNormals[v1]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\tnewTexCoords.push_back(oldTexCoords[v1]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmapIdVert[v1] = cmptValidVert;\r\n\t\t\t\t\tcmptValidVert++;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsearch = mapIdVert.find(v2);\r\n\t\t\t\tif (search == mapIdVert.end()) {\r\n\t\t\t\t\tnewVertices.push_back(vertices()[v2]);\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\tnewColors.push_back(oldColors[v2]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\tnewNormals.push_back(oldNormals[v2]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\tnewTexCoords.push_back(oldTexCoords[v2]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmapIdVert[v2] = cmptValidVert;\r\n\t\t\t\t\tcmptValidVert++;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsearch = mapIdVert.find(v3);\r\n\t\t\t\tif (search == mapIdVert.end()) {\r\n\t\t\t\t\tnewVertices.push_back(vertices()[v3]);\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\tnewColors.push_back(oldColors[v3]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\tnewNormals.push_back(oldNormals[v3]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\tnewTexCoords.push_back(oldTexCoords[v3]);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmapIdVert[v3] = cmptValidVert;\r\n\t\t\t\t\tcmptValidVert++;\r\n\t\t\t\t}\r\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(mapIdVert[v1], mapIdVert[v2]\r\n\t\t\t\t\t, mapIdVert[v3]));\r\n\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tMesh newMesh;\r\n\t\tnewMesh.vertices(newVertices);\r\n\t\tnewMesh.triangles(newTriangles);\r\n\t\tif (hasColors())\r\n\t\t\tnewMesh.colors(newColors);\r\n\t\tif (hasNormals())\r\n\t\t\tnewMesh.normals(newNormals);\r\n\t\tif (hasTexCoords())\r\n\t\t\tnewMesh.texCoords(newTexCoords);\r\n\r\n\t\treturn newMesh;\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::forceBufferGLUpdate(void) const\r\n\t{\r\n\t\tif (!_gl.bufferGL) { SIBR_ERR << \"Tried to forceBufferGL on a non OpenGL Mesh\" << std::endl; return; }\r\n\t\t_gl.dirtyBufferGL = false;\r\n\t\t_gl.bufferGL->build(*this);\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::freeBufferGLUpdate(void) const\r\n\t{\r\n\t\t_gl.dirtyBufferGL = false;\r\n\t\t_gl.bufferGL->free();\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::subdivideMesh2(float threshold) {\r\n\r\n\t\tauto areaHeronsFormula = [](sibr::Vector3f A, sibr::Vector3f B,\r\n\t\t\tsibr::Vector3f C) -> float {\r\n\t\t\tfloat a = distance(A, B);\r\n\t\t\tfloat b = distance(B, C);\r\n\t\t\tfloat c = distance(C, A);\r\n\t\t\treturn sqrtf((a + (b + c))*(c - (a - b))*(c + (a - b))*\r\n\t\t\t\t(a + (b - c))) / 4.f;\r\n\t\t};\r\n\r\n\t\tbool mustChange = true;\r\n\t\twhile (mustChange) {\r\n\t\t\tmustChange = false;\r\n\t\t\tsibr::Mesh::Colors newColors(colors());\r\n\t\t\tsibr::Mesh::Normals newNormals(normals());\r\n\t\t\tsibr::Mesh::UVs newTexCoords(texCoords());\r\n\t\t\tsibr::Mesh::Vertices newVertices(vertices());\r\n\t\t\tsibr::MaterialMesh::MeshIds newMeshIds(meshIds());\r\n\r\n\t\t\tsibr::Mesh::Triangles newTriangles;\r\n\t\t\tsibr::MaterialMesh::MatIds newMatIds;\r\n\t\t\tstd::cout << triangles().size() << \" triangles\" << std::endl;\r\n\t\t\tfor (unsigned int i = 0; i < triangles().size(); i++) {\r\n\t\t\t\tsibr::Vector3u t = triangles().at(i);\r\n\r\n\t\t\t\tint tMatId;\r\n\t\t\t\tif (i < matIds().size())\r\n\t\t\t\t\ttMatId = matIds().at(i);\r\n\r\n\t\t\t\tsibr::Vector3f a = vertices().at(t.x());\r\n\t\t\t\tsibr::Vector3f b = vertices().at(t.y());\r\n\t\t\t\tsibr::Vector3f c = vertices().at(t.z());\r\n\r\n\t\t\t\tif (areaHeronsFormula(a, b, c) >= (_averageArea*threshold)) {\r\n\t\t\t\t\tmustChange = true;\r\n\r\n\t\t\t\t\tsibr::Vector3f aColor, bColor, cColor, aNormal, bNormal, cNormal;\r\n\t\t\t\t\tsibr::Vector2f aTexCoords, bTexCoords, cTexCoords;\r\n\r\n\t\t\t\t\tsibr::Vector3f newColor, newNormal;\r\n\t\t\t\t\tsibr::Vector2f newTexCoord;\r\n\t\t\t\t\tsibr::Vector3f newVertex;\r\n\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\tnewColor = (colors().at(t.x()) + colors().at(t.y())\r\n\t\t\t\t\t\t\t+ colors().at(t.z())) / 3.f;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\tnewNormal = (normals().at(t.x()) + normals().at(t.y())\r\n\t\t\t\t\t\t\t+ normals().at(t.z())) / 3.f;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\tnewTexCoord = (texCoords().at(t.x()) + texCoords().at(t.y())\r\n\t\t\t\t\t\t\t+ texCoords().at(t.z())) / 3.f;\r\n\t\t\t\t\t}\r\n\r\n\r\n\t\t\t\t\tnewVertex = (vertices().at(t.x()) + vertices().at(t.y())\r\n\t\t\t\t\t\t+ vertices().at(t.z())) / 3.f;\r\n\r\n\t\t\t\t\tnewVertices.push_back(newVertex);\r\n\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\tnewColors.push_back(newColor);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\tnewNormals.push_back(newNormal);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\tnewTexCoords.push_back(newTexCoord);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasMeshIds()) {\r\n\t\t\t\t\t\t// Pick the first referenced vertex as the provoking vertex.\r\n\t\t\t\t\t\tnewMeshIds.push_back(meshIds().at(t.x()));\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tint newIndexVertex = static_cast<int> (newVertices.size()) - 1;\r\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.x(),\r\n\t\t\t\t\t\tt.y(),\r\n\t\t\t\t\t\tnewIndexVertex));\r\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.y(),\r\n\t\t\t\t\t\tt.z(),\r\n\t\t\t\t\t\tnewIndexVertex));\r\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.z(),\r\n\t\t\t\t\t\tt.x(),\r\n\t\t\t\t\t\tnewIndexVertex));\r\n\t\t\t\t\tif (i < matIds().size()) {\r\n\t\t\t\t\t\tfor (unsigned int n = 0; n < 3; ++n)\r\n\t\t\t\t\t\t\tnewMatIds.push_back(tMatId);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\tnewTriangles.push_back(t);\r\n\t\t\t\t\tif (i < matIds().size())\r\n\t\t\t\t\t\tnewMatIds.push_back(tMatId);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tvertices(newVertices);\r\n\t\t\tcolors(newColors);\r\n\t\t\tnormals(newNormals);\r\n\t\t\ttexCoords(newTexCoords);\r\n\t\t\ttriangles(newTriangles);\r\n\t\t\tmatIds(newMatIds);\r\n\t\t\tmeshIds(newMeshIds);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::subdivideMesh(float threshold) {\r\n\r\n\t\tbool mustChange = true;\r\n\t\twhile (mustChange) {\r\n\t\t\tmustChange = false;\r\n\t\t\tsibr::Mesh::Colors newColors(colors());\r\n\t\t\tsibr::Mesh::Normals newNormals(normals());\r\n\t\t\tsibr::Mesh::UVs newTexCoords(texCoords());\r\n\t\t\tsibr::Mesh::Vertices newVertices(vertices());\r\n\t\t\tsibr::MaterialMesh::MeshIds newMeshIds(meshIds());\r\n\r\n\t\t\tsibr::Mesh::Triangles newTriangles;\r\n\t\t\tsibr::MaterialMesh::MatIds newMatIds;\r\n\r\n\r\n\t\t\tstd::cout << triangles().size() << \" triangles\" << std::endl;\r\n\r\n\t\t\tfor (unsigned int i = 0; i < triangles().size(); i++) {\r\n\t\t\t\tsibr::Vector3u t = triangles().at(i);\r\n\r\n\t\t\t\tint tMatId;\r\n\t\t\t\tif (i < matIds().size())\r\n\t\t\t\t\ttMatId = matIds().at(i);\r\n\r\n\t\t\t\tsibr::Vector3f a = vertices().at(t.x());\r\n\t\t\t\tsibr::Vector3f b = vertices().at(t.y());\r\n\t\t\t\tsibr::Vector3f c = vertices().at(t.z());\r\n\r\n\t\t\t\tfloat localMaximum = 0.f;\r\n\t\t\t\tint longestSide;\r\n\r\n\t\t\t\tfloat d = distance(a, b);\r\n\t\t\t\tif (d > localMaximum) {\r\n\t\t\t\t\tlocalMaximum = d; longestSide = 0;\r\n\t\t\t\t}\r\n\t\t\t\td = distance(b, c);\r\n\t\t\t\tif (d > localMaximum) {\r\n\t\t\t\t\tlocalMaximum = d; longestSide = 1;\r\n\t\t\t\t}\r\n\t\t\t\td = distance(c, a);\r\n\t\t\t\tif (d > localMaximum) {\r\n\t\t\t\t\tlocalMaximum = d; longestSide = 2;\r\n\t\t\t\t}\r\n\r\n\r\n\t\t\t\tif (localMaximum >= (_averageSize*threshold)) {\r\n\t\t\t\t\tmustChange = true;\r\n\r\n\t\t\t\t\tsibr::Vector3f aColor, bColor, cColor, aNormal, bNormal,\r\n\t\t\t\t\t\tcNormal;\r\n\t\t\t\t\tsibr::Vector2f aTexCoords, bTexCoords, cTexCoords;\r\n\r\n\t\t\t\t\tsibr::Vector3f *v1Pos, *v2Pos, *v1Color, *v2Color,\r\n\t\t\t\t\t\t*v1Normal, *v2Normal;\r\n\t\t\t\t\tsibr::Vector2f *v1TexCoords, *v2TexCoords;\r\n\r\n\t\t\t\t\tsibr::Vector3f newColor, newNormal;\r\n\t\t\t\t\tsibr::Vector2f newTexCoord;\r\n\t\t\t\t\tsibr::Vector3f newVertex;\r\n\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\taColor = colors().at(t.x());\r\n\t\t\t\t\t\tbColor = colors().at(t.y());\r\n\t\t\t\t\t\tcColor = colors().at(t.z());\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\taNormal = normals().at(t.x());\r\n\t\t\t\t\t\tbNormal = normals().at(t.y());\r\n\t\t\t\t\t\tcNormal = normals().at(t.z());\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\taTexCoords = texCoords().at(t.x());\r\n\t\t\t\t\t\tbTexCoords = texCoords().at(t.y());\r\n\t\t\t\t\t\tcTexCoords = texCoords().at(t.z());\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (longestSide == 0) {\r\n\t\t\t\t\t\tv1Pos = &a; v2Pos = &b;\r\n\t\t\t\t\t\tv1Color = &aColor; v2Color = &bColor;\r\n\t\t\t\t\t\tv1Normal = &aNormal; v2Normal = &bNormal;\r\n\t\t\t\t\t\tv1TexCoords = &aTexCoords; v2TexCoords = &bTexCoords;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (longestSide == 1) {\r\n\t\t\t\t\t\tv1Pos = &b; v2Pos = &c;\r\n\t\t\t\t\t\tv1Color = &bColor; v2Color = &cColor;\r\n\t\t\t\t\t\tv1Normal = &bNormal; v2Normal = &cNormal;\r\n\t\t\t\t\t\tv1TexCoords = &bTexCoords; v2TexCoords = &cTexCoords;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (longestSide == 2) {\r\n\t\t\t\t\t\tv1Pos = &c; v2Pos = &a;\r\n\t\t\t\t\t\tv1Color = &cColor; v2Color = &aColor;\r\n\t\t\t\t\t\tv1Normal = &cNormal; v2Normal = &aNormal;\r\n\t\t\t\t\t\tv1TexCoords = &cTexCoords; v2TexCoords = &aTexCoords;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tnewVertex = sibr::Vector3f((v1Pos->x() + v2Pos->x()) / 2.f,\r\n\t\t\t\t\t\t(v1Pos->y() + v2Pos->y()) / 2.f,\r\n\t\t\t\t\t\t(v1Pos->z() + v2Pos->z()) / 2.f);\r\n\t\t\t\t\tnewColor = sibr::Vector3f((v1Color->x() + v2Color->x()) / 2.f,\r\n\t\t\t\t\t\t(v1Color->y() + v2Color->y()) / 2.f,\r\n\t\t\t\t\t\t(v1Color->z() + v2Color->z()) / 2.f);\r\n\t\t\t\t\tnewNormal = sibr::Vector3f((v1Normal->x() + v2Normal->x()) / 2.f,\r\n\t\t\t\t\t\t(v1Normal->y() + v2Normal->y()) / 2.f,\r\n\t\t\t\t\t\t(v1Normal->z() + v2Normal->z()) / 2.f);\r\n\t\t\t\t\tnewTexCoord = sibr::Vector2f((v1TexCoords->x() +\r\n\t\t\t\t\t\tv2TexCoords->x()) / 2.f,\r\n\t\t\t\t\t\t(v1TexCoords->y() + v2TexCoords->y()) / 2.f);\r\n\r\n\t\t\t\t\tnewVertices.push_back(newVertex);\r\n\r\n\t\t\t\t\tif (hasColors()) {\r\n\t\t\t\t\t\tnewColors.push_back(newColor);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasNormals()) {\r\n\t\t\t\t\t\tnewNormals.push_back(newNormal);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasTexCoords()) {\r\n\t\t\t\t\t\tnewTexCoords.push_back(newTexCoord);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (hasMeshIds()) {\r\n\t\t\t\t\t\t// Use the first referenced vertex as the provoking vertex.\r\n\t\t\t\t\t\tnewMeshIds.push_back(meshIds().at(t.x()));\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tint newIndexVertex = static_cast<int> (newVertices.size()) - 1;\r\n\t\t\t\t\tif (i < matIds().size()) {\r\n\t\t\t\t\t\tnewMatIds.push_back(tMatId);\r\n\t\t\t\t\t\tnewMatIds.push_back(tMatId);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (longestSide == 0) {\r\n\t\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.x(),\r\n\t\t\t\t\t\t\tnewIndexVertex,\r\n\t\t\t\t\t\t\tt.z()));\r\n\t\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(newIndexVertex,\r\n\t\t\t\t\t\t\tt.y(),\r\n\t\t\t\t\t\t\tt.z()));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (longestSide == 1) {\r\n\t\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.x(),\r\n\t\t\t\t\t\t\tt.y(),\r\n\t\t\t\t\t\t\tnewIndexVertex));\r\n\t\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.x(),\r\n\t\t\t\t\t\t\tnewIndexVertex,\r\n\t\t\t\t\t\t\tt.z()));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (longestSide == 2) {\r\n\t\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(t.x(),\r\n\t\t\t\t\t\t\tt.y(),\r\n\t\t\t\t\t\t\tnewIndexVertex));\r\n\t\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(newIndexVertex,\r\n\t\t\t\t\t\t\tt.y(),\r\n\t\t\t\t\t\t\tt.z()));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\tnewTriangles.push_back(t);\r\n\t\t\t\t\tif (i < matIds().size())\r\n\t\t\t\t\t\tnewMatIds.push_back(tMatId);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tvertices(newVertices);\r\n\t\t\tcolors(newColors);\r\n\t\t\tnormals(newNormals);\r\n\t\t\ttexCoords(newTexCoords);\r\n\t\t\ttriangles(newTriangles);\r\n\t\t\tmatIds(newMatIds);\r\n\t\t\tmeshIds(newMeshIds);\r\n\t\t}\r\n\t\t// We can now subdivide the large triangles with sub-Triangles\r\n\t}\r\n\r\n\tvoid MaterialMesh::ambientOcclusion(const MaterialMesh::AmbientOcclusion & ao)\r\n\t{\r\n\t\tif (!_aoInitialized) {\r\n\r\n\t\t\t_ambientOcclusion = ao;\r\n\t\t\tcolors(_aoFunction(*this, 64));\r\n\t\t\tcreateSubMeshes();\r\n\t\t\tfloat averageDistance = 0.f;\r\n\t\t\tfor (sibr::Vector3u t : triangles()) {\r\n\r\n\t\t\t\tfloat maximumDistance = 0.f;\r\n\r\n\t\t\t\tsibr::Vector3f a = vertices().at(t.x());\r\n\t\t\t\tsibr::Vector3f b = vertices().at(t.y());\r\n\t\t\t\tsibr::Vector3f c = vertices().at(t.z());\r\n\r\n\t\t\t\tfloat d = distance(a, b);\r\n\t\t\t\tif (d > maximumDistance)  maximumDistance = d;\r\n\t\t\t\td = distance(b, c);\r\n\t\t\t\tif (d > maximumDistance)  maximumDistance = d;\r\n\t\t\t\td = distance(a, c);\r\n\t\t\t\tif (d > maximumDistance)  maximumDistance = d;\r\n\r\n\t\t\t\taverageDistance += maximumDistance;\r\n\t\t\t}\r\n\r\n\r\n\t\t\tconst auto areaHeronsFormula = [](sibr::Vector3f A, sibr::Vector3f B,\r\n\t\t\t\tsibr::Vector3f C) -> float {\r\n\t\t\t\tfloat a = distance(A, B);\r\n\t\t\t\tfloat b = distance(B, C);\r\n\t\t\t\tfloat c = distance(C, A);\r\n\t\t\t\treturn sqrtf((a + (b + c))*(c - (a - b))*(c + (a - b))*\r\n\t\t\t\t\t(a + (b - c))) / 4.f;\r\n\t\t\t};\r\n\r\n\t\t\tfloat averageArea = 0.f;\r\n\t\t\tfor (sibr::Vector3u t : triangles()) {\r\n\r\n\t\t\t\taverageArea += areaHeronsFormula(vertices().at(t.x()),\r\n\t\t\t\t\tvertices().at(t.y()),\r\n\t\t\t\t\tvertices().at(t.z()));\r\n\t\t\t}\r\n\r\n\r\n\r\n\t\t\taverageDistance /= triangles().size();\r\n\t\t\t_averageSize = averageDistance;\r\n\t\t\taverageArea /= triangles().size();\r\n\t\t\t_averageArea = averageArea;\r\n\t\t\tstd::cout << \"Average distance SIZE = \" << _averageSize << std::endl;\r\n\t\t\tstd::cout << \"Average distance SIZE = \" << _averageArea << std::endl;\r\n\t\t\t_aoInitialized = true;\r\n\t\t}\r\n\t\tif (ao.AttenuationDistance != _ambientOcclusion.AttenuationDistance) {\r\n\t\t\t_ambientOcclusion = ao;\r\n\t\t\tcolors(_aoFunction(*this, 64));\r\n\t\t\tcreateSubMeshes();\r\n\t\t}\r\n\t\tif (ao.SubdivideThreshold < _ambientOcclusion.SubdivideThreshold) {\r\n\t\t\t_ambientOcclusion = ao;\r\n\t\t\tsubdivideMesh(_ambientOcclusion.SubdivideThreshold);\r\n\t\t\tcolors(_aoFunction(*this, 64));\r\n\t\t\tcreateSubMeshes();\r\n\t\t}\r\n\t\t_ambientOcclusion = ao;\r\n\t}\r\n\r\n\r\n\tvoid\tMaterialMesh::initAlbedoTextures(void) {\r\n\r\n\t\t//Creates textures for albedo\r\n\t\tif (_albedoTexturesInitialized) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t_albedoTextures.resize(matId2Name().size());\r\n\t\t_idTextures.resize(matId2Name().size());\r\n\t\t_opacityTextures.resize(matId2Name().size());\r\n\t\t_idTexturesOpacity.resize(matId2Name().size());\r\n\t\tunsigned int i = 0;\r\n\t\tfor (auto it = matId2Name().begin();\r\n\t\t\tit != matId2Name().end();\r\n\t\t\t++it)\r\n\t\t{\r\n\t\t\tsibr::ImageRGBA::Ptr texturePtr = diffuseMap(*it);\r\n\t\t\tif (texturePtr) {\r\n\t\t\t\t_albedoTextures[i] = std::shared_ptr<sibr::Texture2DRGBA>(\r\n\t\t\t\t\tnew sibr::Texture2DRGBA(*texturePtr,SIBR_GPU_LINEAR_SAMPLING));\r\n\t\t\t\t_idTextures[i] = _albedoTextures[i]->handle();\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\t_albedoTextures[i] = std::shared_ptr<sibr::Texture2DRGBA>(\r\n\t\t\t\t\tnew sibr::Texture2DRGBA());\r\n\t\t\t\t_idTextures[i] = _albedoTextures[i]->handle();\r\n\t\t\t}\r\n\r\n\t\t\tsibr::ImageRGB::Ptr texturePtrOpacity = opacityMap(*it);\r\n\t\t\tif (texturePtrOpacity && texturePtr) {\r\n\t\t\t\t_opacityTextures[i] = std::shared_ptr<sibr::Texture2DRGB>(\r\n\t\t\t\t\tnew sibr::Texture2DRGB(*texturePtrOpacity,SIBR_GPU_LINEAR_SAMPLING));\r\n\t\t\t\t_idTexturesOpacity[i] = _opacityTextures[i]->handle();\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\t_opacityTextures[i] = std::shared_ptr<sibr::Texture2DRGB>(\r\n\t\t\t\t\tnew sibr::Texture2DRGB());\r\n\t\t\t\t_idTexturesOpacity[i] = _opacityTextures[i]->handle();\r\n\t\t\t}\r\n\r\n\t\t\tif (_hasTagsCoveringFile && _tagsCoveringMaps[*it]) {\r\n\t\t\t\tsibr::ImageRGB::Ptr texturePtrTag = tagsCoveringMap(*it);\r\n\t\t\t\t_tagsCoveringTexture[*it] = std::shared_ptr<sibr::Texture2DRGB>(\r\n\t\t\t\t\tnew sibr::Texture2DRGB(*texturePtrTag,SIBR_GPU_LINEAR_SAMPLING));\r\n\t\t\t\t_idTagsCoveringTexture[*it] = _tagsCoveringTexture[*it]->handle();\r\n\t\t\t}\r\n\r\n\t\t\t_switchTags[*it] = false;\r\n\r\n\t\t\ti++;\r\n\t\t}\r\n\t\tif (_hasTagsFile) {\r\n\t\t\tsibr::ImageRGB::Ptr texturePtr = _tagsMap;\r\n\t\t\t_tagTexture = std::shared_ptr<sibr::Texture2DRGB>(\r\n\t\t\t\tnew sibr::Texture2DRGB(*texturePtr,SIBR_GPU_LINEAR_SAMPLING));\r\n\t\t\t_idTagTexture = _tagTexture->handle();\r\n\t\t}\r\n\r\n\r\n\t\t_albedoTexturesInitialized = true;\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::renderAlbedo(bool depthTest, bool backFaceCulling,\r\n\t\tRenderMode mode, bool frontFaceCulling, bool invertDepthTest,\r\n\t\tbool specificMaterial, std::string nameOfSpecificMaterial\r\n\t\t) const\r\n\t{\r\n\t\tif (_subMeshes.empty()) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tunsigned int i = 0;\r\n\t\tbool textureFound = false;\r\n\t\tstd::string  texName;\r\n\t\tfor (auto it = matId2Name().begin(); it != matId2Name().end() && !textureFound; ++it) {\r\n\r\n\t\t\tif (_albedoTextures[i] != nullptr) {\r\n\r\n\t\t\t\tsibr::ImageRGB::Ptr coveringTagImage = tagsCoveringMap(*it);\r\n\t\t\t\tif (_hasTagsCoveringFile && coveringTagImage\r\n\t\t\t\t\t&& tagsCoveringMaps().find(*it) != tagsCoveringMaps().end()) {\r\n\t\t\t\t\ttexName = *it;\r\n\t\t\t\t\ttextureFound = true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ti++;\r\n\t\t}\r\n\r\n\t\ti = 0;\r\n\t\tfor (auto it = matId2Name().begin(); it != matId2Name().end(); ++it)\r\n\t\t{\r\n\t\t\tif (!specificMaterial || *it == nameOfSpecificMaterial)\r\n\t\t\t\tif (_albedoTextures[i] != nullptr) {\r\n\t\t\t\t\tglActiveTexture(GL_TEXTURE0);\r\n\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, _idTextures[i]);\r\n\r\n\t\t\t\t\tsibr::ImageRGB::Ptr coveringTagImage = tagsCoveringMap(*it);\r\n\t\t\t\t\tif (_hasTagsCoveringFile && coveringTagImage\r\n\t\t\t\t\t\t&& tagsCoveringMaps().find(*it) != tagsCoveringMaps().end()) {\r\n\t\t\t\t\t\tglActiveTexture(GL_TEXTURE1);\r\n\t\t\t\t\t\tif (_switchTags.find(*it) != _switchTags.end() && _switchTags.at(*it)\r\n\t\t\t\t\t\t\t&& _hasTagsFile && _tagTexture)\r\n\t\t\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, _idTagTexture);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, _idTagsCoveringTexture.at(*it));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (_hasTagsFile && _tagTexture != nullptr) {\r\n\r\n\t\t\t\t\t\tglActiveTexture(GL_TEXTURE1);\r\n\t\t\t\t\t\tif (_switchTags.find(*it) != _switchTags.end() && _switchTags.at(*it)\r\n\t\t\t\t\t\t\t&& _idTagsCoveringTexture.size() > 0)\r\n\t\t\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, _idTagsCoveringTexture.at(texName));\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, _idTagTexture);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tglActiveTexture(GL_TEXTURE2);\r\n\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, _idTexturesOpacity[i]);\r\n\t\t\t\t\t_subMeshes[i].render(depthTest, backFaceCulling, mode,\r\n\t\t\t\t\t\tfrontFaceCulling, invertDepthTest);\r\n\t\t\t\t}\r\n\t\t\ti++;\r\n\t\t}\r\n\r\n\t\t\r\n\t}\r\n\r\n\r\n\tvoid\tMaterialMesh::renderThreeSixty(bool depthTest, bool backFaceCulling,\r\n\t\tRenderMode mode, bool frontFaceCulling, bool invertDepthTest) const\r\n\t{\r\n\r\n\t\tMesh::render(depthTest, backFaceCulling, mode, frontFaceCulling,\r\n\t\t\tinvertDepthTest, true);\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::render(bool depthTest, bool backFaceCulling,\r\n\t\tRenderMode mode, bool frontFaceCulling, bool invertDepthTest,\r\n\t\tbool tessellation, bool adjacency) const\r\n\t{\r\n\t\tif (_typeOfRender == RenderCategory::classic)\r\n\t\t{\r\n\t\t\tMesh::render(depthTest, backFaceCulling, mode, frontFaceCulling,\r\n\t\t\t\tinvertDepthTest, adjacency);\r\n\t\t}\r\n\t\telse if (_typeOfRender == RenderCategory::diffuseMaterials)\r\n\t\t{\r\n\t\t\trenderAlbedo(depthTest, backFaceCulling, mode, frontFaceCulling,\r\n\t\t\t\tinvertDepthTest);\r\n\t\t}\r\n\t\telse if (_typeOfRender == RenderCategory::threesixtyMaterials ||\r\n\t\t\t_typeOfRender == RenderCategory::threesixtyDepth)\r\n\t\t{\r\n\t\t\trenderThreeSixty(depthTest, backFaceCulling, mode, frontFaceCulling,\r\n\t\t\t\tinvertDepthTest);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::merge(const MaterialMesh& other)\r\n\t{\r\n\r\n\t\tif (_vertices.empty())\r\n\t\t{\r\n\t\t\tthis->operator = (other);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst size_t oldVerticesCount = vertices().size();\r\n\t\tconst bool thisHasIds = hasMeshIds();\r\n\r\n\t\tsibr::Mesh::merge(other);\r\n\r\n\t\tuint\t\tmatIdsOffset = static_cast<unsigned int> (_matId2Name.size());\r\n\t\tMatIds\t\tmatIds = other.matIds();\r\n\t\tMatId2Name\tmatId2Name;\r\n\r\n\t\tunsigned int nbOfSimilarity = 0;\r\n\t\tfor (unsigned int i = 0; i < other.matId2Name().size(); ++i) {\r\n\t\t\tbool foundSimilarity = false;\r\n\t\t\tunsigned int indexSimilarMaterial = 0;\r\n\t\t\tfor (unsigned int j = 0; j < _matId2Name.size()\r\n\t\t\t\t&& !foundSimilarity; ++j) {\r\n\r\n\t\t\t\tif (other.matId2Name().at(i).compare(_matId2Name.at(j)) == 0) {\r\n\t\t\t\t\t//We find a similar material present on the two meshes\r\n\t\t\t\t\t//Now we modify all triangles ids corresponding to this \r\n\t\t\t\t\t// material\t\r\n\t\t\t\t\tfoundSimilarity = true;\r\n\t\t\t\t\tnbOfSimilarity++;\r\n\t\t\t\t\tindexSimilarMaterial = j;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!foundSimilarity) {\r\n\t\t\t\t//It's a new material.\r\n\t\t\t\t//We have found a new material, We will merge it in our list\r\n\t\t\t\t//of materials later\r\n\t\t\t\tmatId2Name.push_back(other.matId2Name().at(i));\r\n\t\t\t\t//We substract the number of similarity to avoid\r\n\t\t\t\t//the \"gap\" about the materials index\r\n\t\t\t\tfor (unsigned int j = 0; j < other.matIds().size(); ++j) {\r\n\t\t\t\t\tunsigned int id = other.matIds().at(j);\r\n\t\t\t\t\tif (id == i) {\r\n\t\t\t\t\t\tmatIds[j] = id + matIdsOffset - nbOfSimilarity;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tfor (unsigned int j = 0; j < other.matIds().size(); ++j) {\r\n\t\t\t\t\tunsigned int id = other.matIds().at(j);\r\n\t\t\t\t\tif (id == i) {\r\n\t\t\t\t\t\tmatIds[j] = indexSimilarMaterial;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t_matIds.insert(_matIds.end(), matIds.begin(), matIds.end());\r\n\t\t_matId2Name.insert(_matId2Name.end(), matId2Name.begin(),\r\n\t\t\tmatId2Name.end());\r\n\t\t_opacityMaps.insert(other.opacityMaps().begin(),\r\n\t\t\tother.opacityMaps().end());\r\n\t\t_diffuseMaps.insert(other.diffuseMaps().begin(),\r\n\t\t\tother.diffuseMaps().end());\r\n\r\n\t\t// We have to shift all meshes ids.\r\n\t\tconst bool otherHasIds = other.hasMeshIds();\r\n\t\tif (thisHasIds && otherHasIds) {\r\n\t\t\t// Shift all other IDs by _maxMeshId+1.\r\n\t\t\t_maxMeshId += 1;\r\n\t\t\tMaterialMesh::MeshIds oIds(other.meshIds());\r\n\t\t\tconst int shift = int(_maxMeshId);\r\n\t\t\tfor (size_t vid = 0; vid < oIds.size(); ++vid) {\r\n\t\t\t\toIds[vid] = shift + oIds[vid];\r\n\t\t\t}\r\n\t\t\t_meshIds.insert(_meshIds.end(), oIds.begin(), oIds.end());\r\n\t\t\t_maxMeshId += other._maxMeshId;\r\n\r\n\r\n\t\t}\r\n\t\telse if (thisHasIds) {\r\n\t\t\t// In that case other has no IDs.\r\n\t\t\t_maxMeshId += 1;\r\n\t\t\tMaterialMesh::MeshIds newMeshIds(other.vertices().size(), int(_maxMeshId));\r\n\t\t\t_meshIds.insert(_meshIds.end(), newMeshIds.begin(), newMeshIds.end());\r\n\r\n\t\t}\r\n\t\telse if (otherHasIds) {\r\n\t\t\t// in that case give a new ID to the current mesh and insert the other IDs.\r\n\t\t\t_maxMeshId = other._maxMeshId + 1;\r\n\t\t\t_meshIds = MaterialMesh::MeshIds(oldVerticesCount, int(_maxMeshId));\r\n\t\t\t_meshIds.insert(_meshIds.end(), other.meshIds().begin(), other.meshIds().end());\r\n\r\n\t\t}\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::makeWhole(void)\r\n\t{\r\n\t\tsibr::Mesh::makeWhole();\r\n\t\tif (!hasMatIds()) {\r\n\t\t\t_matIds = MatIds(triangles().size(), 0);\r\n\t\t\t_matIdsVertices = MatIds(vertices().size(), 0);\r\n\t\t\t_matId2Name.push_back(\"emptyMat\");\r\n\t\t}\r\n\t\tif (!hasMeshIds()) {\r\n\t\t\t_meshIds = MatIds(vertices().size(), 0);\r\n\t\t\t_maxMeshId = 0;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::createSubMeshes(void) {\r\n\r\n\t\t_subMeshes.clear();\r\n\r\n\t\tfor (unsigned int i = 0; i < _matId2Name.size(); i++)\r\n\t\t{\r\n\t\t\t_subMeshes.push_back(generateSubMaterialMesh(i));\r\n\t\t}\r\n\t}\r\n\r\n\tsibr::MaterialMesh::Ptr MaterialMesh::invertedFacesMesh2() const\r\n\t{\r\n\t\tconst auto invertedFacesMesh = sibr::Mesh::invertedFacesMesh2();\r\n\t\tauto invertedFacesMaterialMesh = std::make_shared<MaterialMesh>\r\n\t\t\t(*invertedFacesMesh);\r\n\t\t// If we have some mesh IDs, just clone them as-is, no need for doubling.\r\n\t\tif (hasMeshIds()) {\r\n\t\t\tinvertedFacesMaterialMesh->meshIds(meshIds());\r\n\t\t}\r\n\r\n\t\tconst int nVertices = (int)vertices().size();\r\n\t\tconst int nTriangles = (int)triangles().size();\r\n\r\n\t\tMesh::Triangles Ntriangles(2 * nTriangles);\r\n\t\tMaterialMesh::MatIds NmatIds(hasMatIds() ? (2 * nTriangles) : 0);\r\n\r\n\t\tint v_id = 0;\r\n\t\tsibr::Vector3u shift(nVertices, nVertices, nVertices);\r\n\t\tint t_id = 0;\r\n\t\tfor (const auto & t : triangles()) {\r\n\t\t\tNtriangles[t_id] = t;\r\n\t\t\tNtriangles[t_id + nTriangles] = t.yxz() + shift;\r\n\t\t\t++t_id;\r\n\t\t}\r\n\t\tinvertedFacesMaterialMesh->triangles(Ntriangles);\r\n\r\n\t\tif (hasMatIds()) {\r\n\t\t\tint m_id = 0;\r\n\t\t\tfor (const auto & m : matIds()) {\r\n\t\t\t\tNmatIds[m_id] = m;\r\n\t\t\t\tNmatIds[m_id + nTriangles] = m;\r\n\t\t\t\t++m_id;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tinvertedFacesMaterialMesh->matIds(NmatIds);\r\n\t\tinvertedFacesMaterialMesh->matId2Name(_matId2Name);\r\n\t\tinvertedFacesMaterialMesh->opacityMaps(_opacityMaps);\r\n\t\tinvertedFacesMaterialMesh->diffuseMaps(_diffuseMaps);\r\n\r\n\t\treturn invertedFacesMaterialMesh;\r\n\t}\r\n\r\n\tvoid MaterialMesh::addEnvironmentMap(float* forcedCenterX,\r\n\t\tfloat* forcedCenterY,\r\n\t\tfloat* forcedCenterZ,\r\n\t\tfloat* forcedRadius)\r\n\t{\r\n\t\tsibr::Vector3f center;\r\n\t\tfloat radius;\r\n\t\tgetBoundingSphere(center, radius);\r\n\r\n\t\tif (forcedCenterX) center.x() = *forcedCenterX;\r\n\t\tif (forcedCenterY) center.y() = *forcedCenterY;\r\n\t\tif (forcedCenterZ) center.z() = *forcedCenterZ;\r\n\t\tif (forcedRadius) radius = *forcedRadius;\r\n\r\n\t\t//std::vector<std::string> partsOfSphere;\r\n\t\tstd::vector<PartOfSphere> partsOfSphere = { PartOfSphere::BOTTOM, PartOfSphere::UP };\r\n\t\t//partsOfSphere.push_back(\"bottom\");\r\n\t\t//partsOfSphere.push_back(\"up\");\r\n\r\n\t\tfor (PartOfSphere part : partsOfSphere) {\r\n\t\t\tstd::shared_ptr<Mesh> pSphere = getEnvSphere(center, radius,\r\n\t\t\t\tVector3f(0.f, 1.f, 0.f),\r\n\t\t\t\tVector3f(1.f, 0.f, 0.f),\r\n\t\t\t\tpart\r\n\t\t\t);\r\n\r\n\t\t\tsibr::MaterialMesh sphere(*pSphere);\r\n\r\n\t\t\tMatId2Name materialNames;\r\n\t\t\tMatIds materialIds;\r\n\r\n\t\t\tstd::string matName;\r\n\t\t\tif (part == PartOfSphere::BOTTOM)\r\n\t\t\t\tmatName = std::string(\"SibrSkyEmissivebottom\");\r\n\t\t\telse\r\n\t\t\t\tmatName = std::string(\"SibrSkyEmissiveup\");\r\n\t\t\tmaterialNames.push_back(matName);\r\n\r\n\t\t\tstd::vector<int> matIdsSphere;\r\n\t\t\tfor (unsigned int i = 0; i < sphere.triangles().size(); ++i) {\r\n\t\t\t\tmatIdsSphere.push_back(0);\r\n\t\t\t}\r\n\t\t\tsphere.matId2Name(materialNames);\r\n\t\t\tsphere.matIds(matIdsSphere);\r\n\r\n\t\t\tconst sibr::ImageRGBA::Pixel color(0,\r\n\t\t\t\t255,\r\n\t\t\t\t255,\r\n\t\t\t\t255);\r\n\t\t\tsibr::ImageRGBA::Ptr textureDiffuse(new sibr::ImageRGBA(1, 1, color));\r\n\t\t\t_diffuseMaps[matName] = textureDiffuse;\r\n\r\n\t\t\tconst sibr::ImageRGB::Pixel opacityAlpha(255, 255, 255);\r\n\t\t\tsibr::ImageRGB::Ptr textureOpacity(new sibr::ImageRGB(\r\n\t\t\t\t1, 1, opacityAlpha));\r\n\t\t\t_opacityMaps[matName] = textureOpacity;\r\n\t\t\tsphere.generateNormals();\r\n\t\t\tmerge(sphere);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/MaterialMesh.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <vector>\r\n# include <map>\r\n# include <sstream>\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n# include \"core/graphics/Image.hpp\"\r\n# include \"core/graphics/Mesh.hpp\"\r\n# include \"core/graphics/MeshBufferGL.hpp\"\r\n# include \"core/graphics/Texture.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t\r\n\t/** Store both CPU and GPU data for a geometric mesh.\r\n\t\tSpecifically designed for synthetic scenes with material information.\r\n\t\tProvide many processing and display methods.\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tclass SIBR_GRAPHICS_EXPORT MaterialMesh : public sibr::Mesh\r\n\t{\r\n\tpublic:\r\n\t\ttypedef std::vector<int>\t\t\t\t\t\t\t\tMatIds;\r\n\t\ttypedef std::vector<int>\t\t\t\t\t\t\t\tMeshIds;\r\n\t\ttypedef std::vector<std::string>\t\t\t\t\t\tMatId2Name;\r\n\r\n\t\ttypedef std::map<std::string, sibr::ImageRGB::Ptr>\t\tOpacityMaps;\r\n\t\ttypedef std::map<std::string, sibr::ImageRGBA::Ptr>\t\tDiffuseMaps;\r\n\r\n\t\ttypedef sibr::ImageRGB::Ptr\t\t\t\t\t\t\t\tTagsMap;\r\n\t\ttypedef std::map<std::string, sibr::ImageRGB::Ptr>\t\tTagsCoveringMaps;\r\n\r\n\t\ttypedef std::vector<Mesh>\t\t\t\t\t\t\t\tSubMeshes;\r\n\t\ttypedef std::vector<sibr::ImageRGBA>\t\t\t\t\tAlbedoTextures;\r\n\r\n\t\ttypedef std::map<std::string, bool>\t\t\t\t\t\tSwitchTagsProperty;\r\n\r\n\t\tSIBR_CLASS_PTR(MaterialMesh);\r\n\r\n\t\t/** Synthetic data rendering options. */\r\n\t\tenum class RenderCategory\r\n\t\t{\r\n\t\t\tclassic,\r\n\t\t\tdiffuseMaterials,\r\n\t\t\tthreesixtyMaterials,\r\n\t\t\tthreesixtyDepth\r\n\t\t};\r\n\r\n\t\t/** Ambient occlusion options. */\r\n\t\tstruct AmbientOcclusion {\r\n\t\t\tbool AoIsActive = false;\r\n\t\t\tfloat AttenuationDistance = 1.f;\r\n\t\t\tfloat IlluminanceCoefficient = 1.f;\r\n\t\t\tfloat SubdivideThreshold = 10.f;\r\n\t\t};\r\n\t\ttypedef struct AmbientOcclusion AmbientOcclusion;\r\n\r\n\t\tstd::string vertexShaderAlbedo =\r\n\t\t\t\"#version 450\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\r\n\t\t\t\"layout(location = 1) in vec3 in_colors;\t\t\t\\n\"\r\n\t\t\t\"layout(location = 2) in vec2 in_uvCoords;\t\t\t\\n\"\r\n\t\t\t\"layout(location = 3) in vec3 in_normal;\t\t\t\\n\"\r\n\t\t\t\"layout(location = 4) in float in_ao;\t\t\t\\n\"\r\n\t\t\t\"//layout(location = 4) in float in_material;\t\t\t\\n\"\r\n\t\t\t\"layout (location = 2) out vec2 uvCoords;\t\t\t\\n\"\r\n\t\t\t\"//out float material;\t\t\t\\n\"\r\n\t\t\t\"layout (location = 3) out vec3 normal;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"out float ao ;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"out vec3 pos_vertex;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 1) out vec3 colors;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform mat4 MVP;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform bool lightIsPresent;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform vec3 lightPos;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tnormal = in_normal;\t\t\\n\"\r\n\t\t\t\"\tao = in_ao;\t\t\\n\"\r\n\t\t\t\"\tuvCoords = in_uvCoords;\t\t\\n\"\r\n\t\t\t\"\tcolors= in_colors;\t\t\\n\"\r\n\t\t\t\"\tpos_vertex= in_vertex;\t\t\\n\"\r\n\t\t\t\"\t//material= float(in_material);\t\t\\n\"\r\n\t\t\t\"\tgl_Position = MVP*vec4(in_vertex,1) ;\t\t\\n\"\r\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\t\tstd::string fragmentShaderAlbedo =\r\n\t\t\t\"#version 450\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout(binding = 0) uniform sampler2D tex;\t\t\t\t\\n\"\r\n\t\t\t\"layout(binding = 2) uniform sampler2D opacity;\t\t\t\t\\n\"\r\n\t\t\t\"uniform int layer;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform bool AoIsActive;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform vec2 grid;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float IlluminanceCoefficient;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform bool lightIsPresent;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float scaleTags;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float intensityLight;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform vec3 lightPos;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 2) in vec2 uvCoords;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 3) in vec3 normal ;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 1) in vec3 colors;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tvec4 opacityColor;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tvec3 colorsModified = colors;\\n\"\r\n\t\t\t\"\tfloat lighter_ao = colors.x * IlluminanceCoefficient; \\n\"\r\n\t\t\t\"\tif (lighter_ao > 1.f ) lighter_ao = 1.f;\\n\"\r\n\t\t\t\"\tcolorsModified.x = lighter_ao;\\n\"\r\n\t\t\t\"\tcolorsModified.y = lighter_ao;\\n\"\r\n\t\t\t\"\tcolorsModified.z = lighter_ao;\\n\"\r\n\t\t\t\"\topacityColor = texture(opacity,vec2(uvCoords.x,1.0-uvCoords.y));\\n\"\r\n\t\t\t\"\tif (opacityColor.x < 0.1f && opacityColor.y < 0.1f && opacityColor.z < 0.1f ) discard;\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tout_color = texture(tex,vec2(uvCoords.x,1.0-uvCoords.y));\\n\"\r\n\t\t\t//\"\tif (out_color.a != 0.f ) discard; \\n\"\r\n\t\t\t//\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tif (AoIsActive ) {\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tout_color = out_color * vec4(colorsModified,1);\\n}\"\r\n\t\t\t\"\tout_color = vec4(out_color.x,out_color.y,out_color.z,out_color.a);\\n\"\r\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\r\n\t\tstd::string fragmentShaderAlbedoTag =\r\n\t\t\t\"#version 450\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout(binding = 0) uniform sampler2D tex;\t\t\t\t\\n\"\r\n\t\t\t\"layout(binding = 1) uniform sampler2D tags;\t\t\t\t\\n\"\r\n\t\t\t\"layout(binding = 2) uniform sampler2D opacity;\t\t\t\t\\n\"\r\n\t\t\t\"uniform int layer;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float scaleTags;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform bool AoIsActive;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform vec2 grid;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float IlluminanceCoefficient;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform bool lightIsPresent;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float intensityLight;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform vec3 lightPos;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 2) in vec2 uvCoords;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 3) in vec3 normal ;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout (location = 1) in vec3 colors;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"in vec3 pos_vertex;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tvec4 opacityColor;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tvec3 colorsModified = colors;\\n\"\r\n\t\t\t\"\tfloat lighter_ao = colors.x * IlluminanceCoefficient; \\n\"\r\n\t\t\t\"\tif (lighter_ao >= 1.f ) lighter_ao = 1.f;\\n\"\r\n\t\t\t\"\tcolorsModified.x = lighter_ao;\\n\"\r\n\t\t\t\"\tcolorsModified.y = lighter_ao;\\n\"\r\n\t\t\t\"\tcolorsModified.z = lighter_ao;\\n\"\r\n\t\t\t\"\topacityColor = texture(opacity,vec2(uvCoords.x,1.0-uvCoords.y));\\n\"\r\n\t\t\t\"\tif (opacityColor.x < 0.1f || opacityColor.y < 0.1f || opacityColor.z < 0.1f ) discard;\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tout_color = texture(tex,vec2(uvCoords.x,1.0-uvCoords.y));\\n\"\r\n\t\t\t\"\tif (out_color.a < 0.1f ) discard; \\n\"\r\n\t\t\t//\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tout_color = texture(tags,vec2((uvCoords.x)*scaleTags,(1.0-(uvCoords.y))*scaleTags));\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tif (out_color.x == 1.f && out_color.y == 1.f && out_color.z == 1.f)\t\t\\n\"\r\n\t\t\t\"\tout_color = texture(tex,vec2(uvCoords.x,1.0-uvCoords.y));\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tfloat coeffLight = 1.f;\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tif( lightIsPresent) {\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\t\t\tvec3 vertexToLight = normalize( lightPos - pos_vertex );\\n\"\r\n\t\t\t\"\t\t\t\tcoeffLight = abs(intensityLight*dot( vertexToLight, normal )) ; \\n\"\r\n\t\t\t//\"\t\t\t\tcoeffLight = max(0.0,powerLight* dot( vertexToLight, normal )) ; \\n\"\r\n\t\t\t\"\t\t\t\tcoeffLight = 0.50+coeffLight/2.0 ; \\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t}\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tif (AoIsActive ) {\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tout_color = out_color * vec4(colorsModified,1);\\n}\"\r\n\t\t\t\"\tout_color = out_color * vec4(coeffLight,coeffLight,coeffLight,1);\\n\"\r\n\t\t\t\"\tout_color = vec4(out_color.x,out_color.y,out_color.z,out_color.a);\\n\"\r\n\t\t\t//\"\tif (out_color.x < 0.01f && out_color.y < 0.01f && out_color.z < 0.01f) discard;\t\t\\n\"\r\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\tpublic:\r\n\r\n\t\t/** Constructor.\r\n\t\t\\param withGraphics init associated OpenGL buffers object (requires an openGL context)\r\n\t\t*/\r\n\t\tMaterialMesh(bool withGraphics = true) : Mesh(withGraphics) {\r\n\t\t}\r\n\r\n\t\t/** Constructor from a basic mesh.\r\n\t\t\\param mesh the mesh to copy\r\n\t\t*/\r\n\t\tMaterialMesh(sibr::Mesh& mesh) : Mesh(mesh) {}\r\n\r\n\t\t/** Set material IDs (per triangle)\r\n\t\t\\param matIds the new ids\r\n\t\t*/\r\n\t\tinline void\tmatIds(const MatIds& matIds);\r\n\t\t\r\n\t\t/** \\return a reference to the per-triangle material IDs. */\r\n\t\tinline const MatIds& matIds(void) const;\r\n\t\t\r\n\t\t/** \\return a reference to the per-vertex material IDs. */\r\n\t\tinline const MatIds& matIdsVertices(void) const;\r\n\r\n\t\t/** \\return true if each triangle has a material ID assigned. */\r\n\t\tinline bool\thasMatIds(void) const;\r\n\r\n\t\t/** \\return the mapping between IDs and material names. */\r\n\t\tinline const MatId2Name& matId2Name(void) const;\r\n\t\t\r\n\t\t/** Set the mapping between IDs and material names.\r\n\t\t\\param matId2Name the new mapping\r\n\t\t*/\r\n\t\tinline void matId2Name(const MatId2Name& matId2Name);\r\n\r\n\t\t/** Set the mesh ID of each vertex.\r\n\t\t\\param meshIds the new ids\r\n\t\t*/\r\n\t\tinline void meshIds(const MeshIds& meshIds);\r\n\t\t\r\n\t\t/** \\return a reference to the per-vertex mesh IDs. */\r\n\t\tinline const MeshIds& meshIds(void) const;\r\n\t\t\r\n\t\t/** \\return true if source mesh information is available for each vertex. */\r\n\t\tinline bool hasMeshIds(void) const;\r\n\r\n\t\t/** Query a material opacity map.\r\n\t\t\\param matName the material name\r\n\t\t\\return the opacity texture if it exist\r\n\t\t*/\r\n\t\tinline sibr::ImageRGB::Ptr opacityMap(const std::string& matName) const;\r\n\t\t\r\n\t\t/** Set all material opacity maps.\r\n\t\t\\param maps the new maps\r\n\t\t*/\r\n\t\tinline void opacityMaps(const OpacityMaps & maps);\r\n\t\t\r\n\t\t/** \\return all opacity maps. */\r\n\t\tinline const OpacityMaps& opacityMaps(void) const;\r\n\r\n\t\t/// Set the switchTag \r\n\t\tinline void switchTag(const SwitchTagsProperty& switchTag);\r\n\t\t/// get the switchTag \r\n\t\tinline const SwitchTagsProperty& switchTag(void) const;\r\n\r\n\t\t/// Return the pointer to oppacity texture if it exist\r\n\t\t/** Query a material diffuse map.\r\n\t\t\\param matName the material name\r\n\t\t\\return the diffuse texture if it exist\r\n\t\t*/\r\n\t\tinline sibr::ImageRGBA::Ptr diffuseMap(const std::string& matName) const;\r\n\t\t\r\n\t\t/** Set all material diffuse maps.\r\n\t\t\\param maps the new maps\r\n\t\t*/\r\n\t\tinline void diffuseMaps(const DiffuseMaps & maps);\r\n\t\t\r\n\t\t/** \\return all diffuse maps. */\r\n\t\tinline const DiffuseMaps& diffuseMaps(void) const;\r\n\t\t\r\n\t\t/** Indicate if the mesh has an associated tag file (for calibration).\r\n\t\t\\param hasOrNot the flag\r\n\t\t*/\r\n\t\tinline void hasTagsFile(bool hasOrNot);\r\n\t\t\r\n\t\t/** \\return true if the mesh has an associated tag file. */\r\n\t\tinline const bool hasTagsFile(void) const;\r\n\t\t\r\n\t\t/** Set the tag map.\r\n\t\t\\param map the new map\r\n\t\t*/\r\n\t\tinline void tagsMap(const TagsMap & map);\r\n\t\t\r\n\t\t/** \\return the current tag map. */\r\n\t\tinline const TagsMap& tagsMap(void) const;\r\n\t\t\r\n\t\t/** Indicate if the mesh has an associated covering tag file (for calibration).\r\n\t\t\\param hasOrNot the flag\r\n\t\t*/\r\n\t\tinline void hasTagsCoveringFile(bool hasOrNot);\r\n\t\t\r\n\t\t/** \\return true if the mesh has an associated covering tag file. */\r\n\t\tinline const bool hasTagsCoveringFile(void) const;\r\n\t\t\r\n\t\t/** Set the covering tag map.\r\n\t\t\\param map the new map\r\n\t\t*/\r\n\t\tinline void tagsCoveringMaps(const TagsCoveringMaps & map);\r\n\t\t\r\n\t\t/** \\return the current covering tag map. */\r\n\t\tinline const TagsCoveringMaps& tagsCoveringMaps(void) const;\r\n\t\t/// Return the pointer to oppacity texture if it exist\r\n\t\tinline sibr::ImageRGB::Ptr tagsCoveringMap(const std::string& matName) const;\r\n\t\t\r\n\t\t/** Set the sub meshes.\r\n\t\t\\param subMeshes a list of submeshes\r\n\t\t*/\r\n\t\tinline void subMeshes(const SubMeshes& subMeshes);\r\n\t\t\r\n\t\t/** \\return the list of submeshes. */\r\n\t\tinline const SubMeshes& subMeshes(void) const;\r\n\r\n\t\t/** Set the synthetic rendering mode.\r\n\t\t\\param type the new mode\r\n\t\t*/\r\n\t\tinline void typeOfRender(const RenderCategory& type);\r\n\t\t\r\n\t\t/** \\return the current synthetic rendering mode. */ \r\n\t\tinline const RenderCategory& typeOfRender(void) const;\r\n\r\n\t\t/** Set the ambient occlusion options and compute AO values, storing them in the vertex colors.\r\n\t\t\\param ao the new options\r\n\t\t*/\r\n\t\tvoid ambientOcclusion(const AmbientOcclusion& ao);\r\n\r\n\t\t/** \\return the current ambient occlusion options. */\r\n\t\tinline const AmbientOcclusion& ambientOcclusion(void);\r\n\r\n\t\t/** Set the function used to compute ambient occlusion at each vertex. \r\n\t\t\\param aoFunction the new function to use \r\n\t\t*/\r\n\t\tinline void aoFunction(std::function<sibr::Mesh::Colors(\r\n\t\t\tsibr::MaterialMesh&,\r\n\t\t\tconst int)>& aoFunction);\r\n\r\n\t\t/** Load a mesh from the disk.\r\n\t\t\\param filename the file path\r\n\t\t\\return a success flag\r\n\t\t\\note Supports OBJ and PLY for now.\r\n\t\t*/\r\n\t\tbool\tload(const std::string& filename);\r\n\r\n\t\t/** Load a scene from a set of mitsuba XML scene files (referencing multiple OBJs/PLYs). \r\n\t\tIt handles instances (duplicating the geometry and applying the per-instance transformation).\r\n\t\t\\param xmlFile the file path\r\n\t\t\\param loadTextures should the material textures be loaded\r\n\t\t\\return a success flag\r\n\t\t*/\r\n\t\tbool\tloadMtsXML(const std::string& xmlFile, bool loadTextures = true);\r\n\r\n\t\t/*\r\n\t\tLoad tags image files from a list of file paths.\r\n\t\t\\param listFilesTags a list of image paths\r\n\t\t*/\r\n\t\tvoid\tloadCoveringTagsTexture(const std::vector<std::string>& listFilesTags);\r\n\r\n\t\t/** Attribute a random color at each vertex based on the material IDs of the faces it belongs to. */\r\n\t\tvoid\tfillColorsWithIndexMaterials();\r\n\r\n\t\t/** Store the material ID of each vertex in its color attribute (R: bits 0-7, G: 8-15, B: 16-23). */\r\n\t\tvoid\tfillColorsWithMatIds();\r\n\r\n\t\t/** Merge another mesh into this one.\r\n\t\t\\param other the mesh to merge\r\n\t\t\\sa makeWhole\r\n\t\t*/\r\n\t\tvoid\tmerge(const MaterialMesh& other);\r\n\r\n\t\t/** Make the mesh whole, ie it will have default values for all components (texture, materials, colors, etc)\r\n\t\t  It is useful when merging two meshes. If the second one is missing some attributes, the merging will break the mesh state if it isn't made whole.\r\n\t\t*/\r\n\t\tvoid\tmakeWhole(void);\r\n\r\n\t\t/** Split the mesh geometry in multiple submeshes based on each vertex material ID. */\r\n\t\tvoid\tcreateSubMeshes(void);\r\n\r\n\t\t/** \\return a copy of the mesh with \"doubled\" faces (obtained by merging the current mesh with a copy with inverted faces. */\r\n\t\tsibr::MaterialMesh::Ptr invertedFacesMesh2() const;\r\n\r\n\t\t/** Force upload of data to the GPU. */\r\n\t\tvoid\tforceBufferGLUpdate(void) const;\r\n\t\t\r\n\t\t/** Delete GPU mesh data. */\r\n\t\tvoid\tfreeBufferGLUpdate(void) const;\r\n\r\n\t\t/** Subdivide a mesh triangles until a triangle area threshold is reached.\r\n\t\t\\param threshold the maximum deviation from the average triangle area allowed\r\n\t\t*/\r\n\t\tvoid subdivideMesh2(float threshold);\r\n\r\n\t\t/** Subdivide a mesh triangles until an edge length threshold is reached.\r\n\t\t\\param threshold the maximum deviation from the average edge length allowed\r\n\t\t*/\r\n\t\tvoid\tsubdivideMesh(float threshold);\r\n\r\n\t\t/** Add an environment sphere to the mesh, surrounding the existing geometry.\r\n\t\t\\param forcedCenterX optional sphere center x coordinate\r\n\t\t\\param forcedCenterY optional sphere center y coordinate\r\n\t\t\\param forcedCenterZ optional sphere center z coordinate\r\n\t\t\\param forcedRadius optional sphere radius\r\n\t\t*/\r\n\t\tvoid addEnvironmentMap(float* forcedCenterX = nullptr,\r\n\t\t\tfloat* forcedCenterY = nullptr,\r\n\t\t\tfloat* forcedCenterZ = nullptr,\r\n\t\t\tfloat* forcedRadius = nullptr);\r\n\r\n\t\t/** Render the geometry using OpenGL.\r\n\t\t\\param depthTest should depth testing be performed\r\n\t\t\\param backFaceCulling should culling be performed\r\n\t\t\\param mode the primitives rendering mode\r\n\t\t\\param frontFaceCulling should the culling test be flipped\r\n\t\t\\param invertDepthTest should the depth test be flipped (GL_GREATER_THAN)\r\n\t\t\\param tessellation should the rendering call tesselation shaders\r\n\t\t\\param adjacency should we get adjacent triangles info in geometry shader\r\n\t\t*/\r\n\t\tvoid\trender(\r\n\t\t\tbool depthTest = true,\r\n\t\t\tbool backFaceCulling = true,\r\n\t\t\tRenderMode mode = FillRenderMode,\r\n\t\t\tbool frontFaceCulling = false,\r\n\t\t\tbool invertDepthTest = false,\r\n\t\t\tbool tessellation = false,\r\n\t\t\tbool adjacency = false\r\n\t\t) const;\r\n\r\n\t\t/** Render the geometry with albedo and tag textures.\r\n\t\t\\param depthTest should depth testing be performed\r\n\t\t\\param backFaceCulling should culling be performed\r\n\t\t\\param mode the primitives rendering mode\r\n\t\t\\param frontFaceCulling should the culling test be flipped\r\n\t\t\\param invertDepthTest should the depth test be flipped (GL_GREATER_THAN)\r\n\t\t\\param specificMaterial should we use a specific material\r\n\t\t\\param nameOfSpecificMaterial name of the specific material\r\n\t\t*/\r\n\t\tvoid\trenderAlbedo(\r\n\t\t\tbool depthTest = true,\r\n\t\t\tbool backFaceCulling = true,\r\n\t\t\tRenderMode mode = FillRenderMode,\r\n\t\t\tbool frontFaceCulling = false,\r\n\t\t\tbool invertDepthTest = false,\r\n\t\t\tbool specificMaterial = false,\r\n\t\t\tstd::string nameOfSpecificMaterial = \"\"\r\n\t\t) const;\r\n\r\n\t\t/** Render the geometry for 360 environment maps.\r\n\t\t\\param depthTest should depth testing be performed\r\n\t\t\\param backFaceCulling should culling be performed\r\n\t\t\\param mode the primitives rendering mode\r\n\t\t\\param frontFaceCulling should the culling test be flipped\r\n\t\t\\param invertDepthTest should the depth test be flipped (GL_GREATER_THAN)\r\n\t\t*/\r\n\t\tvoid\trenderThreeSixty(\r\n\t\t\tbool depthTest,\r\n\t\t\tbool backFaceCulling,\r\n\t\t\tRenderMode mode,\r\n\t\t\tbool frontFaceCulling,\r\n\t\t\tbool invertDepthTest\r\n\t\t) const;\r\n\r\n\t\t/** Upload the material textures to the GPU. */\r\n\t\tvoid\tinitAlbedoTextures(void);\r\n\r\n\t\t/** Generate a mesh containing all triangles with a given material.\r\n\t\t\\param material the material ID\r\n\t\t\\return the submesh\r\n\t\t*/\r\n\t\tMesh generateSubMaterialMesh(int material) const;\r\n\r\n\tprivate:\r\n\r\n\r\n\t\tMatIds\t\t_matIds; ///< Per triangle material ID.\r\n\t\tMatIds\t\t_matIdsVertices; ///< Per vertex material ID.\r\n\t\tMatId2Name\t_matId2Name; ///< ID to name material mapping.\r\n\r\n\t\tMeshIds\t\t_meshIds; ///< Per-vertex submesh ID.\r\n\t\tsize_t\t\t_maxMeshId = 0; ///< Maximum submesh ID encounter.\r\n\r\n\t\tOpacityMaps _opacityMaps; ///< Material opacity images.\r\n\t\tDiffuseMaps _diffuseMaps; ///< Material diffuse images.\r\n\r\n\t\t//std::vector<std::string>\t_uniformColorMtlList;\r\n\t\tTagsMap\t\t_tagsMap;  ///< Material tag images.\r\n\t\tTagsCoveringMaps _tagsCoveringMaps;  ///< Material covering tag images.\r\n\t\tstd::vector<std::string> uniformColorMtlList; ///< List of materials with a diffuse map.\r\n\r\n\t\tSubMeshes\t_subMeshes; ///< Submeshes, one per material, for rendering them separately.\r\n\t\tRenderCategory _typeOfRender = RenderCategory::diffuseMaterials; ///< Synthetic rendering mode.\r\n\r\n\t\tbool _albedoTexturesInitialized = false; ///< Are the texture initialized.\r\n\t\tstd::vector<sibr::Texture2DRGBA::Ptr> _albedoTextures; ///< Albedo textures.\r\n\t\tstd::vector<GLuint> _idTextures; ///< Texture handles.\r\n\t\tstd::vector<sibr::Texture2DRGB::Ptr> _opacityTextures;///< Opacity textures.\r\n\t\tstd::vector<GLuint> _idTexturesOpacity;///< Opacity texture handles.\r\n\r\n\t\tbool _hasTagsFile = false; ///< Is a tag file associated to the mesh.\r\n\t\tsibr::Texture2DRGB::Ptr _tagTexture; ///< Tag texture.\r\n\t\tGLuint _idTagTexture = 0; ///< Tag texture handle.\r\n\r\n\t\tbool _hasTagsCoveringFile = false; ///< Is a covering tag file associated to the mesh.\r\n\t\tsibr::Texture2DRGB::Ptr _tagCoveringTexture;///< Convering tag texture.\r\n\t\tGLuint _idTagCoveringTexture = 0; ///< Covering tag texture handle.\r\n\r\n\t\tstd::vector<sibr::ImageRGB::Ptr> _listCoveringImagesTags;\r\n\t\tstd::map<std::string,sibr::Texture2DRGB::Ptr> _tagsCoveringTexture;\r\n\t\tstd::map<std::string,GLuint> _idTagsCoveringTexture;\r\n\r\n\t\tSwitchTagsProperty _switchTags;\r\n\r\n\t\t//AO attributes\r\n\t\tfloat _currentThreshold;\r\n\t\tAmbientOcclusion _ambientOcclusion; ///< AO options.\r\n\t\tstd::function<sibr::Mesh::Colors(sibr::MaterialMesh&, const int)> _aoFunction; ///< AO generation function.\r\n\t\tbool _aoInitialized = false; ///< Is AO data initialized.\r\n\t\tfloat _averageSize = 0.0f; ///< Average maximum edge length.\r\n\t\tfloat _averageArea = 0.0f; ///< Average triangle area.\r\n\r\n\t};\r\n\r\n\t///// DEFINITION /////\r\n\r\n\r\n\r\n\tvoid\tMaterialMesh::matIds(const MatIds& matIds) {\r\n\t\t_matIds = matIds;\r\n\t}\r\n\tconst MaterialMesh::MatIds& MaterialMesh::matIds(void) const {\r\n\t\treturn _matIds;\r\n\t}\r\n\tbool\tMaterialMesh::hasMatIds(void) const {\r\n\t\treturn (_triangles.size() > 0 && _triangles.size() == _matIds.size());\r\n\t}\r\n\tconst MaterialMesh::MatIds& MaterialMesh::matIdsVertices(void) const {\r\n\t\treturn _matIdsVertices;\r\n\t}\r\n\tconst MaterialMesh::MatId2Name& MaterialMesh::matId2Name(void) const {\r\n\t\treturn _matId2Name;\r\n\t}\r\n\tvoid MaterialMesh::matId2Name(const MatId2Name & matId2Name)\r\n\t{\r\n\t\t_matId2Name = matId2Name;\r\n\t}\r\n\r\n\tvoid\tMaterialMesh::meshIds(const MeshIds& meshIds) {\r\n\t\t_meshIds = meshIds;\r\n\t}\r\n\tconst MaterialMesh::MeshIds& MaterialMesh::meshIds(void) const {\r\n\t\treturn _meshIds;\r\n\t}\r\n\tbool\tMaterialMesh::hasMeshIds(void) const {\r\n\t\treturn (!_meshIds.empty() && _meshIds.size() == _vertices.size());\r\n\t}\r\n\r\n\t// Opacity map function\r\n\tImageRGB::Ptr MaterialMesh::opacityMap(const std::string& matName) const\r\n\t{\r\n\t\tstd::map<std::basic_string<char>, sibr::ImagePtr<unsigned char, 3> >::const_iterator el = _opacityMaps.find(matName);\r\n\t\tif (el != _opacityMaps.end()) {\r\n\t\t\treturn el->second;\r\n\t\t}\r\n\t\treturn nullptr;\r\n\t}\r\n\tconst MaterialMesh::OpacityMaps& MaterialMesh::opacityMaps(void) const\r\n\t{\r\n\t\treturn _opacityMaps;\r\n\t}\r\n\r\n\tvoid MaterialMesh::hasTagsFile(bool hasOrNot) \r\n\t{\r\n\t\t_hasTagsFile = hasOrNot;\r\n\t}\r\n\r\n\tconst bool MaterialMesh::hasTagsFile(void) const \r\n\t{\r\n\t\treturn _hasTagsFile;\r\n\t}\r\n\r\n\tvoid MaterialMesh::hasTagsCoveringFile(bool hasOrNot) \r\n\t{\r\n\t\t_hasTagsCoveringFile = hasOrNot;\r\n\t}\r\n\r\n\tconst bool MaterialMesh::hasTagsCoveringFile(void) const \r\n\t{\r\n\t\treturn _hasTagsCoveringFile;\r\n\t}\r\n\r\n\tvoid MaterialMesh::opacityMaps(const OpacityMaps& maps)\r\n\t{\r\n\t\t_opacityMaps = maps;\r\n\t}\r\n\r\n\tvoid MaterialMesh::tagsMap(const TagsMap & map) {\r\n\t\t_tagsMap = map;\r\n\t}\r\n\r\n\tconst MaterialMesh::TagsMap& MaterialMesh::tagsMap(void) const {\r\n\t\treturn _tagsMap;\r\n\t}\r\n\r\n\tvoid MaterialMesh::tagsCoveringMaps(const TagsCoveringMaps & map) {\r\n\t\t_tagsCoveringMaps = map;\r\n\t}\r\n\r\n\tconst MaterialMesh::TagsCoveringMaps& MaterialMesh::tagsCoveringMaps(void) const {\r\n\t\treturn _tagsCoveringMaps;\r\n\t}\r\n\r\n\tsibr::ImageRGB::Ptr MaterialMesh::tagsCoveringMap(const std::string& matName) const {\r\n\t\tstd::map<std::basic_string<char>, sibr::ImagePtr<unsigned char, 3> >::const_iterator el = _tagsCoveringMaps.find(matName);\r\n\r\n\t\tif (el != _tagsCoveringMaps.end()) {\r\n\t\t\treturn el->second;\r\n\t\t}\r\n\t\telse return nullptr;\r\n\t}\r\n\r\n\t/// Set the switchTag \r\n\tvoid MaterialMesh::switchTag(const SwitchTagsProperty& switchTag) {\r\n\t\t_switchTags = switchTag;\r\n\t}\r\n\t/// get the switchTag \r\n\tconst MaterialMesh::SwitchTagsProperty& MaterialMesh::switchTag(void) const {\r\n\t\treturn _switchTags;\r\n\t}\r\n\r\n\tImageRGBA::Ptr MaterialMesh::diffuseMap(const std::string& matName) const\r\n\t{\r\n\t\tstd::map<std::basic_string<char>, sibr::ImagePtr<unsigned char, 4> >::const_iterator el = _diffuseMaps.find(matName);\r\n\r\n\t\tif (el != _diffuseMaps.end()) {\r\n\t\t\treturn el->second;\r\n\t\t}\r\n\t\telse return nullptr;\r\n\t}\r\n\r\n\t/*ImageRGB MaterialMesh::diffuseMap(const std::string& matName)\r\n\t{\r\n\t\tauto & el =_diffuseMaps.find(matName);\r\n\t\tif (el != _diffuseMaps.end()) {\r\n\t\t\treturn el->second;\r\n\t\t}\r\n\t}*/\r\n\r\n\tconst MaterialMesh::DiffuseMaps& MaterialMesh::diffuseMaps(void) const\r\n\t{\r\n\t\treturn _diffuseMaps;\r\n\t}\r\n\r\n\tvoid MaterialMesh::diffuseMaps(const DiffuseMaps& maps)\r\n\t{\r\n\t\t_diffuseMaps = maps;\r\n\t}\r\n\r\n\tconst MaterialMesh::SubMeshes& MaterialMesh::subMeshes(void) const\r\n\t{\r\n\t\treturn _subMeshes;\r\n\t}\r\n\r\n\r\n\tvoid MaterialMesh::subMeshes(const SubMeshes& subMeshes)\r\n\t{\r\n\t\t_subMeshes = subMeshes;\r\n\t}\r\n\r\n\tconst MaterialMesh::RenderCategory& MaterialMesh::typeOfRender(void) const {\r\n\t\treturn _typeOfRender;\r\n\t}\r\n\r\n\r\n\tinline const MaterialMesh::AmbientOcclusion & MaterialMesh::ambientOcclusion(void)\r\n\t{\r\n\t\treturn _ambientOcclusion;\r\n\t}\r\n\r\n\tinline void MaterialMesh::aoFunction(std::function<sibr::Mesh::Colors\r\n\t(sibr::MaterialMesh&, const int)>&\r\n\t\taoFunction)\r\n\t{\r\n\t\t_aoFunction = aoFunction;\r\n\t}\r\n\r\n\tvoid MaterialMesh::typeOfRender(const RenderCategory& type) {\r\n\t\t_typeOfRender = type;\r\n\t}\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Mesh.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <memory>\n#include <map>\n#include <queue>\n\n#include <assimp/Importer.hpp> // C++ importer interface\n#include <assimp/scene.h> // Output data structure\n#include <assimp/postprocess.h> // Post processing flags\n#include <assimp/cexport.h>\n#include <assimp/Exporter.hpp>\n\n#include \"core/system/ByteStream.hpp\"\n#include \"core/graphics/Mesh.hpp\"\n\n#include \"boost/filesystem.hpp\"\n#include \"core/system/XMLTree.h\"\n#include \"core/system/Matrix.hpp\"\n#include <set>\n#include <boost/variant/detail/substitute.hpp>\n#include \"core/assets/colmapheader.h\"\n\nnamespace sibr\n{\n\ttypedef uint32_t image_t;\n\ttypedef uint32_t camera_t;\n\ttypedef uint64_t point3D_t;\n\ttypedef uint32_t point2D_t;\n\n\tvoid ReadPoints3DBinary(const std::string& path, Mesh::Vertices& verts, Mesh::Colors& cols, int& numverts) {\n\t  std::ifstream file(path, std::ios::binary);\n\t//  CHECK(file.is_open()) << path;\n\n\t  const size_t num_points3D = ReadBinaryLittleEndian<uint64_t>(&file);\n\t  numverts = num_points3D;\n\t  std::cerr << \"Num 3D pts \" << num_points3D << std::endl;\n\t  for (size_t i = 0; i < num_points3D; ++i) {\n\t    //class Point3D point3D;\n\n\t    const uint64_t point3D_id = ReadBinaryLittleEndian<uint64_t>(&file);\n\t  //  num_added_points3D_ = std::max(num_added_points3D_, point3D_id);\n\n\t//    point3D.XYZ()(0) = ReadBinaryLittleEndian<double>(&file);\n\t//    point3D.XYZ()(1) = ReadBinaryLittleEndian<double>(&file);\n\t//    point3D.XYZ()(2) = ReadBinaryLittleEndian<double>(&file);\n\t//    point3D.Color(0) = ReadBinaryLittleEndian<uint8_t>(&file);\n\t//    point3D.Color(1) = ReadBinaryLittleEndian<uint8_t>(&file);\n\t//    point3D.Color(2) = ReadBinaryLittleEndian<uint8_t>(&file);\n\t//    point3D.SetError(ReadBinaryLittleEndian<double>(&file));\n\n\t\tdouble x = ReadBinaryLittleEndian<double>(&file);\n\t\tdouble y = ReadBinaryLittleEndian<double>(&file);\n\t\tdouble z = ReadBinaryLittleEndian<double>(&file);\n\t\tVector3f vert(x,y,z);\n\n\t\tverts.push_back(vert);\n\n\t\tfloat r = float(ReadBinaryLittleEndian<uint8_t>(&file))/255.f;\n\t\tfloat g = float(ReadBinaryLittleEndian<uint8_t>(&file))/255.f;\n\t\tfloat b = float(ReadBinaryLittleEndian<uint8_t>(&file))/255.f;\n\n\t\tVector3f c(r, g, b);\n\n\t\tcols.push_back(c);\n\t\tdouble err =  ReadBinaryLittleEndian<double>(&file);\n\n\t    const size_t track_length = ReadBinaryLittleEndian<uint64_t>(&file);\n\t    //std::cerr << \"Track length \" << track_length << std::endl;\n\t\t// read and include\n\t    for (size_t j = 0; j < track_length; ++j) {\n\t      const image_t image_id = ReadBinaryLittleEndian<image_t>(&file);\n\t      const point2D_t point2D_idx = ReadBinaryLittleEndian<point2D_t>(&file);\n\t      //point3D.Track().AddElement(image_id, point2D_idx);\n\t    }\n\t    //point3D.Track().Compress();\n\n\t    //points3D_.emplace(point3D_id, point3D);\n\t  }\n\t}\n\n\tvoid ReadPoints3DText(const std::string& path, Mesh::Vertices& verts, Mesh::Vertices& cols) {\n\t//  points3D_.clear();\n\t  std::ifstream file(path);\n\t//  CHECK(file.is_open()) << path;\n\t  std::string line;\n\t  std::string item;\n\t  while (std::getline(file, line)) {\n\t    StringTrim(&line);\n\t    if (line.empty() || line[0] == '#') {\n\t      continue;\n\t    }\n\t    std::stringstream line_stream(line);\n\t    // ID\n\t    std::getline(line_stream, item, ' ');\n\t    const point3D_t point3D_id = std::stoll(item);\n\n\t    // Make sure, that we can add new 3D points after reading 3D points\n\t    // without overwriting existing 3D points.\n\t    // num_added_points3D_ = std::max(num_added_points3D_, point3D_id);\n\n\t    // XYZ\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.XYZ(0) = \" << std::stold(item) << std::endl;\n\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.XYZ(1) = \" << std::stold(item) << std::endl;\n\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.XYZ(2) = \" << std::stold(item) << std::endl;\n\n\t    // Color\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.Color(0) = \" << static_cast<uint8_t>(std::stoi(item)) << std::endl;\n\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.Color(1) = \" << static_cast<uint8_t>(std::stoi(item)) << std::endl;\n\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.Color(2) = \" << static_cast<uint8_t>(std::stoi(item)) << std::endl;\n\n\t    // ERROR\n\t    std::getline(line_stream, item, ' ');\n\t    std::cerr << \"point3D.SetError(\" << std::stold(item) << std::endl;\n\n\t    // TRACK\n\t    while (!line_stream.eof()) {\n\t    //  TrackElement track_el;\n\n\t      std::getline(line_stream, item, ' ');\n\t      StringTrim(&item);\n\t      if (item.empty()) {\n\t\tbreak;\n\t      }\n\t      std::cerr << \"track_el.image_id = \" << std::stoul(item) << std::endl;\n\n\t      std::getline(line_stream, item, ' ');\n\t      std::cerr << \"track_el.point2D_idx = \" << std::stoul(item) << std::endl;\n\t//      point3D.Track().AddElement(track_el);\n\t    }\n\t //   point3D.Track().Compress();\n\t  //  points3D_.emplace(point3D_id, point3D);\n\t  }\n\t}\n\n\tMesh::Mesh(bool withGraphics) : _meshPath(\"\") {\n\t\tif (withGraphics) {\n\t\t\t_gl.bufferGL.reset(new MeshBufferGL);\n\t\t}\n\t\telse {\n\t\t\t_gl.bufferGL = nullptr;\n\t\t}\n\t}\n\n\tbool\t\tMesh::saveToObj(const std::string& filename)  const\n\t{\n\t\taiScene scene;\n\t\tscene.mRootNode = new aiNode();\n\n\t\tscene.mMaterials = new aiMaterial * [1];\n\t\tscene.mMaterials[0] = nullptr;\n\t\tscene.mNumMaterials = 1;\n\n\t\tscene.mMaterials[0] = new aiMaterial();\n\n\t\tscene.mMeshes = new aiMesh * [1];\n\t\tscene.mNumMeshes = 1;\n\n\t\tscene.mMeshes[0] = new aiMesh();\n\t\tscene.mMeshes[0]->mMaterialIndex = 0;\n\n\t\tscene.mRootNode->mMeshes = new unsigned int[1];\n\t\tscene.mRootNode->mMeshes[0] = 0;\n\t\tscene.mRootNode->mNumMeshes = 1;\n\n\t\tauto pMesh = scene.mMeshes[0];\n\n\t\tconst auto& vVertices = _vertices;\n\n\t\tpMesh->mVertices = new aiVector3D[vVertices.size()];\n\t\tpMesh->mNumVertices = static_cast<unsigned int>(vVertices.size());\n\n\t\tif (hasNormals()) {\n\t\t\tpMesh->mNormals = new aiVector3D[vVertices.size()];\n\t\t}\n\t\telse {\n\t\t\tpMesh->mNormals = nullptr;\n\t\t}\n\n\t\tif (hasTexCoords()) {\n\t\t\tpMesh->mTextureCoords[0] = new aiVector3D[vVertices.size()];\n\t\t\tpMesh->mNumUVComponents[0] = 2;\n\t\t}\n\t\telse {\n\t\t\tpMesh->mTextureCoords[0] = nullptr;\n\t\t\tpMesh->mNumUVComponents[0] = 0;\n\t\t}\n\n\t\tint j = 0;\n\t\tfor (auto itr = vVertices.begin(); itr != vVertices.end(); ++itr)\n\t\t{\n\t\t\tpMesh->mVertices[itr - vVertices.begin()] = aiVector3D(vVertices[j].x(), vVertices[j].y(), vVertices[j].z());\n\t\t\tif (hasNormals())\n\t\t\t\tpMesh->mNormals[itr - vVertices.begin()] = aiVector3D(_normals[j].x(), _normals[j].y(), _normals[j].z());\n\t\t\tif (hasTexCoords())\n\t\t\t\tpMesh->mTextureCoords[0][itr - vVertices.begin()] = aiVector3D(_texcoords[j][0], _texcoords[j][1], 0);\n\t\t\tj++;\n\t\t}\n\n\t\tpMesh->mFaces = new aiFace[_triangles.size()];\n\t\tpMesh->mNumFaces = (unsigned int)(_triangles.size());\n\n\t\tfor (uint i = 0; i < _triangles.size(); ++i)\n\t\t{\n\t\t\tconst Vector3u& tri = _triangles[i];\n\t\t\taiFace& face = pMesh->mFaces[i];\n\t\t\tface.mIndices = new unsigned int[3];\n\t\t\tface.mNumIndices = 3;\n\n\t\t\tface.mIndices[0] = tri[0];\n\t\t\tface.mIndices[1] = tri[1];\n\t\t\tface.mIndices[2] = tri[2];\n\t\t}\n\t\tAssimp::Exporter mAiExporter;\n\t\tconst aiScene* s = (const aiScene*)&(scene);\n\n\t\tSIBR_LOG << \"Saving (via ASSIMP) \" << filename << \"'...\" << std::endl;\n\t\tmAiExporter.Export(s, \"obj\", filename);\n\n\t\t// mesh and scene destructors free memory\n\n\n\t\treturn true;\n\t}\n\n\n\tbool\t\tMesh::saveToBinaryPLY(const std::string& filename, bool universal, const std::string& textureName)  const\n\t{\n\t\tassert(_vertices.size());\n\n\t\tSIBR_LOG << \"Saving '\" << filename << \"'...\" << std::endl;\n\n\t\t// Note that Assimp supports also export for some formats. However,\n\t\t// when I tried with the current Assimp version (3.0) it failed to\n\t\t// do a good export for .ply (using binary version).\n\t\t// In addition, at this time there is no control/ExportProperties.\n\t\t// Thus I just do it myself.\n\n\t\tstd::ofstream\tfile(filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);\n\n\t\tif (file)\n\t\t{\n\t\t\tfile << \"ply\" << std::endl;\n\t\t\tfile << \"format binary_big_endian 1.0\" << std::endl;\n\t\t\tfile << \"comment Created by SIBR project\" << std::endl;\n\t\t\tif (hasTexCoords())\n\t\t\t{\n\t\t\t\tfile << \"comment TextureFile \" << textureName << std::endl;\n\t\t\t}\n\t\t\tfile << \"element vertex \" << _vertices.size() << std::endl;\n\t\t\tfile << \"property float x\" << std::endl;\n\t\t\tfile << \"property float y\" << std::endl;\n\t\t\tfile << \"property float z\" << std::endl;\n\t\t\tif (hasColors())\n\t\t\t{\n\t\t\t\tif (universal)\n\t\t\t\t{\n\t\t\t\t\tfile << \"property uchar red\" << std::endl;\n\t\t\t\t\tfile << \"property uchar green\" << std::endl;\n\t\t\t\t\tfile << \"property uchar blue\" << std::endl;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfile << \"property ushort red\" << std::endl;\n\t\t\t\t\tfile << \"property ushort green\" << std::endl;\n\t\t\t\t\tfile << \"property ushort blue\" << std::endl;\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tif (hasNormals())\n\t\t\t{\n\t\t\t\tfile << \"property float nx\" << std::endl;\n\t\t\t\tfile << \"property float ny\" << std::endl;\n\t\t\t\tfile << \"property float nz\" << std::endl;\n\t\t\t}\n\t\t\tif (hasTexCoords())\n\t\t\t{\n\t\t\t\tfile << \"property float texture_u\" << std::endl;\n\t\t\t\tfile << \"property float texture_v\" << std::endl;\n\t\t\t}\n\n\t\t\tfile << \"element face \" << _triangles.size() << std::endl;\n\t\t\tfile << \"property list uchar uint vertex_indices\" << std::endl;\n\t\t\tfile << \"end_header\" << std::endl;\n\n\t\t\t/// BINARY version /////\n\t\t\tByteStream\tbytes;\n\n\t\t\tfor (uint i = 0; i < _vertices.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Vector3f& v = _vertices[i];\n\n\t\t\t\tbytes << float(v[0]) << float(v[1]) << float(v[2]);\n\n\t\t\t\tif (hasColors())\n\t\t\t\t{\n\t\t\t\t\tconst Vector3f& c = _colors[i];\n\t\t\t\t\tif (universal)\n\t\t\t\t\t\tbytes\n\t\t\t\t\t\t<< uint8(c[0] * (UINT8_MAX - 1))\n\t\t\t\t\t\t<< uint8(c[1] * (UINT8_MAX - 1))\n\t\t\t\t\t\t<< uint8(c[2] * (UINT8_MAX - 1)); // ! converting colors explicitly\n\t\t\t\t\telse\n\t\t\t\t\t\tbytes\n\t\t\t\t\t\t<< uint16(c[0] * (UINT16_MAX - 1))\n\t\t\t\t\t\t<< uint16(c[1] * (UINT16_MAX - 1))\n\t\t\t\t\t\t<< uint16(c[2] * (UINT16_MAX - 1)); // ! converting colors explicitly\n\t\t\t\t}\n\n\t\t\t\tif (hasNormals())\n\t\t\t\t{\n\t\t\t\t\tconst Vector3f& n = _normals[i];\n\n\t\t\t\t\tbytes << float(n[0]) << float(n[1]) << float(n[2]);\n\t\t\t\t}\n\n\t\t\t\tif (hasTexCoords())\n\t\t\t\t{\n\t\t\t\t\tconst Vector2f& uv = _texcoords[i];\n\n\t\t\t\t\tbytes << float(uv[0]) << float(uv[1]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (uint i = 0; i < _triangles.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Vector3u& tri = _triangles[i];\n\n\t\t\t\tbytes << uint8(3);\n\t\t\t\tfor (uint j = 0; j < 3; ++j)\n\t\t\t\t\tbytes << uint32(tri[j]);\n\t\t\t}\n\n\t\t\tfile.write(reinterpret_cast<const char*>(bytes.buffer()), bytes.bufferSize());\n\t\t\tfile.close();\n\t\t\tSIBR_LOG << \"Saving '\" << filename << \"'... done\" << std::endl;\n\t\t\treturn true;\n\t\t}\n\t\tSIBR_LOG << \"error: cannot write to file '\" << filename << \"'.\" << std::endl;\n\t\treturn false;\n\n\t}\n\n\tbool\t\tMesh::saveToASCIIPLY(const std::string& filename, bool universal, const std::string& textureName) const\n\t{\n\t\tassert(_vertices.size());\n\n\t\t// Note that Assimp supports also export for some formats. However,\n\t\t// when I tried with the current Assimp version (3.0) it failed to\n\t\t// do a good export for .ply (using binary version).\n\t\t// In addition, at this time there is no control/ExportProperties.\n\t\t// Thus I just do it myself.\n\n\t\tstd::ofstream\tfile(filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);\n\n\t\tif (file)\n\t\t{\n\t\t\tfile << \"ply\" << std::endl;\n\t\t\tfile << \"format ascii 1.0\" << std::endl;\n\t\t\tfile << \"comment Created by SIBR project\" << std::endl;\n\t\t\tif (hasTexCoords())\n\t\t\t{\n\t\t\t\tfile << \"comment TextureFile \" << textureName << std::endl;\n\t\t\t}\n\t\t\tfile << \"element vertex \" << _vertices.size() << std::endl;\n\t\t\tfile << \"property float x\" << std::endl;\n\t\t\tfile << \"property float y\" << std::endl;\n\t\t\tfile << \"property float z\" << std::endl;\n\n\t\t\tif (hasColors())\n\t\t\t{\n\t\t\t\tif (universal)\n\t\t\t\t{\n\t\t\t\t\tfile << \"property uchar red\" << std::endl;\n\t\t\t\t\tfile << \"property uchar green\" << std::endl;\n\t\t\t\t\tfile << \"property uchar blue\" << std::endl;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfile << \"property ushort red\" << std::endl;\n\t\t\t\t\tfile << \"property ushort green\" << std::endl;\n\t\t\t\t\tfile << \"property ushort blue\" << std::endl;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hasNormals())\n\t\t\t{\n\t\t\t\tfile << \"property float nx\" << std::endl;\n\t\t\t\tfile << \"property float ny\" << std::endl;\n\t\t\t\tfile << \"property float nz\" << std::endl;\n\t\t\t}\n\n\t\t\tif (hasTexCoords())\n\t\t\t{\n\t\t\t\tfile << \"property float texture_u\" << std::endl;\n\t\t\t\tfile << \"property float texture_v\" << std::endl;\n\t\t\t}\n\n\t\t\tfile << \"element face \" << _triangles.size() << std::endl;\n\t\t\tfile << \"property list uchar uint vertex_indices\" << std::endl;\n\t\t\tfile << \"end_header\" << std::endl;\n\n\t\t\t/////// ASCII version /////\n\n\t\t\tfor (uint i = 0; i < _vertices.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Vector3f& v = _vertices[i];\n\n\t\t\t\tfile << v[0] << \" \" << v[1] << \" \" << v[2] << \" \";\n\n\t\t\t\tif (hasColors())\n\t\t\t\t{\n\t\t\t\t\tconst Vector3f& c = _colors[i];\n\n\t\t\t\t\tif (universal)\n\t\t\t\t\t{\n\t\t\t\t\t\tfile << int(c[0] * (UINT8_MAX - 1)) << \" \"\n\t\t\t\t\t\t\t<< int(c[1] * (UINT8_MAX - 1)) << \" \"\n\t\t\t\t\t\t\t<< int(c[2] * (UINT8_MAX - 1)) << \" \";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfile << int(c[0] * (UINT16_MAX - 1)) << \" \"\n\t\t\t\t\t\t\t<< int(c[1] * (UINT16_MAX - 1)) << \" \"\n\t\t\t\t\t\t\t<< int(c[2] * (UINT16_MAX - 1)) << \" \";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (hasNormals())\n\t\t\t\t{\n\t\t\t\t\tconst Vector3f& n = _normals[i];\n\n\t\t\t\t\tfile << n[0] << \" \" << n[1] << \" \" << n[2] << \" \";\n\t\t\t\t}\n\n\t\t\t\tif (hasTexCoords())\n\t\t\t\t{\n\t\t\t\t\tconst Vector2f& uv = _texcoords[i];\n\n\t\t\t\t\tfile << uv[0] << \" \" << uv[1] << \" \";\n\t\t\t\t}\n\n\t\t\t\tfile << std::endl;\n\t\t\t}\n\n\t\t\tfor (uint i = 0; i < _triangles.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Vector3u& tri = _triangles[i];\n\n\t\t\t\tfile << 3;\n\t\t\t\tfor (uint j = 0; j < 3; ++j)\n\t\t\t\t\tfile << \" \" << tri[j];\n\t\t\t\tfile << std::endl;\n\t\t\t}\n\n\t\t\tfile.close();\n\t\t\tSIBR_LOG << \"'\" << filename << \"' saved.\" << std::endl;\n\t\t\treturn true;\n\t\t}\n\t\tSIBR_LOG << \"error: cannot write to file '\" << filename << \"'.\" << std::endl;\n\t\treturn false;\n\n\t}\n\tbool\tMesh::load(const std::string& filename, const std::string& dataset_path )\n\t{\n\t\t// Does the file exists?\n\t\tif (!sibr::fileExists(filename)) {\n\t\t\tSIBR_LOG << \"Error: can't load mesh '\" << filename << \".\" << std::endl;\n\t\t\treturn false;\n\t\t}\n\t\tAssimp::Importer\timporter;\n\t\t//importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); // cause Assimp to remove all degenerated faces as soon as they are detected\n\t\tconst aiScene* scene = importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_FindDegenerates);\n\n\t\tif (!scene)\n\t\t{\n\t\t\tSIBR_LOG << \"error: can't load mesh '\" << filename\n\t\t\t\t<< \"' (\" << importer.GetErrorString() << \").\" << std::endl;\n\t\t\treturn false;\n\t\t}\n\n\t\t// check for texture\n\t\taiMaterial *material;\n\t\tif( scene->mNumMaterials > 0 ) {\n\t\t\tmaterial = scene->mMaterials[0];\n\t\t\taiString Path;\n\t\t\tif(material->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS ) {\n\t\t\t\t_textureImageFileName = Path.data;\n\t\t\t\tstd::cerr << \"Texture name \" << _textureImageFileName << std::endl;\n\t\t\t}\n\n\t\t}\n\n\t\tif (scene->mNumMeshes == 0)\n\t\t{\n\t\t\tSIBR_LOG << \"error: the loaded model file ('\" << filename\n\t\t\t\t<< \"') contains zero or more than one mesh. Number of meshes : \" << scene->mNumMeshes << std::endl;\n\t\t\treturn false;\n\t\t}\n\n\t\tauto convertVec = [](const aiVector3D& v) { return Vector3f(v.x, v.y, v.z); };\n\t\t_triangles.clear();\n\n\t\tuint offsetVertices = 0;\n\t\tuint offsetFaces = 0;\n\t\tuint matId = 0;\n\t\tstd::map<std::string, int> matName2Id;\n\t\tMatrix3f converter;\n\t\tconverter <<\n\t\t\t1, 0, 0,\n\t\t\t0, 1, 0,\n\t\t\t0, 0, 1;\n\n\t\tfor (uint meshId = 0; meshId < scene->mNumMeshes; ++meshId) {\n\t\t\tconst aiMesh* mesh = scene->mMeshes[meshId];\n\n\t\t\t_vertices.resize(offsetVertices + mesh->mNumVertices);\n\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i)\n\t\t\t\t_vertices[offsetVertices + i] = converter * convertVec(mesh->mVertices[i]);\n\n\n\t\t\tif (mesh->HasVertexColors(0) && mesh->mColors[0])\n\t\t\t{\n\t\t\t\t_colors.resize(offsetVertices + mesh->mNumVertices);\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i)\n\t\t\t\t{\n\t\t\t\t\t_colors[offsetVertices + i] = Vector3f(\n\t\t\t\t\t\tmesh->mColors[0][i].r,\n\t\t\t\t\t\tmesh->mColors[0][i].g,\n\t\t\t\t\t\tmesh->mColors[0][i].b);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mesh->HasNormals())\n\t\t\t{\n\t\t\t\t_normals.resize(offsetVertices + mesh->mNumVertices);\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i) {\n\t\t\t\t\t_normals[offsetVertices + i] = converter * convertVec(mesh->mNormals[i]);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif (mesh->HasTextureCoords(0))\n\t\t\t{\n\t\t\t\t_texcoords.resize(offsetVertices + mesh->mNumVertices);\n\t\t\t\tfor (uint i = 0; i < mesh->mNumVertices; ++i)\n\t\t\t\t\t_texcoords[offsetVertices + i] = convertVec(mesh->mTextureCoords[0][i]).xy();\n\t\t\t\t// TODO: make a clean function\n\t\t\t\tstd::string texFileName = dataset_path + \"/capreal/\" + _textureImageFileName;\n\t\t\t\tif( !fileExists(texFileName))\n\t\t\t\t\ttexFileName = parentDirectory(parentDirectory(dataset_path)) + \"/capreal/\" + _textureImageFileName;\n\t\t\t\tif( !fileExists(texFileName))\n\t\t\t\t\ttexFileName = parentDirectory(dataset_path) + \"/capreal/\" + _textureImageFileName;\n\n\t\t\t\tif (!mesh->HasVertexColors(0) && fileExists(texFileName)) {\n\t\t\t\t\t// Sample the texture\n\t\t\t\t\tsibr::ImageRGB texImg;\n\t\t\t\t\ttexImg.load(texFileName);\n\t\t\t\t\tstd::cout << \"Computing vertex colors ..\";\n\t\t\t\t\t_colors.resize(offsetVertices + mesh->mNumVertices);\n\t\t\t\t\tfor (uint ci = 0; ci < mesh->mNumVertices; ++ci)\n\t\t\t\t\t{\n\t\t\t\t\t\tVector2f uv = _texcoords[offsetVertices + ci];\n\t\t\t\t\t\tVector3ub col = texImg((uv[0]*texImg.w()), uint((1-uv[1])*texImg.h()));\n\t\t\t\t\t\t_colors[offsetVertices + ci] = Vector3f(float(col[0]) / 255.0, float(col[1]) / 255.0, float(col[2]) / 255.0);\n\t\t\t\t\t}\n\t\t\t\t\tSIBR_WRG << \"Done.\" << std::endl;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (meshId == 0) {\n\t\t\t\tSIBR_LOG << \"Mesh contains: colors: \" << mesh->HasVertexColors(0)\n\t\t\t\t\t<< \", normals: \" << mesh->HasNormals()\n\t\t\t\t\t<< \", texcoords: \" << mesh->HasTextureCoords(0) << std::endl;\n\t\t\t}\n\n\t\t\t_triangles.reserve(offsetFaces + mesh->mNumFaces);\n\t\t\tfor (uint i = 0; i < mesh->mNumFaces; ++i)\n\t\t\t{\n\t\t\t\tconst aiFace* f = &mesh->mFaces[i];\n\t\t\t\tif (f->mNumIndices != 3)\n\t\t\t\t\tSIBR_LOG << \"warning: discarding a face (not a triangle, num indices: \"\n\t\t\t\t\t<< f->mNumIndices << \")\" << std::endl;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVector3u tri = Vector3u(offsetVertices + f->mIndices[0], offsetVertices + f->mIndices[1], offsetVertices + f->mIndices[2]);\n\t\t\t\t\tif (tri[0] < 0 || tri[0] >= _vertices.size()\n\t\t\t\t\t\t|| tri[1] < 0 || tri[1] >= _vertices.size()\n\t\t\t\t\t\t|| tri[2] < 0 || tri[2] >= _vertices.size())\n\t\t\t\t\t\tSIBR_WRG << \"face num [\" << i << \"] contains invalid vertex id(s)\" << std::endl;\n\t\t\t\t\telse {\n\t\t\t\t\t\t_triangles.push_back(tri);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\toffsetFaces = (uint)_triangles.size();\n\t\t\toffsetVertices = (uint)_vertices.size();\n\n\t\t}\n\n\t\t_meshPath = filename;\n\n\t\tSIBR_LOG << \"Mesh '\" << filename << \" successfully loaded. \" << scene->mNumMeshes << \" meshes were loaded with a total of \"\n\t\t\t<< \" (\" << _triangles.size() << \") faces and \"\n\t\t\t<< \" (\" << _vertices.size() << \") vertices detected. Init GL ...\" << std::endl;\n\t\tSIBR_LOG << \"Init GL mesh complete \" << std::endl;\n\n\t\t_gl.dirtyBufferGL = true;\n\t\treturn true;\n\t}\n\t\n\tbool\tMesh::loadSfM(const std::string& filename, const std::string& dataset_path )\n\t{\n\t\t// Does the file exist?\n\t\n\t\tstd::string fname = dataset_path + \"points3D.bin\";\n\n\t\tstd::cerr << \"LOADSFM: Try to open \" << fname << std::endl;\n\n\t\tif (!sibr::fileExists(fname)) {\n\t\t\tSIBR_LOG << \"Error: can't load mesh '\" << fname << \".\" << std::endl;\n\t\t\treturn false;\n\t\t}\n\t\tVertices verts;\n\t\tColors cols;\n\t\tint numverts;\n\n\t\tReadPoints3DBinary(fname, verts, cols, numverts);\n\t\t_triangles.clear();\n\n\t\tuint matId = 0;\n\n\t\t_vertices.resize(numverts);\n\n\t\tfor (uint i = 0; i < numverts;  ++i)\n\t\t\t\t_vertices[i] = verts[i];\n\n\t\t_colors.resize(numverts);\n\n\t\tfor (uint i = 0; i < numverts; ++i)\n\t\t\t_colors[i] = Vector3f(\n\t\t\t\tcols[i].x(), cols[i].y(), cols[i].z());\n\n\t\t_meshPath = dataset_path + \"/points3D.bin\";\n\t\t_renderingOptions.mode = PointRenderMode;\n\n\t\tSIBR_LOG << \"SfM Mesh '\" << filename << \" successfully loaded. \" << \" (\" << _vertices.size() << \") vertices detected. Init GL ...\" << std::endl;\n\t\tSIBR_LOG << \"Init GL mesh complete \" << std::endl;\n\n\t\t_gl.dirtyBufferGL = true;\n\t\treturn true;\n\t}\n\n\n\tbool sibr::Mesh::loadMtsXML(const std::string& xmlFile)\n\t{\n\t\tbool allLoaded = true;\n\t\tstd::string pathFolder = boost::filesystem::path(xmlFile).parent_path().string();\n\t\tsibr::XMLTree doc(xmlFile);\n\n\t\tstd::map<std::string, sibr::Mesh> meshes;\n\t\tstd::map<std::string, std::string> idToFilename;\n\n\t\trapidxml::xml_node<>* nodeScene = doc.first_node(\"scene\");\n\n\t\tfor (rapidxml::xml_node<>* node = nodeScene->first_node(\"shape\");\n\t\t\tnode; node = node->next_sibling(\"shape\"))\n\t\t{\n\t\t\tif (strcmp(node->first_attribute()->name(), \"type\") == 0 &&\n\t\t\t\tstrcmp(node->first_attribute()->value(), \"shapegroup\") == 0) {\n\n\t\t\t\tstd::cout << \"Found : \" << node->first_attribute(\"id\")->value() << std::endl;\n\n\t\t\t\tstd::string id = node->first_attribute(\"id\")->value();\n\t\t\t\tstd::string filename = node->first_node(\"shape\")->first_node(\"string\")->first_attribute(\"value\")->value();\n\t\t\t\tidToFilename[id] = filename;\n\t\t\t}\n\t\t}\n\n\t\tfor (rapidxml::xml_node<>* node = nodeScene->first_node(\"shape\");\n\t\t\tnode; node = node->next_sibling(\"shape\"))\n\t\t{\n\n\t\t\tif (strcmp(node->first_attribute()->name(), \"type\") == 0 &&\n\t\t\t\tstrcmp(node->first_attribute()->value(), \"instance\") == 0\n\t\t\t\t) {\n\t\t\t\trapidxml::xml_node<>* nodeRef = node->first_node(\"ref\");\n\t\t\t\tconst std::string id = nodeRef->first_attribute(\"id\")->value();\n\t\t\t\tconst std::string filename = idToFilename[id];\n\t\t\t\tconst std::string meshPath = pathFolder + \"/\" + filename;\n\n\t\t\t\tif (meshes.find(filename) == meshes.end()) {\n\t\t\t\t\tmeshes[filename] = sibr::Mesh();\n\t\t\t\t\tif (!meshes[filename].load(meshPath)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tstd::cout << \"Adding one instance of : \" << filename << std::endl;\n\n\t\t\t\trapidxml::xml_node<>* nodeTrans = node->first_node(\"transform\");\n\t\t\t\tif (nodeTrans) {\n\t\t\t\t\tsibr::Mesh toWorldMesh = meshes[filename];\n\t\t\t\t\trapidxml::xml_node<>* nodeM1 = nodeTrans->first_node(\"matrix\");\n\t\t\t\t\tstd::string matrix1 = nodeM1->first_attribute(\"value\")->value();\n\t\t\t\t\trapidxml::xml_node<>* nodeM2 = nodeM1->next_sibling(\"matrix\");\n\t\t\t\t\tstd::string matrix2 = nodeM2->first_attribute(\"value\")->value();\n\n\n\t\t\t\t\tstd::istringstream issM1(matrix1);\n\t\t\t\t\tstd::vector<std::string> splitM1(std::istream_iterator<std::string>{issM1},\n\t\t\t\t\t\tstd::istream_iterator<std::string>());\n\t\t\t\t\tsibr::Matrix4f m1;\n\t\t\t\t\tm1 <<\n\t\t\t\t\t\tstd::stof(splitM1[0]), std::stof(splitM1[1]), std::stof(splitM1[2]), std::stof(splitM1[3]),\n\t\t\t\t\t\tstd::stof(splitM1[4]), std::stof(splitM1[5]), std::stof(splitM1[6]), std::stof(splitM1[7]),\n\t\t\t\t\t\tstd::stof(splitM1[8]), std::stof(splitM1[9]), std::stof(splitM1[10]), std::stof(splitM1[11]),\n\t\t\t\t\t\tstd::stof(splitM1[12]), std::stof(splitM1[13]), std::stof(splitM1[14]), std::stof(splitM1[15]);\n\n\t\t\t\t\tstd::istringstream issM2(matrix2);\n\t\t\t\t\tstd::vector<std::string> splitM2(std::istream_iterator<std::string>{issM2},\n\t\t\t\t\t\tstd::istream_iterator<std::string>());\n\t\t\t\t\tsibr::Matrix4f m2;\n\t\t\t\t\tm2 <<\n\t\t\t\t\t\tstd::stof(splitM2[0]), std::stof(splitM2[1]), std::stof(splitM2[2]), std::stof(splitM2[3]),\n\t\t\t\t\t\tstd::stof(splitM2[4]), std::stof(splitM2[5]), std::stof(splitM2[6]), std::stof(splitM2[7]),\n\t\t\t\t\t\tstd::stof(splitM2[8]), std::stof(splitM2[9]), std::stof(splitM2[10]), std::stof(splitM2[11]),\n\t\t\t\t\t\tstd::stof(splitM2[12]), std::stof(splitM2[13]), std::stof(splitM2[14]), std::stof(splitM2[15]);\n\n\t\t\t\t\tsibr::Mesh::Vertices vertices;\n\t\t\t\t\tfor (int v = 0; v < toWorldMesh.vertices().size(); v++) {\n\t\t\t\t\t\tsibr::Vector4f v4(toWorldMesh.vertices()[v].x(), toWorldMesh.vertices()[v].y(), toWorldMesh.vertices()[v].z(), 1.0);\n\t\t\t\t\t\tvertices.push_back((m2 * (m1 * v4)).xyz());\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttoWorldMesh.vertices(vertices);\n\t\t\t\t\tmerge(toWorldMesh);\n\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tmerge(meshes[filename]);\n\t\t\t\t}\n\n\n\t\t\t}\n\t\t\telse if (strcmp(node->first_attribute()->name(), \"type\") == 0 &&\n\t\t\t\tstrcmp(node->first_attribute()->value(), \"obj\") == 0\n\t\t\t\t) {\n\t\t\t\trapidxml::xml_node<>* nodeRef = node->first_node(\"string\");\n\t\t\t\tconst std::string filename = nodeRef->first_attribute(\"value\")->value();\n\t\t\t\tconst std::string meshPath = pathFolder + \"/\" + filename;\n\n\t\t\t\tif (meshes.find(filename) == meshes.end()) {\n\t\t\t\t\tmeshes[filename] = sibr::Mesh();\n\t\t\t\t\tif (!meshes[filename].load(meshPath)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tstd::cout << \"Adding one instance of : \" << filename << std::endl;\n\n\t\t\t\trapidxml::xml_node<>* nodeTrans = node->first_node(\"transform\");\n\t\t\t\tif (nodeTrans) {\n\t\t\t\t\tsibr::Mesh toWorldMesh = meshes[filename];\n\t\t\t\t\trapidxml::xml_node<>* nodeM1 = nodeTrans->first_node(\"matrix\");\n\t\t\t\t\tstd::string matrix1 = nodeM1->first_attribute(\"value\")->value();\n\n\n\t\t\t\t\tstd::istringstream issM1(matrix1);\n\t\t\t\t\tstd::vector<std::string> splitM1(std::istream_iterator<std::string>{issM1},\n\t\t\t\t\t\tstd::istream_iterator<std::string>());\n\t\t\t\t\tsibr::Matrix4f m1;\n\t\t\t\t\tm1 <<\n\t\t\t\t\t\tstd::stof(splitM1[0]), std::stof(splitM1[1]), std::stof(splitM1[2]), std::stof(splitM1[3]),\n\t\t\t\t\t\tstd::stof(splitM1[4]), std::stof(splitM1[5]), std::stof(splitM1[6]), std::stof(splitM1[7]),\n\t\t\t\t\t\tstd::stof(splitM1[8]), std::stof(splitM1[9]), std::stof(splitM1[10]), std::stof(splitM1[11]),\n\t\t\t\t\t\tstd::stof(splitM1[12]), std::stof(splitM1[13]), std::stof(splitM1[14]), std::stof(splitM1[15]);\n\n\n\t\t\t\t\tsibr::Mesh::Vertices vertices;\n\t\t\t\t\tfor (int v = 0; v < toWorldMesh.vertices().size(); v++) {\n\t\t\t\t\t\tsibr::Vector4f v4(toWorldMesh.vertices()[v].x(), toWorldMesh.vertices()[v].y(), toWorldMesh.vertices()[v].z(), 1.0);\n\t\t\t\t\t\tvertices.push_back((m1 * v4).xyz());\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttoWorldMesh.vertices(vertices);\n\t\t\t\t\tmerge(toWorldMesh);\n\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tmerge(meshes[filename]);\n\t\t\t\t}\n\n\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tvoid\tMesh::save(const std::string& filename, bool universal, const std::string &textureName) const\n\t{\n\t\tif (vertices().empty())\n\t\t\tSIBR_ERR << \"cannot save this mesh (no vertices found)\" << std::endl;\n\t\t// This function is a just a switch (so we can change format details\n\t\t// internally).\n\n\t\tconst std::string ext = sibr::getExtension(filename);\n\t\tif (ext == \"obj\") {\n\t\t\tsaveToObj(filename);\n\t\t}\n\t\telse {\n\t\t\t// If you encounter problem with the resulting mesh, you can switch\n\t\t\t// to the ASCII version for easy reading\n\t\t\t// Meshlab does not support uint16 colors, if you want to use the mesh in such\n\t\t\t// program, set 'universal' = true\n\t\t\tif (universal) {\n\t\t\t\tsaveToASCIIPLY(filename, true, textureName);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsaveToBinaryPLY(filename, false);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tvoid\tMesh::vertices(const std::vector<float>& vertices)\n\t{\n\t\t_gl.dirtyBufferGL = true;\n\t\t_vertices.clear();\n\n\t\t// iterator for values\n\t\tstd::vector<float>::const_iterator it = vertices.begin();\n\n\t\t//\n\t\twhile (it != vertices.end())\n\t\t{\n\t\t\tVector3f vertex;\n\n\t\t\tfor (int i = 0; i < 3; ++i, ++it)\n\t\t\t\tvertex[i] = (*it);\n\n\t\t\t_vertices.push_back(vertex);\n\t\t}\n\t}\n\n\tvoid\tMesh::triangles(const std::vector<uint>& triangles)\n\t{\n\t\t_gl.dirtyBufferGL = true;\n\t\t_triangles.clear();\n\n\t\t// iterator for values\n\t\tstd::vector<uint>::const_iterator it = triangles.begin();\n\n\t\t//\n\t\twhile (it != triangles.end())\n\t\t{\n\t\t\tVector3u triangle;\n\n\t\t\tfor (int i = 0; i < 3; ++i, ++it)\n\t\t\t\ttriangle[i] = (*it);\n\n\t\t\t_triangles.push_back(triangle);\n\t\t}\n\t}\n\n\tvoid\t\tMesh::texCoords(const std::vector<float>& texcoords)\n\t{\n\t\t_gl.dirtyBufferGL = true;\n\t\t_texcoords.clear();\n\n\t\t// iterator for values\n\t\tstd::vector<float>::const_iterator it = texcoords.begin();\n\n\t\t//\n\t\twhile (it != texcoords.end())\n\t\t{\n\t\t\tVector2f texcoord;\n\n\t\t\tfor (int i = 0; i < 2; ++i, ++it)\n\t\t\t\ttexcoord[i] = (*it);\n\n\t\t\t_texcoords.push_back(texcoord);\n\t\t}\n\t}\n\n\tvoid\tMesh::normals(const std::vector<float>& normals)\n\t{\n\t\t_gl.dirtyBufferGL = true;\n\t\t_normals.clear();\n\n\t\t// iterator for values\n\t\tstd::vector<float>::const_iterator it = normals.begin();\n\n\t\t//\n\t\twhile (it != normals.end())\n\t\t{\n\t\t\tVector3f normal;\n\n\t\t\tfor (int i = 0; i < 3; ++i, ++it)\n\t\t\t\tnormal[i] = (*it);\n\n\t\t\t_normals.push_back(normal);\n\t\t}\n\t}\n\n\tvoid\tMesh::generateNormals(void)\n\t{\n\n\t\t// will store a list of normals (of all triangles around each vertex)\n\t\tstd::vector<std::vector<Vector3f>>\tvertexNormals(_vertices.size());\n\n\t\tauto normalizeNormal = [](const Vector3f& normal) -> Vector3f {\n\t\t\tfloat len = normal.norm();\n\t\t\tif (len > std::numeric_limits<float>::epsilon())\n\t\t\t\treturn normal / len;\n\t\t\t//else // may happen on tiny sharp edge, in this case points up\n\t\t\treturn Vector3f(0.f, 1.f, 0.f);\n\t\t};\n\n\t\tint i = 0;\n\t\tfor (const Vector3u& tri : _triangles)\n\t\t{\n\t\t\ti++;\n\t\t\tif (tri[0] > _vertices.size() || tri[1] > _vertices.size() ||\n\t\t\t\ttri[2] > _vertices.size()) {\n\t\t\t\tSIBR_ERR << \"Incorrect indices (\" << i << \") \" << tri[0] << \":\" << tri[1] << \":\" << tri[2] << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tVector3f u = _vertices[tri[0]] - _vertices[tri[2]];\n\t\t\t\tVector3f v = _vertices[tri[0]] - _vertices[tri[1]];\n\t\t\t\tVector3f normal = normalizeNormal(u.cross(v));\n\n\t\t\t\tvertexNormals[tri[0]].push_back(normal);\n\t\t\t\tvertexNormals[tri[1]].push_back(normal);\n\t\t\t\tvertexNormals[tri[2]].push_back(normal);\n\t\t\t}\n\t\t}\n\n\t\t_normals.resize(vertexNormals.size());\n\t\tfor (uint i = 0; i < _normals.size(); ++i)\n\t\t{\n\t\t\tVector3f n = std::accumulate(vertexNormals[i].begin(), vertexNormals[i].end(), Vector3f(0.f, 0.f, 0.f));\n\t\t\tn = (n / (float)vertexNormals.size());\n\t\t\tn = normalizeNormal(n);\n\t\t\t_normals[i] = -n;\n\t\t}\n\n\t\t_gl.dirtyBufferGL = true;\n\t}\n\n\tvoid\tMesh::generateSmoothNormals(int numIter)\n\t{\n\t\tSIBR_LOG << \"Generate vertex normals...\" << std::endl;\n\t\t// will store a list of normals (of all triangles around each vertex)\n\t\tstd::vector<std::vector<Vector3f>>\tvertexNormals(_vertices.size());\n\n\t\tauto normalizeNormal = [](const Vector3f& normal) -> Vector3f {\n\t\t\tfloat len = normal.norm();\n\t\t\tif (len > std::numeric_limits<float>::epsilon())\n\t\t\t\treturn normal / len;\n\t\t\t//else // may happen on tiny sharp edge, in this case points up\n\t\t\treturn Vector3f(0.f, 1.f, 0.f);\n\t\t};\n\n\n\t\tfor (int i = 0; i < _triangles.size(); i++)\n\t\t{\n\t\t\tconst Vector3u& tri = _triangles[i];\n\t\t\tif (tri[0] > _vertices.size() || tri[1] > _vertices.size() ||\n\t\t\t\ttri[2] > _vertices.size()) {\n\t\t\t\tSIBR_ERR << \"Incorrect indices (\" << i << \") \" << tri[0] << \":\" << tri[1] << \":\" << tri[2] << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tVector3f u = _vertices[tri[1]] - _vertices[tri[0]];\n\t\t\t\tVector3f v = _vertices[tri[2]] - _vertices[tri[0]];\n\t\t\t\tVector3f normal = u.cross(v);\n\n\t\t\t\tvertexNormals[tri[0]].push_back(normal);\n\t\t\t\tvertexNormals[tri[1]].push_back(normal);\n\t\t\t\tvertexNormals[tri[2]].push_back(normal);\n\t\t\t}\n\t\t}\n\n\t\t_normals.resize(vertexNormals.size());\n\t\t//#pragma omp parallel for\n\t\tfor (int i = 0; i < _normals.size(); ++i)\n\t\t{\n\t\t\tVector3f n = std::accumulate(vertexNormals[i].begin(), vertexNormals[i].end(), Vector3f(0.f, 0.f, 0.f));\n\t\t\tif (numIter == 0)//no iteration\n\t\t\t\tn = normalizeNormal(n);\n\t\t\t_normals[i] = n;\n\t\t}\n\n\t\t//Here we computed normals based on surrounding triangles\n\n\t\tfor (int it = 0; it < numIter; it++) {\n\n\t\t\tstd::vector<std::vector<Vector3f>>\tvertexNormalsIter(_vertices.size());\n\n\t\t\tfor (int i = 0; i < _triangles.size(); i++)\n\t\t\t{\n\t\t\t\tconst Vector3u& tri = _triangles[i];\n\t\t\t\tif (tri[0] > _vertices.size() || tri[1] > _vertices.size() ||\n\t\t\t\t\ttri[2] > _vertices.size()) {\n\t\t\t\t\tSIBR_ERR << \"Incorrect indices (\" << i << \") \" << tri[0] << \":\" << tri[1] << \":\" << tri[2] << std::endl;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tfor (int tId = 0; tId < 3; tId++) {\n\t\t\t\t\t\tVector3f normal = _normals[tri[tId]];\n\t\t\t\t\t\tvertexNormalsIter[tri[(tId + 1) % 3]].push_back(normal);\n\t\t\t\t\t\tvertexNormalsIter[tri[(tId + 2) % 3]].push_back(normal);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfloat maxLength = 0.0f;\n\t\t\tfor (int i = 0; i < _normals.size(); ++i)\n\t\t\t{\n\t\t\t\tVector3f n = std::accumulate(vertexNormalsIter[i].begin(), vertexNormalsIter[i].end(), Vector3f(0.f, 0.f, 0.f));\n\t\t\t\tif (it + 1 == numIter)//last iteration\n\t\t\t\t\tn = normalizeNormal(n);\n\t\t\t\t_normals[i] = n;\n\t\t\t\tmaxLength = std::max(maxLength, _normals[i].norm());\n\t\t\t}\n\n\t\t\t// To avoid float overflow after multiple iterations, we need to normalize.\n\t\t\t// But we can't just normalize each normal separately because we want to\n\t\t\t// preserve the relative triangle area weighting.\n\t\t\t// So instead we just send everything in [0,1] each time apart from the last iteration.\n\t\t\tif (maxLength > 0.0f && (it + 1 < numIter)) {\n\t\t\t\tfor (int i = 0; i < _normals.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\t_normals[i] /= maxLength;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\t_gl.dirtyBufferGL = true;\n\t}\n\n\tvoid\tMesh::generateSmoothNormalsDisconnected(int numIter)\n\t{\n\t\tSIBR_LOG << \"Generate vertex normals...\" << std::endl;\n\t\t// will store a list of normals (of all triangles around each vertex)\n\t\tstd::vector<std::vector<Vector3f>>\tvertexNormals(_vertices.size());\n\n\t\tauto normalizeNormal = [](const Vector3f& normal) -> Vector3f {\n\t\t\tfloat len = normal.norm();\n\t\t\tif (len > std::numeric_limits<float>::epsilon())\n\t\t\t\treturn normal / len;\n\t\t\t//else // may happen on tiny sharp edge, in this case points up\n\t\t\treturn Vector3f(0.f, 1.f, 0.f);\n\t\t};\n\n\t\tstd::vector<std::pair<sibr::Vector3f, int>> vertCopy;\n\t\tfor (int i = 0; i < _vertices.size(); ++i)\n\t\t{\n\t\t\tvertCopy.push_back(std::make_pair(_vertices[i], i));\n\t\t}\n\n\t\tstd::sort(vertCopy.begin(), vertCopy.end());\n\n\t\tfor (int i = 0; i < 100; ++i)\n\t\t{\n\t\t\tstd::cout << \"\\t \" << vertCopy[i].first << std::endl;\n\t\t}\n\n\t\tstd::vector<int> v2firstCopy(_vertices.size(), -1);\n\t\tint dupCount = 0;\n\t\tfor (int i = 0; i < _vertices.size(); ++i)\n\t\t{\n\t\t\tif (i % 1000 == 0)\n\t\t\t\tstd::cout << \"\\t \" << i << \" of \" << _vertices.size() << std::endl;\n\n\t\t\tint ii = i - 1;\n\t\t\tif ((vertCopy[ii].first - vertCopy[i].first).norm() > 0.000001f) {\n\n\t\t\t\tv2firstCopy[vertCopy[i].second] = vertCopy[i].second;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdupCount++;\n\t\t\t\tv2firstCopy[vertCopy[i].second] = v2firstCopy[vertCopy[ii].second];\n\t\t\t}\n\n\t\t}\n\n\t\tstd::cout << \"Duplicates found :\" << dupCount << std::endl;\n\n\t\tfor (int i = 0; i < _triangles.size(); i++)\n\t\t{\n\t\t\tconst Vector3u& tri = _triangles[i];\n\t\t\tif (tri[0] > _vertices.size() || tri[1] > _vertices.size() ||\n\t\t\t\ttri[2] > _vertices.size()) {\n\t\t\t\tSIBR_ERR << \"Incorrect indices (\" << i << \") \" << tri[0] << \":\" << tri[1] << \":\" << tri[2] << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tVector3f u = _vertices[tri[1]] - _vertices[tri[0]];\n\t\t\t\tVector3f v = _vertices[tri[2]] - _vertices[tri[0]];\n\t\t\t\tVector3f normal = u.cross(v);\n\n\t\t\t\tvertexNormals[tri[0]].push_back(normal);\n\t\t\t\tvertexNormals[tri[1]].push_back(normal);\n\t\t\t\tvertexNormals[tri[2]].push_back(normal);\n\t\t\t}\n\t\t}\n\n\t\tsibr::Mesh::Normals normalsCopy;\n\t\tnormalsCopy.resize(vertexNormals.size());\n\t\t//#pragma omp parallel for\n\t\tfor (int i = 0; i < normalsCopy.size(); ++i)\n\t\t{\n\t\t\tnormalsCopy[i] = sibr::Vector3f(0, 0, 0);\n\t\t}\n\t\tfor (int i = 0; i < normalsCopy.size(); ++i)\n\t\t{\n\t\t\tVector3f n = std::accumulate(vertexNormals[i].begin(), vertexNormals[i].end(), Vector3f(0.f, 0.f, 0.f));\n\t\t\tnormalsCopy[v2firstCopy[i]] += n;\n\t\t}\n\n\t\t//Here we computed normals based on surrounding triangles\n\n\t\tfor (int it = 0; it < numIter; it++) {\n\n\t\t\tstd::vector<std::vector<Vector3f>>\tvertexNormalsIter(_vertices.size());\n\n\t\t\tfor (int i = 0; i < _triangles.size(); i++)\n\t\t\t{\n\t\t\t\tconst Vector3u& tri = _triangles[i];\n\t\t\t\tif (tri[0] > _vertices.size() || tri[1] > _vertices.size() ||\n\t\t\t\t\ttri[2] > _vertices.size()) {\n\t\t\t\t\tSIBR_ERR << \"Incorrect indices (\" << i << \") \" << tri[0] << \":\" << tri[1] << \":\" << tri[2] << std::endl;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tfor (int tId = 0; tId < 3; tId++) {\n\t\t\t\t\t\tVector3f normal = normalsCopy[v2firstCopy[tri[tId]]];\n\t\t\t\t\t\tvertexNormalsIter[tri[(tId + 1) % 3]].push_back(normal);\n\t\t\t\t\t\tvertexNormalsIter[tri[(tId + 2) % 3]].push_back(normal);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//#pragma omp parallel for\n\t\t\tfor (int i = 0; i < normalsCopy.size(); ++i)\n\t\t\t{\n\t\t\t\tnormalsCopy[i] = sibr::Vector3f(0, 0, 0);\n\t\t\t}\n\t\t\tfor (int i = 0; i < normalsCopy.size(); ++i)\n\t\t\t{\n\t\t\t\tVector3f n = std::accumulate(vertexNormalsIter[i].begin(), vertexNormalsIter[i].end(), Vector3f(0.f, 0.f, 0.f));\n\t\t\t\tnormalsCopy[v2firstCopy[i]] += n;\n\t\t\t}\n\n\t\t}\n\n\t\t_normals.resize(normalsCopy.size());\n\t\tfor (int i = 0; i < _normals.size(); ++i)\n\t\t{\n\t\t\t_normals[i] += normalizeNormal(normalsCopy[v2firstCopy[i]]);\n\t\t}\n\n\t\t_gl.dirtyBufferGL = true;\n\t}\n\n\tvoid Mesh::laplacianSmoothing(int numIter, bool updateNormals) {\n\n\t\tif (numIter < 1) {\n\t\t\treturn;\n\t\t}\n\n\t\t/// Build neighbors information.\n\t\t/// \\todo TODO: we could also detect vertices on the edges of the mesh to preserve their positions.\n\t\tstd::vector<std::set<unsigned>> neighbors(_vertices.size());\n\t\tfor (const sibr::Vector3u& tri : _triangles) {\n\t\t\tneighbors[tri[0]].emplace(tri[1]);\n\t\t\tneighbors[tri[0]].emplace(tri[2]);\n\t\t\tneighbors[tri[1]].emplace(tri[0]);\n\t\t\tneighbors[tri[1]].emplace(tri[2]);\n\t\t\tneighbors[tri[2]].emplace(tri[1]);\n\t\t\tneighbors[tri[2]].emplace(tri[0]);\n\t\t}\n\n\t\t/// Smooth by averaging.\n\t\tconst size_t verticesSize = _vertices.size();\n\n\t\tfor (int it = 0; it < numIter; ++it) {\n\t\t\tstd::vector<sibr::Vector3f> newVertices(verticesSize);\n\n\t\t\tfor (size_t vid = 0; vid < verticesSize; ++vid) {\n\t\t\t\tnewVertices[vid] = sibr::Vector3f(0.0f, 0.0f, 0.f);\n\t\t\t\tfor (const auto& ovid : neighbors[vid]) {\n\t\t\t\t\tnewVertices[vid] += _vertices[ovid];\n\t\t\t\t}\n\t\t\t\tnewVertices[vid] /= float(neighbors[vid].size());\n\t\t\t}\n\n\t\t\tvertices(newVertices);\n\t\t}\n\n\t\tif (updateNormals) {\n\t\t\tgenerateNormals();\n\t\t}\n\t}\n\n\tvoid Mesh::adaptativeTaubinSmoothing(int numIter, bool updateNormals) {\n\n\t\tif (numIter < 1) {\n\t\t\treturn;\n\t\t}\n\n\t\t/// Build neighbors information.\n\t\t/// \\todo TODO: we could also detect vertices on the edges of the mesh to preserve their positions.\n\t\tstd::vector<std::set<unsigned>> neighbors(_vertices.size());\n\t\tstd::map<int, std::map<int, std::set<float>>> cotanW;\n\t\tfor (const sibr::Vector3u& tri : _triangles) {\n\t\t\tneighbors[tri[0]].emplace(tri[1]);\n\t\t\tneighbors[tri[0]].emplace(tri[2]);\n\t\t\tneighbors[tri[1]].emplace(tri[0]);\n\t\t\tneighbors[tri[1]].emplace(tri[2]);\n\t\t\tneighbors[tri[2]].emplace(tri[1]);\n\t\t\tneighbors[tri[2]].emplace(tri[0]);\n\n\t\t\tstd::vector<sibr::Vector3f> vs;\n\t\t\tfor (int i = 0; i < 3; i++)\n\t\t\t\tvs.push_back(_vertices[tri[i]]);\n\n\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\tfloat angle = acos((vs[i] - vs[(i + 2) % 3]).normalized().dot((vs[(i + 1) % 3] - vs[(i + 2) % 3]).normalized()));\n\t\t\t\tcotanW[tri[i]][tri[(i + 1) % 3]].emplace(1.0f / (tan(angle) + 0.00001f));\n\t\t\t\tcotanW[tri[(i + 1) % 3]][tri[i]].emplace(1.0f / (tan(angle) + 0.00001f));\n\t\t\t}\n\n\t\t}\n\n\t\t/// Smooth by averaging.\n\t\tconst size_t verticesSize = _vertices.size();\n\n\t\tstd::vector<sibr::Vector3f> newColors(verticesSize);\n\t\tfor (int it = 0; it < numIter; ++it) {\n\t\t\tstd::vector<sibr::Vector3f> newVertices(verticesSize);\n#pragma omp parallel for\n\t\t\tfor (int vid = 0; vid < verticesSize; ++vid) {\n\t\t\t\tsibr::Vector3f v = _vertices[vid];\n\t\t\t\tsibr::Vector3f dtV = sibr::Vector3f(0.0f, 0.0f, 0.f);\n\t\t\t\tfloat totalW = 0;\n\n\t\t\t\tstd::vector<sibr::Vector3f> colorsLocal;\n\t\t\t\tcolorsLocal.push_back(_colors[vid]);\n\t\t\t\tfor (const auto& ovid : neighbors[vid]) {\n\t\t\t\t\tfloat w = 0;\n\t\t\t\t\tfor (const auto& cot : cotanW[vid][ovid]) {\n\t\t\t\t\t\tw += 0.5 * cot;\n\t\t\t\t\t}\n\t\t\t\t\ttotalW += w;\n\t\t\t\t\tdtV += w * _vertices[ovid];\n\t\t\t\t\tcolorsLocal.push_back(_colors[ovid]);\n\t\t\t\t}\n\n\t\t\t\tsibr::Vector3f meanColor;\n\t\t\t\tfor (const auto& c : colorsLocal) {\n\t\t\t\t\tmeanColor += c;\n\t\t\t\t}\n\t\t\t\tmeanColor /= colorsLocal.size();\n\t\t\t\tsibr::Vector3f varColor;\n\t\t\t\tfor (const auto& c : colorsLocal) {\n\t\t\t\t\tpow(c.x() - meanColor.x(), 2);\n\t\t\t\t\tvarColor += sibr::Vector3f(pow(c.x() - meanColor.x(), 2), pow(c.y() - meanColor.y(), 2), pow(c.z() - meanColor.z(), 2));\n\t\t\t\t}\n\t\t\t\tvarColor /= colorsLocal.size();\n\n\t\t\t\tnewColors[vid] = varColor;\n\n\t\t\t\tif (totalW > 0) {\n\t\t\t\t\tdtV /= totalW;\n\t\t\t\t\tdtV = dtV - v;\n\t\t\t\t\tif (it % 2 == 0) {\n\t\t\t\t\t\tnewVertices[vid] = v + 0.25 * dtV;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tnewVertices[vid] = v + 0.25 * dtV;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvertices(newVertices);\n\t\t}\n\n\t\tcolors(newColors);\n\t\tif (updateNormals) {\n\t\t\tgenerateNormals();\n\t\t}\n\t}\n\n\n\n\tMesh Mesh::generateSubMesh(std::function<bool(int)> func) const\n\t{\n\n\t\tsibr::Mesh::Vertices newVertices;\n\t\tsibr::Mesh::Triangles newTriangles;\n\n\t\tsibr::Mesh::Colors newColors;\n\t\tsibr::Mesh::Normals newNormals;\n\t\tsibr::Mesh::UVs newUVs;\n\n\t\tstd::map<int, int> mapIdVert;\n\n\t\tint cmptValidVert = 0;\n\t\tint cmptVert = 0;\n\n\t\tsibr::Mesh::Colors oldColors;\n\t\tif (hasColors())\n\t\t\toldColors = colors();\n\n\t\tsibr::Mesh::Normals oldNormals;\n\t\tif (hasNormals())\n\t\t\toldNormals = normals();\n\n\t\tsibr::Mesh::UVs oldUVs;\n\t\tif (hasTexCoords())\n\t\t\toldUVs = texCoords();\n\n\t\tfor (int v = 0; v < vertices().size(); v++) {\n\n\t\t\tif (func(v)) {\n\n\t\t\t\tnewVertices.push_back(vertices()[v]);\n\n\t\t\t\tif (hasColors())\n\t\t\t\t\tnewColors.push_back(oldColors[cmptVert]);\n\t\t\t\tif (hasNormals())\n\t\t\t\t\tnewNormals.push_back(oldNormals[cmptVert]);\n\t\t\t\tif (hasTexCoords())\n\t\t\t\t\tnewUVs.push_back(oldUVs[cmptVert]);\n\n\t\t\t\tmapIdVert[cmptVert] = cmptValidVert;\n\t\t\t\tcmptValidVert++;\n\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmapIdVert[cmptVert] = -1;\n\t\t\t}\n\n\t\t\tcmptVert++;\n\n\t\t}\n\n\t\tfor (sibr::Vector3u t : triangles()) {\n\n\t\t\tif (mapIdVert[t.x()] != -1 &&\n\t\t\t\tmapIdVert[t.y()] != -1 &&\n\t\t\t\tmapIdVert[t.z()] != -1) {\n\t\t\t\tsibr::Vector3u newt(mapIdVert[t.x()], mapIdVert[t.y()], mapIdVert[t.z()]);\n\t\t\t\tnewTriangles.push_back(newt);\n\t\t\t}\n\t\t}\n\n\t\tMesh newMesh;\n\t\tnewMesh.vertices(newVertices);\n\t\tnewMesh.triangles(newTriangles);\n\t\tif (hasColors())\n\t\t\tnewMesh.colors(newColors);\n\t\tif (hasNormals())\n\t\t\tnewMesh.normals(newNormals);\n\t\tif (hasTexCoords())\n\t\t\tnewMesh.texCoords(newUVs);\n\n\t\treturn newMesh;\n\t}\n\n\tvoid\tMesh::forceBufferGLUpdate(bool adjacency) const\n\t{\n\t\tif (!_gl.bufferGL) { SIBR_ERR << \"Tried to forceBufferGL on a non OpenGL Mesh\" << std::endl; return; }\n\t\t_gl.dirtyBufferGL = false;\n\t\t_gl.bufferGL->build(*this, adjacency);\n\t}\n\n\tvoid\tMesh::freeBufferGLUpdate(void) const\n\t{\n\t\t_gl.dirtyBufferGL = false;\n\t\t_gl.bufferGL->free();\n\t}\n\n\tvoid\tMesh::render(bool depthTest, bool backFaceCulling, RenderMode mode, bool frontFaceCulling, bool invertDepthTest, bool tessellation, bool adjacency) const\n\t{\n\t\tif (!_gl.bufferGL) { SIBR_ERR << \"Tried to render a non OpenGL Mesh\" << std::endl; return; }\n\n\t\tif(adjacency && _renderingOptions.adjacency != adjacency)\n\t\t{\n\t\t\t_renderingOptions.adjacency = adjacency;\n\t\t\t_gl.dirtyBufferGL = true;\n\t\t}\n\t\tglLineWidth(3);\n\t\t_renderingOptions.depthTest = depthTest;\n\t\t_renderingOptions.backFaceCulling = backFaceCulling;\n\t\t_renderingOptions.mode = mode;\n\t\t_renderingOptions.frontFaceCulling = frontFaceCulling;\n\t\t_renderingOptions.invertDepthTest = invertDepthTest;\n\t\t_renderingOptions.tessellation = tessellation;\n\n\t\tif (_gl.dirtyBufferGL)\n\t\t\tforceBufferGLUpdate(adjacency);\n\n\t\tif (depthTest)\n\t\t\tglEnable(GL_DEPTH_TEST);\n\t\telse\n\t\t\tglDisable(GL_DEPTH_TEST);\n\n\t\tif (backFaceCulling)\n\t\t{\n\t\t\tglEnable(GL_CULL_FACE);\n\t\t\tif (!frontFaceCulling)\n\t\t\t\tglCullFace(GL_BACK);\n\t\t\telse\n\t\t\t\tglCullFace(GL_FRONT);\n\t\t}\n\t\telse\n\t\t\tglDisable(GL_CULL_FACE);\n\n\t\tif (invertDepthTest) {\n\t\t\tglDepthFunc(GL_GEQUAL);\n\t\t}\n\n\t\tswitch (mode)\n\t\t{\n\t\tcase sibr::Mesh::FillRenderMode:\n\t\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\t\tbreak;\n\t\tcase sibr::Mesh::PointRenderMode:\n\t\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_POINT);\n\t\t\tbreak;\n\t\tcase sibr::Mesh::LineRenderMode:\n\t\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tif (_triangles.size() != 0) {\n\t\t\tif (tessellation) {\n\t\t\t\t_gl.bufferGL->drawTessellated();\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_gl.bufferGL->draw(adjacency);\n\t\t\t}\n\t\t}\n\t\telse if (_vertices.size() != 0) {\n\t\t\t_gl.bufferGL->draw_points();\n\t\t}\n\n\t\t// Reset default state (Policy is 'restore default values')\n\t\tglDisable(GL_CULL_FACE);\n\t\tglDisable(GL_DEPTH_TEST);\n\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\tglDepthFunc(GL_LESS);\n\t}\n\n\tvoid\tMesh::renderSubMesh(unsigned int begin, unsigned int end,\n\t\tbool depthTest,\n\t\tbool backFaceCulling,\n\t\tRenderMode mode,\n\t\tbool frontFaceCulling,\n\t\tbool invertDepthTest\n\t) const {\n\t\tif (!_gl.bufferGL) { SIBR_ERR << \"Tried to render a non OpenGL Mesh\" << std::endl; return; }\n\t\tif (_gl.dirtyBufferGL)\n\t\t\tforceBufferGLUpdate();\n\n\t\tif (depthTest)\n\t\t\tglEnable(GL_DEPTH_TEST);\n\t\telse\n\t\t\tglDisable(GL_DEPTH_TEST);\n\n\t\tif (backFaceCulling)\n\t\t{\n\t\t\tglEnable(GL_CULL_FACE);\n\t\t\tif (!frontFaceCulling)\n\t\t\t\tglCullFace(GL_BACK);\n\t\t\telse\n\t\t\t\tglCullFace(GL_FRONT);\n\t\t}\n\t\telse\n\t\t\tglDisable(GL_CULL_FACE);\n\n\t\tif (invertDepthTest) {\n\t\t\tglDepthFunc(GL_GEQUAL);\n\t\t}\n\n\t\tswitch (mode)\n\t\t{\n\t\tcase sibr::Mesh::FillRenderMode:\n\t\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\t\tbreak;\n\t\tcase sibr::Mesh::PointRenderMode:\n\t\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_POINT);\n\t\t\tbreak;\n\t\tcase sibr::Mesh::LineRenderMode:\n\t\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tif (_triangles.size() != 0) {\n\t\t\t_gl.bufferGL->draw(begin, end);\n\t\t}\n\t\telse if (_vertices.size() != 0) {\n\t\t\t_gl.bufferGL->draw_points(begin, end);\n\t\t}\n\n\t\t// Reset default state (Policy is 'restore default values')\n\t\tglDisable(GL_CULL_FACE);\n\t\tglDisable(GL_DEPTH_TEST);\n\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\tglDepthFunc(GL_LESS);\n\t}\n\n\n\tvoid\tMesh::render_points(void) const\n\t{\n\t\tforceBufferGLUpdate();\n\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_POINT);\n\t\t_gl.bufferGL->draw_points();\n\t\tglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t}\n\n\tvoid\tMesh::render_points(bool depthTest) const\n\t{\n\t\tif (depthTest) {\n\t\t\tglEnable(GL_DEPTH_TEST);\n\t\t}\n\t\telse {\n\t\t\tglDisable(GL_DEPTH_TEST);\n\t\t}\n\n\t\trender_points();\n\n\t\tglDisable(GL_DEPTH_TEST);\n\t}\n\n\tvoid\tMesh::render_lines(void) const\n\t{\n\t\tif (!_gl.bufferGL) { SIBR_ERR << \"Tried to render a non OpenGL Mesh\" << std::endl; return; }\n\t\tif (_gl.dirtyBufferGL)\n\t\t\tforceBufferGLUpdate();\n\t\t_gl.bufferGL->draw_lines();\n\t}\n\n\tMesh::SubMesh Mesh::extractSubMesh(const std::vector<int>& newVerticesIds, VERTEX_LIST_CHOICE v_choice) const\n\t{\n\n\t\tint numOldVertices = (int)vertices().size();\n\t\tbool keep_from_list = (v_choice == VERTEX_LIST_CHOICE::KEEP);\n\t\tstd::vector<bool> willBeKept(numOldVertices, !keep_from_list);\n\t\tfor (int id : newVerticesIds) {\n\t\t\tif (id >= 0 && id < numOldVertices) {\n\t\t\t\twillBeKept[id] = keep_from_list;\n\t\t\t}\n\t\t}\n\n\t\tint numValidNewVertices = 0;\n\t\tfor (bool b : willBeKept) {\n\t\t\tif (b) {\n\t\t\t\t++numValidNewVertices;\n\t\t\t}\n\t\t}\n\n\t\tstd::vector<int> oldToNewVertexId(numOldVertices, -1);\n\n\t\tsibr::Mesh::Vertices newVertices(numValidNewVertices);\n\t\tsibr::Mesh::Triangles newTriangles;\n\n\t\tsibr::Mesh::Colors newColors;\n\t\tsibr::Mesh::Normals newNormals;\n\t\tsibr::Mesh::UVs newUVs;\n\n\t\tif (hasColors()) {\n\t\t\tnewColors.resize(numValidNewVertices);\n\t\t}\n\t\tif (hasNormals()) {\n\t\t\tnewNormals.resize(numValidNewVertices);\n\t\t}\n\t\tif (hasTexCoords()) {\n\t\t\tnewUVs.resize(numValidNewVertices);\n\t\t}\n\n\t\tint new_vertex_id = 0;\n\t\tfor (int id = 0; id < numOldVertices; ++id) {\n\t\t\tif (willBeKept[id]) {\n\t\t\t\tnewVertices[new_vertex_id] = vertices()[id];\n\t\t\t\toldToNewVertexId[id] = new_vertex_id;\n\n\t\t\t\tif (hasColors()) {\n\t\t\t\t\tnewColors[new_vertex_id] = colors()[id];\n\t\t\t\t}\n\t\t\t\tif (hasNormals()) {\n\t\t\t\t\tnewNormals[new_vertex_id] = normals()[id];\n\t\t\t\t}\n\t\t\t\tif (hasTexCoords()) {\n\t\t\t\t\tnewUVs[new_vertex_id] = texCoords()[id];\n\t\t\t\t}\n\t\t\t\t++new_vertex_id;\n\t\t\t}\n\t\t}\n\n\t\tstd::vector<bool> isInRemovedTriangle(numOldVertices, false);\n\t\tfor (const auto& t : triangles()) {\n\t\t\tsibr::Vector3i newVerticesId = t.cast<int>().unaryExpr([&oldToNewVertexId](int v_id) { return oldToNewVertexId[v_id];  });\n\t\t\tif (newVerticesId.unaryViewExpr([](int v_id) { return v_id >= 0 ? 1 : 0; }).all()) {\n\t\t\t\tnewTriangles.push_back(newVerticesId.cast<unsigned>());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (int c = 0; c < 3; c++) {\n\t\t\t\t\tisInRemovedTriangle[t[c]] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tbool oldMeshHasGraphics = (_gl.bufferGL.get() != nullptr);\n\n\t\tMesh::SubMesh subMesh;\n\t\tsubMesh.meshPtr = std::make_shared<sibr::Mesh>(oldMeshHasGraphics);\n\t\tsibr::Mesh& mesh = *subMesh.meshPtr;\n\t\tmesh.vertices(newVertices);\n\t\tmesh.triangles(newTriangles);\n\t\tif (hasColors()) {\n\t\t\tmesh.colors(newColors);\n\t\t}\n\t\tif (hasNormals()) {\n\t\t\tmesh.normals(newNormals);\n\t\t}\n\t\tif (hasTexCoords()) {\n\t\t\tmesh.texCoords(newUVs);\n\t\t}\n\n\t\tfor (int id = 0; id < numOldVertices; ++id) {\n\t\t\tif (isInRemovedTriangle[id]) {\n\t\t\t\tsubMesh.complementaryVertices.push_back(id);\n\t\t\t}\n\t\t}\n\n\t\treturn subMesh;\n\t}\n\n\tsibr::Mesh Mesh::invertedFacesMesh() const\n\t{\n\t\tsibr::Mesh invertedFacesMesh(_gl.bufferGL != nullptr);\n\t\tinvertedFacesMesh.vertices(vertices());\n\t\tif (hasColors()) {\n\t\t\tinvertedFacesMesh.colors(colors());\n\t\t}\n\t\tif (hasNormals()) {\n\t\t\tinvertedFacesMesh.normals(normals());\n\t\t}\n\t\tif (hasTexCoords()) {\n\t\t\tinvertedFacesMesh.texCoords(texCoords());\n\t\t}\n\n\t\tsibr::Mesh::Triangles invertedTriangles(triangles().size());\n\t\tfor (int t_id = 0; t_id < (int)triangles().size(); ++t_id) {\n\t\t\tinvertedTriangles[t_id] = triangles()[t_id].yxz();\n\t\t}\n\t\tinvertedFacesMesh.triangles(invertedTriangles);\n\n\t\treturn invertedFacesMesh;\n\t}\n\n\tsibr::Mesh::Ptr Mesh::invertedFacesMesh2() const\n\t{\n\t\tauto invertedFacesMesh = std::make_shared<Mesh>(_gl.bufferGL != nullptr);\n\n\t\tint nVertices = (int)vertices().size();\n\t\tint nTriangles = (int)triangles().size();\n\n\t\tMesh::Vertices Nvertices(2 * nVertices);\n\t\tMesh::Triangles Ntriangles(2 * nTriangles);\n\t\tMesh::Colors Ncolors(hasColors() ? 2 * nVertices : 0);\n\t\tMesh::Normals Nnormals(hasNormals() ? 2 * nVertices : 0);\n\t\tMesh::UVs Nuvs(hasTexCoords() ? 2 * nVertices : 0);\n\n\t\tint v_id = 0;\n\t\tfor (const auto& v : vertices()) {\n\t\t\tNvertices[v_id] = v;\n\t\t\tNvertices[v_id + nVertices] = v;\n\n\t\t\tif (hasNormals()) {\n\t\t\t\tNnormals[v_id] = normals()[v_id];\n\t\t\t\tNnormals[v_id + nVertices] = -normals()[v_id];\n\t\t\t}\n\n\t\t\tif (hasColors()) {\n\t\t\t\tNcolors[v_id] = colors()[v_id];\n\t\t\t\tNcolors[v_id + nVertices] = colors()[v_id];\n\t\t\t}\n\n\t\t\tif (hasTexCoords()) {\n\t\t\t\tNuvs[v_id] = texCoords()[v_id];\n\t\t\t\tNuvs[v_id + nVertices] = texCoords()[v_id];\n\t\t\t}\n\n\t\t\t++v_id;\n\t\t}\n\t\tinvertedFacesMesh->vertices(Nvertices);\n\n\t\tif (hasNormals()) {\n\t\t\tinvertedFacesMesh->normals(Nnormals);\n\t\t}\n\t\tif (hasColors()) {\n\t\t\tinvertedFacesMesh->colors(Ncolors);\n\t\t}\n\t\tif (hasTexCoords()) {\n\t\t\tinvertedFacesMesh->texCoords(Nuvs);\n\t\t}\n\n\t\tsibr::Vector3u shift(nVertices, nVertices, nVertices);\n\t\tint t_id = 0;\n\t\tfor (const auto& t : triangles()) {\n\t\t\tNtriangles[t_id] = t;\n\t\t\tNtriangles[t_id + nTriangles] = t.yxz() + shift;\n\t\t\t++t_id;\n\t\t}\n\t\tinvertedFacesMesh->triangles(Ntriangles);\n\n\t\treturn invertedFacesMesh;\n\t}\n\n\tconst std::string Mesh::getMeshFilePath(void) const\n\t{\n\t\treturn _meshPath;\n\t}\n\n\tvoid\t\t\t\t\tMesh::getBoundingSphere(Vector3f& outCenter, float& outRadius, bool referencedOnly, bool usePCcenter) const\n\t{\n\t\t// Get the center of mass\n\n\t\tdouble totalArea = 0, currentArea;\n\t\tdouble xCenter = 0, yCenter = 0, zCenter = 0;\n\n\t\tconst Triangles& tri = _triangles;\n\t\tconst Vertices& vert = _vertices;\n\t\tif (usePCcenter) {\n\t\t\tsibr::Vector3d outCenterDbl;\n\t\t\tfor (const Vector3f& v : vert)\n\t\t\t{\n\t\t\t\toutCenterDbl += v.cast<double>();\n\t\t\t}\n\n\t\t\toutCenter = (outCenterDbl / vert.size()).cast<float>();\n\t\t}\n\t\telse {\n\t\t\tif (tri.size() == 0) {\n\t\t\t\tSIBR_WRG << \"No triangles found for evaluation of sphere center, result will be NaN\";\n\t\t\t}\n\t\t\tfor (const Vector3u& t : tri)\n\t\t\t{\n\t\t\t\tfloat trix1 = vert[t[0]].x();\n\t\t\t\tfloat triy1 = vert[t[0]].y();\n\t\t\t\tfloat triz1 = vert[t[0]].z();\n\n\t\t\t\tfloat trix2 = vert[t[1]].x();\n\t\t\t\tfloat triy2 = vert[t[1]].y();\n\t\t\t\tfloat triz2 = vert[t[1]].z();\n\n\t\t\t\tfloat trix3 = vert[t[2]].x();\n\t\t\t\tfloat triy3 = vert[t[2]].y();\n\t\t\t\tfloat triz3 = vert[t[2]].z();\n\n\t\t\t\tcurrentArea = ((vert[t[1]] - vert[t[0]]).cross(vert[t[2]] - vert[t[0]])).norm() / 2.0;\n\t\t\t\ttotalArea += currentArea;\n\n\t\t\t\txCenter += ((trix1 + trix2 + trix3) / 3) * currentArea;\n\t\t\t\tyCenter += ((triy1 + triy2 + triy3) / 3) * currentArea;\n\t\t\t\tzCenter += ((triz1 + triz2 + triz3) / 3) * currentArea;\n\t\t\t}\n\n\t\t\toutCenter = Vector3f(float(xCenter / totalArea), float(yCenter / totalArea), float(zCenter / totalArea));\n\t\t}\n\n\n\t\toutRadius = 0.f;\n\t\tif (referencedOnly) {\n\t\t\tfor (const Vector3u& t : tri)\n\t\t\t{\n\t\t\t\toutRadius = std::max(outRadius, distance(vert[t[0]], outCenter));\n\t\t\t\toutRadius = std::max(outRadius, distance(vert[t[1]], outCenter));\n\t\t\t\toutRadius = std::max(outRadius, distance(vert[t[2]], outCenter));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor (const Vector3f& v : vert)\n\t\t\t\toutRadius = std::max(outRadius, distance(v, outCenter));\n\t\t}\n\t}\n\n\n\tEigen::AlignedBox<float, 3> Mesh::getBoundingBox(void) const\n\t{\n\t\tEigen::AlignedBox<float, 3> box;\n\t\tfor (const auto& vertex : _vertices) {\n\t\t\tbox.extend(vertex);\n\t\t}\n\t\treturn box;\n\t}\n\n\tsibr::Mesh::Ptr sibr::Mesh::getEnvSphere(sibr::Vector3f center, float radius, sibr::Vector3f zenith, sibr::Vector3f north,\n\t\tPartOfSphere part) {\n\n\t\tsibr::Vector3f east = north.cross(zenith);\n\t\tsibr::Mesh::Ptr envMesh(new sibr::Mesh());\n\t\tsibr::Mesh::Vertices vert;\n\t\tsibr::Mesh::UVs uvs;\n\t\tsibr::Mesh::Triangles tri;\n\n\t\tint highLimit = 0, lowLimit = 0;\n\t\tswitch (part)\n\t\t{\n\t\tcase PartOfSphere::WHOLE:\n\t\t\thighLimit = 90;\n\t\t\tlowLimit = -90;\n\t\t\tbreak;\n\t\tcase PartOfSphere::UP:\n\t\t\thighLimit = 90;\n\t\t\tlowLimit = 0;\n\t\t\tbreak;\n\t\tcase PartOfSphere::BOTTOM:\n\t\t\thighLimit = 0;\n\t\t\tlowLimit = -90;\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (int lat = lowLimit; lat <= highLimit; lat++) {\n\t\t\tfor (int lgt = 0; lgt <= 360; lgt++) {\n\n\t\t\t\tsibr::Vector3f point = cos(0.5 * M_PI * lat / 90.0f) * (cos(2 * M_PI * lgt / 360.0f) * north + sin(2 * M_PI * lgt / 360.0f) * east)\n\t\t\t\t\t+ sin(0.5 * M_PI * lat / 90.0f) * zenith;\n\n\t\t\t\tvert.push_back(10.f * radius * point + center);\n\n\t\t\t\tuvs.push_back(sibr::Vector2f(lgt / 360.0f, 0.5 + lat / 180.0f));\n\n\t\t\t}\n\t\t}\n\n\t\tfor (int lat = lowLimit; lat < highLimit; lat++) {\n\t\t\tfor (int lgt = 0; lgt < 360; lgt++) {\n\n\t\t\t\tint delta = 1;\n\t\t\t\tint lgtShift = lgt + 361 * (lat - lowLimit);\n\t\t\t\ttri.push_back(sibr::Vector3u(lgtShift, lgtShift + delta, lgtShift + 361 + delta));\n\t\t\t\ttri.push_back(sibr::Vector3u(lgtShift, lgtShift + 361 + delta, lgtShift + 361));\n\t\t\t}\n\t\t}\n\t\tenvMesh->vertices(vert);\n\t\tenvMesh->texCoords(uvs);\n\t\tenvMesh->triangles(tri);\n\n\t\treturn envMesh;\n\t}\n\n\tsibr::Vector3f Mesh::centroid() const\n\t{\n\t\tsibr::Vector3d centroid(0, 0, 0);\n\t\tfor (auto& vertex : _vertices) {\n\t\t\tcentroid += vertex.cast<double>();\n\t\t}\n\t\tif (_vertices.size() > 0) {\n\t\t\tcentroid /= static_cast<double>(_vertices.size());\n\t\t}\n\t\treturn centroid.cast<float>();\n\t}\n\n\tstd::stringstream Mesh::getOffStream(bool verbose) const\n\t{\n\t\tif (verbose) {\n\t\t\tstd::cout << \"[sibr::Mesh::getOffStream()] ... \" << std::flush;\n\t\t}\n\n\t\tstd::stringstream s;\n\t\ts << \"OFF \\n \\n\" << vertices().size() << \" \" << triangles().size() << \" 0 \" << std::endl;\n\n\t\tfor (const auto& v : vertices()) {\n\t\t\ts << v.x() << \" \" << v.y() << \" \" << v.z() << std::endl;\n\t\t}\n\n\t\tfor (const auto& t : triangles()) {\n\t\t\ts << \"3 \" << t.x() << \" \" << t.y() << \" \" << t.z() << std::endl;\n\t\t}\n\n\t\tif (verbose) {\n\t\t\tstd::cout << \" done \" << std::endl;\n\t\t}\n\n\t\treturn s;\n\t}\n\n\tvoid Mesh::fromOffStream(std::stringstream& stream, bool computeNormals)\n\t{\n\t\tint n_vert;\n\t\tint n_faces;\n\t\tint n_edges;\n\n\t\tstd::string line;\n\t\tsafeGetline(stream, line);\n\t\tsafeGetline(stream, line);\n\t\tstd::istringstream iss(line);\n\n\t\tiss >> n_vert >> n_faces >> n_edges;\n\n\t\t_vertices.resize(n_vert);\n\t\tfor (int v = 0; v < n_vert; ++v) {\n\t\t\tsafeGetline(stream, line);\n\t\t\tstd::istringstream lineStream(line);\n\n\t\t\tlineStream >> _vertices[v][0] >> _vertices[v][1] >> _vertices[v][2];\n\n\t\t}\n\n\t\t_triangles.resize(0);\n\t\t_triangles.reserve(3 * n_faces);\n\t\tint face_size;\n\t\tfor (int t = 0; t < n_faces; ++t) {\n\t\t\tsafeGetline(stream, line);\n\t\t\tstd::istringstream lineStream(line);\n\n\t\t\tlineStream >> face_size;\n\n\t\t\tif (face_size == 3) {\n\t\t\t\tsibr::Vector3u t;\n\t\t\t\tlineStream >> t[0] >> t[1] >> t[2];\n\t\t\t\t_triangles.push_back(t);\n\n\t\t\t}\n\t\t\telse if (face_size == 4) {\n\t\t\t\tsibr::Vector3u t1, t2;\n\t\t\t\tlineStream >> t1[0] >> t1[1] >> t1[2] >> t2[2];\n\t\t\t\tt2[0] = t1[0];\n\t\t\t\tt2[1] = t1[2];\n\t\t\t\t_triangles.push_back(t1);\n\t\t\t\t_triangles.push_back(t2);\n\t\t\t}\n\t\t}\n\n\t\tif (computeNormals) {\n\t\t\tgenerateNormals();\n\t\t}\n\n\t\tif (_gl.bufferGL.get()) {\n\t\t\t_gl.dirtyBufferGL = true;\n\t\t}\n\t}\n\n\tsibr::Mesh::Ptr Mesh::getTestCube(bool withGraphics)\n\t{\n\t\tstd::vector<sibr::Vector3f> vertices = {\n\t\t\t{ +1, +1, +1 }, { -1, +1, +1 }, { -1, -1, +1 },\n\t\t\t{ +1, -1, +1 }, { +1, +1, -1 }, { -1, +1, -1 },\n\t\t\t{ -1, -1, -1 }, { +1, -1, -1 }\n\t\t};\n\n\t\tstd::vector<sibr::Vector3u> indices = {\n\t\t\t{ 0, 1, 2 }, { 0, 2, 3 }, { 7, 4, 0 }, { 7, 0, 3 }, { 4, 5, 1 },\n\t\t\t{ 4, 1, 0 }, { 5, 6, 2 }, { 5, 2, 1 }, { 3, 2, 6 }, { 3, 6, 7 },\n\t\t\t{ 6, 5, 4 }, { 6, 4, 7 }\n\t\t};\n\n\t\tsibr::Mesh::Ptr mesh(new sibr::Mesh(withGraphics));\n\t\tmesh->vertices(vertices);\n\t\tmesh->triangles(indices);\n\t\tmesh->generateNormals();\n\n\t\treturn mesh;\n\t}\n\n\tMesh::Ptr Mesh::getSphereMesh(const Vector3f& center, float radius, bool withGraphics, int precision) {\n\t\tconst int nTheta = precision;\n\t\tconst int nPhi = precision;\n\t\tconst int nPoints = nTheta * nPhi;\n\t\tstd::vector<Vector3f> vertices(nPoints), normals(nPoints);\n\n\t\tfor (int t = 0; t < nTheta; ++t) {\n\t\t\tdouble theta = (t / (double)(nTheta - 1)) * M_PI;\n\t\t\tdouble cosT = std::cos(theta);\n\t\t\tdouble sinT = std::sin(theta);\n\t\t\tfor (int p = 0; p < nPhi; ++p) {\n\t\t\t\tdouble phi = 2.0 * (p / (double)(nPhi - 1) - 0.5) * M_PI;\n\t\t\t\tdouble cosP = std::cos(phi), sinP = std::sin(phi);\n\t\t\t\tnormals[p + nPhi * t] = Vector3d(sinT * cosP, sinT * sinP, cosT).cast<float>();\n\t\t\t\tvertices[p + nPhi * t] = center + radius * normals[p + nPhi * t];\n\t\t\t}\n\t\t}\n\n\t\tstd::vector<uint> indices(6 * (nTheta - 1) * nPhi);\n\t\tint triangle_id = 0;\n\t\tfor (int t = 0; t < nTheta - 1; ++t) {\n\t\t\tfor (int p = 0; p < nPhi; ++p) {\n\t\t\t\tint current_id = p + nPhi * t;\n\t\t\t\tint offset_row = 1 - (p == nPhi - 1 ? nPhi : 0);\n\t\t\t\tint next_in_row = current_id + offset_row;\n\t\t\t\tint next_in_col = current_id + nPhi;\n\t\t\t\tint next_next = next_in_col + offset_row;\n\t\t\t\tindices[3 * triangle_id + 0] = current_id;\n\t\t\t\tindices[3 * triangle_id + 1] = next_in_col;\n\t\t\t\tindices[3 * triangle_id + 2] = next_in_row;\n\t\t\t\tindices[3 * triangle_id + 3] = next_in_row;\n\t\t\t\tindices[3 * triangle_id + 4] = next_in_col;\n\t\t\t\tindices[3 * triangle_id + 5] = next_next;\n\t\t\t\ttriangle_id += 2;\n\t\t\t}\n\t\t}\n\n\t\tMesh::Ptr sphereMesh = Mesh::Ptr(new Mesh(withGraphics));\n\t\tsphereMesh->vertices(vertices);\n\t\tsphereMesh->normals(normals);\n\t\tsphereMesh->triangles(indices);\n\n\t\treturn sphereMesh;\n\t}\n\n\tsibr::Mesh::Ptr Mesh::subDivide(float limitSize, size_t maxRecursion) const\n\t{\n\t\tstruct Less {\n\t\t\tbool operator()(const sibr::Vector3f& a, const sibr::Vector3f& b) const {\n\t\t\t\treturn a < b;\n\t\t\t}\n\t\t};\n\n\t\tstruct Edge {\n\t\t\tsibr::Vector3f midPoint;\n\t\t\tsibr::Vector3f midNormal;\n\t\t\tstd::vector<int> triangles_ids;\n\t\t\tfloat length;\n\t\t\tint v_ids[2];\n\t\t};\n\n\t\tstruct Triangle {\n\t\t\tTriangle() : edges_ids(std::vector<int>(3, -1)), edges_flipped(std::vector<bool>(3, false)) {}\n\t\t\tstd::vector<int> edges_ids;\n\t\t\tstd::vector<bool> edges_flipped;\n\t\t};\n\n\t\tauto subMeshPtr = std::make_shared<sibr::Mesh>();\n\n\t\tstd::map<sibr::Vector3f, int, Less> mapEdges;\n\t\tstd::vector<Edge> edges;\n\t\tstd::vector<Triangle> tris(triangles().size());\n\n\t\tint t_id = 0;\n\t\tint e_id = 0;\n\t\tfor (const auto& t : triangles()) {\n\t\t\tbool degenerate = false;\n\t\t\tfor (int k = 0; k < 3; ++k) {\n\t\t\t\tint v0 = t[k];\n\t\t\t\tint v1 = t[(k + 1) % 3];\n\t\t\t\t// Skip degenerate faces.\n\t\t\t\tif (v0 == v1) {\n\t\t\t\t\tdegenerate = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst sibr::Vector3f midPoint = 0.5f * (vertices()[v0] + vertices()[v1]);\n\t\t\t\tsibr::Vector3f midNormal(0.0f, 0.0f, 0.0f);\n\t\t\t\tif (hasNormals()) {\n\t\t\t\t\tmidNormal = (0.5f * (normals()[v0] + normals()[v1])).normalized();\n\t\t\t\t}\n\n\t\t\t\tconst float length = (vertices()[v0] - vertices()[v1]).norm();\n\t\t\t\tif (mapEdges.count(midPoint) == 0) {\n\t\t\t\t\tmapEdges[midPoint] = e_id;\n\t\t\t\t\tconst Edge edge = { midPoint, midNormal, {t_id}, length, {v0,v1} };\n\t\t\t\t\ttris[t_id].edges_ids[k] = e_id;\n\t\t\t\t\tedges.push_back(edge);\n\t\t\t\t\t++e_id;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tconst int edge_id = mapEdges[midPoint];\n\t\t\t\t\tedges[edge_id].triangles_ids.push_back(t_id);\n\t\t\t\t\ttris[t_id].edges_ids[k] = edge_id;\n\t\t\t\t\tif (v0 != edges[edge_id].v_ids[0]) {\n\t\t\t\t\t\ttris[t_id].edges_flipped[k] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (degenerate) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t++t_id;\n\t\t}\n\n\t\tconst int nOldVertices = (int)vertices().size();\n\t\tsibr::Mesh::Vertices newVertices = vertices();\n\t\tsibr::Mesh::Normals newNormals = normals();\n\n\t\tstd::vector<int> edge_to_divided_edges(edges.size(), -1);\n\n\t\tsibr::Mesh::Triangles newTriangles;\n\n\t\tbool dbg = false;\n\t\tint num_divided_edges = 0;\n\t\tfor (const Triangle& t : tris) {\n\t\t\t// Ignore undef triangles.\n\t\t\tif (t.edges_ids[0] == -1 && t.edges_ids[1] == -1 && t.edges_ids[2] == -1) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstd::vector<int> ks(3, -1);\n\t\t\tstd::vector<int> non_ks(3, -1);\n\t\t\tfor (int k = 0; k < 3; ++k) {\n\t\t\t\tconst int e_id = t.edges_ids[k];\n\t\t\t\tif (edges[e_id].length > limitSize) {\n\t\t\t\t\tif (ks[0] < 0) {\n\t\t\t\t\t\tks[0] = k;\n\t\t\t\t\t}\n\t\t\t\t\telse if (ks[1] < 0) {\n\t\t\t\t\t\tks[1] = k;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tks[2] = k;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (edge_to_divided_edges[e_id] < 0) {\n\t\t\t\t\t\tedge_to_divided_edges[e_id] = num_divided_edges;\n\t\t\t\t\t\tnewVertices.push_back(edges[e_id].midPoint);\n\t\t\t\t\t\tnewNormals.push_back(edges[e_id].midNormal);\n\t\t\t\t\t\t++num_divided_edges;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (non_ks[0] < 0) {\n\t\t\t\t\t\tnon_ks[0] = k;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst sibr::Vector3i corners_ids = sibr::Vector3i(0, 1, 2).unaryViewExpr([&](int i) {\n\t\t\t\treturn edges[t.edges_ids[i]].v_ids[t.edges_flipped[i] ? 1 : 0];\n\t\t\t\t});\n\t\t\tconst sibr::Vector3i midpoints_ids = sibr::Vector3i(0, 1, 2).unaryViewExpr([&](int i) {\n\t\t\t\treturn  edge_to_divided_edges[t.edges_ids[i]] >= 0 ? nOldVertices + edge_to_divided_edges[t.edges_ids[i]] : -1;\n\t\t\t\t});\n\n\t\t\tif (ks[2] >= 0) {\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(corners_ids[0], midpoints_ids[0], midpoints_ids[2]));\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(corners_ids[1], midpoints_ids[1], midpoints_ids[0]));\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(corners_ids[2], midpoints_ids[2], midpoints_ids[1]));\n\t\t\t\tnewTriangles.push_back(midpoints_ids.cast<unsigned>());\n\t\t\t}\n\t\t\telse if (ks[1] >= 0) {\n\t\t\t\tconst int candidate_edge_1_v_id_1 = corners_ids[non_ks[0]];\n\t\t\t\tconst int candidate_edge_1_v_id_2 = midpoints_ids[(non_ks[0] + 1) % 3];\n\t\t\t\tconst int candidate_edge_2_v_id_1 = corners_ids[(non_ks[0] + 1) % 3];\n\t\t\t\tconst int candidate_edge_2_v_id_2 = midpoints_ids[(non_ks[0] + 2) % 3];\n\t\t\t\tconst float candidate_edge_1_norm = (newVertices[candidate_edge_1_v_id_1] - newVertices[candidate_edge_1_v_id_2]).norm();\n\t\t\t\tconst float candidate_edge_2_norm = (newVertices[candidate_edge_2_v_id_1] - newVertices[candidate_edge_2_v_id_2]).norm();\n\t\t\t\tif (candidate_edge_1_norm < candidate_edge_2_norm) {\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(candidate_edge_1_v_id_1, corners_ids[(non_ks[0] + 1) % 3], candidate_edge_1_v_id_2));\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(candidate_edge_1_v_id_2, midpoints_ids[(non_ks[0] + 2) % 3], candidate_edge_1_v_id_1));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(candidate_edge_2_v_id_1, candidate_edge_2_v_id_2, corners_ids[non_ks[0]]));\n\t\t\t\t\tnewTriangles.push_back(sibr::Vector3u(candidate_edge_2_v_id_1, midpoints_ids[(non_ks[0] + 1) % 3], candidate_edge_2_v_id_2));\n\t\t\t\t}\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(midpoints_ids[(non_ks[0] + 1) % 3], corners_ids[(non_ks[0] + 2) % 3], midpoints_ids[(non_ks[0] + 2) % 3]));\n\t\t\t}\n\t\t\telse if (ks[0] >= 0) {\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(midpoints_ids[ks[0]], corners_ids[(ks[0] + 1) % 3], corners_ids[(ks[0] + 2) % 3]));\n\t\t\t\tnewTriangles.push_back(sibr::Vector3u(midpoints_ids[ks[0]], corners_ids[(ks[0] + 2) % 3], corners_ids[ks[0]]));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnewTriangles.push_back(corners_ids.cast<unsigned>());\n\t\t\t}\n\t\t}\n\t\tstd::cout << \".\" << std::flush;\n\n\t\tsubMeshPtr->vertices(newVertices);\n\t\tif (hasNormals()) {\n\t\t\tsubMeshPtr->normals(newNormals);\n\t\t}\n\t\tsubMeshPtr->triangles(newTriangles);\n\t\tif (num_divided_edges > 0 && maxRecursion > 0) {\n\t\t\treturn subMeshPtr->subDivide(limitSize, maxRecursion - 1);\n\t\t}\n\n\t\treturn subMeshPtr;\n\t}\n\n\tfloat Mesh::meanEdgeSize() const\n\t{\n\t\tdouble sumSizes = 0;\n\t\tfor (const auto& t : triangles()) {\n\t\t\tconst auto& v1 = vertices()[t[0]];\n\t\t\tconst auto& v2 = vertices()[t[1]];\n\t\t\tconst auto& v3 = vertices()[t[2]];\n\t\t\tsumSizes += (double)((v1 - v2).norm() + (v2 - v3).norm() + (v3 - v1).norm());\n\t\t}\n\n\t\treturn (float)(sumSizes / (3 * triangles().size()));\n\t}\n\n\tMesh::Ptr Mesh::clone() const\n\t{\n\t\tauto outMesh = std::make_shared<sibr::Mesh>(_gl.bufferGL != nullptr);\n\t\toutMesh->vertices(vertices());\n\t\toutMesh->triangles(triangles());\n\n\t\tif (hasNormals()) {\n\t\t\toutMesh->normals(normals());\n\t\t}\n\n\t\tif (hasColors()) {\n\t\t\toutMesh->colors(colors());\n\t\t}\n\n\t\tif (hasTexCoords()) {\n\t\t\toutMesh->texCoords(texCoords());\n\t\t}\n\n\t\treturn outMesh;\n\t}\n\n\tvoid\t\tMesh::merge(const Mesh& other)\n\t{\n\t\tbool withGraphics = (_gl.bufferGL != nullptr);\n\n\t\tif (_vertices.empty())\n\t\t{\n\t\t\tthis->operator = (other);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVertices\tvertices;\n\t\t\tNormals\t\tnormals;\n\t\t\tColors\t\tcolors;\n\t\t\tUVs\t\t\ttexcoords;\n\n\t\t\tconst uint offset = static_cast<uint>(_vertices.size());\n\t\t\tVector3u\ttriOffset(offset, offset, offset);\n\t\t\tTriangles\ttriangles = other.triangles();\n\n\t\t\tfor (Vector3u& t : triangles)\n\t\t\t\tt += triOffset;\n\n\n\t\t\tif (hasNormals())\n\t\t\t\t_normals.insert(_normals.end(), other.normals().begin(), other.normals().end());\n\n\t\t\tif (hasColors())\n\t\t\t\t_colors.insert(_colors.end(), other.colors().begin(), other.colors().end());\n\n\t\t\tif (hasTexCoords())\n\t\t\t\t_texcoords.insert(_texcoords.end(), other.texCoords().begin(), other.texCoords().end());\n\n\n\t\t\t_vertices.insert(_vertices.end(), other.vertices().begin(), other.vertices().end());\n\t\t\t_triangles.insert(_triangles.end(), triangles.begin(), triangles.end());\n\n\t\t}\n\n\t\tif (withGraphics)\n\t\t\t_gl.bufferGL.reset(new MeshBufferGL);\n\t}\n\n\tvoid sibr::Mesh::makeWhole(void)\n\t{\n\t\tif (!hasNormals())\n\t\t\t_normals = Normals(vertices().size());\n\n\t\tif (!hasColors())\n\t\t\t_colors = Colors(vertices().size());\n\n\t\tif (!hasTexCoords())\n\t\t\t_texcoords = UVs(vertices().size());\n\n\t}\n\n\tvoid\t\tMesh::eraseTriangles(const std::vector<uint>& faceIDList)\n\t{\n\t\tuint indexMax = 0;\n\t\tfor (uint i = 0; i < triangles().size(); ++i)\n\t\t\tindexMax = std::max(indexMax, triangles()[i].maxCoeff());\n\n\t\tstd::vector<bool>\tfaceToErase(triangles().size(), false);\n\t\tfor (uint faceID : faceIDList)\n\t\t\tfaceToErase[faceID] = true;\n\n\n\t\tMesh::Triangles\t\tnewTris;\n\t\tMesh::Vertices\t\tnewVerts;\n\t\tstd::vector<int>\tindexRemap(indexMax + 1, -1);\n\n\t\tnewTris.reserve(triangles().size());\n\t\tnewVerts.reserve(vertices().size());\n\n\t\tfor (uint i = 0; i < triangles().size(); ++i)\n\t\t{\n\t\t\tif (faceToErase[i])\n\t\t\t\tcontinue;\n\n\t\t\tVector3u t = triangles()[i];\n\t\t\tVector3u newT;\n\t\t\tfor (uint j = 0; j < 3; ++j)\n\t\t\t{\n\t\t\t\tif (indexRemap[t[j]] == -1)\n\t\t\t\t{\n\t\t\t\t\tindexRemap[t[j]] = (int)newVerts.size();\n\t\t\t\t\tnewVerts.push_back(vertices().at(t[j]));\n\t\t\t\t}\n\t\t\t\tnewT[j] = (uint)indexRemap[t[j]];\n\n\t\t\t}\n\n\t\t\tnewTris.push_back(newT);\n\t\t}\n\n\t\tif (hasColors())\n\t\t{\n\t\t\tMesh::Colors\tnewColors(newVerts.size());\n\t\t\tfor (uint i = 0; i < indexRemap.size(); ++i)\n\t\t\t\tif (indexRemap[i] != -1)\n\t\t\t\t\tnewColors[indexRemap[i]] = colors().at(i);\n\t\t\tcolors(newColors);\n\t\t}\n\n\t\tif (hasNormals())\n\t\t{\n\t\t\tMesh::Normals\tnewNormals(newVerts.size());\n\t\t\tfor (uint i = 0; i < indexRemap.size(); ++i)\n\t\t\t\tif (indexRemap[i] != -1)\n\t\t\t\t\tnewNormals[indexRemap[i]] = normals().at(i);\n\t\t\tnormals(newNormals);\n\t\t}\n\n\n\t\tif (hasTexCoords())\n\t\t{\n\t\t\tMesh::UVs\tnewUVs(newVerts.size());\n\t\t\tfor (uint i = 0; i < indexRemap.size(); ++i)\n\t\t\t\tif (indexRemap[i] != -1)\n\t\t\t\t\tnewUVs[indexRemap[i]] = texCoords().at(i);\n\t\t\ttexCoords(newUVs);\n\t\t}\n\n\t\ttriangles(newTris);\n\t\tvertices(newVerts);\n\t}\n\n\tstd::vector<std::vector<int> > Mesh::removeDisconnectedComponents()\n\t{\n\t\tstd::vector<std::vector<int> > allComponents;\n\n\t\tstd::vector<std::vector<int>> v_triangles(vertices().size());\n\t\tint t_id = 0;\n\t\tfor (const auto& t : triangles()) {\n\t\t\tfor (int k = 0; k < 3; ++k) {\n\t\t\t\tv_triangles[t[k]].push_back(t_id);\n\t\t\t}\n\t\t\t++t_id;\n\t\t}\n\n\n\t\tstd::vector<bool> wasVisited(vertices().size(), false);\n\t\tint v_id = 0;\n\t\tfor (const auto& v : vertices()) {\n\n\t\t\tif (!wasVisited[v_id]) {\n\t\t\t\tstd::priority_queue<int> next_ids;\n\t\t\t\tnext_ids.push(v_id);\n\t\t\t\twasVisited[v_id] = true;\n\n\t\t\t\tstd::vector<int> component;\n\n\t\t\t\twhile (next_ids.size() > 0) {\n\t\t\t\t\tint next_id = next_ids.top();\n\t\t\t\t\tnext_ids.pop();\n\t\t\t\t\tcomponent.push_back(next_id);\n\n\t\t\t\t\tfor (int t : v_triangles[next_id]) {\n\t\t\t\t\t\tfor (int k = 0; k < 3; ++k) {\n\t\t\t\t\t\t\tint other_v_id = triangles()[t][k];\n\t\t\t\t\t\t\tif (!wasVisited[other_v_id]) {\n\t\t\t\t\t\t\t\tnext_ids.push(other_v_id);\n\t\t\t\t\t\t\t\twasVisited[other_v_id] = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tallComponents.emplace_back(component);\n\t\t\t}\n\t\t\t++v_id;\n\t\t}\n\n\t\treturn allComponents;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Mesh.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <vector>\n# include <map>\n# include <sstream>\n\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/graphics/MeshBufferGL.hpp\"\n# include \"core/graphics/Image.hpp\"\n\n// Be sure to use STL objects from client's dll version by exporting this declaration (see warning C4251)\n//template class SIBR_GRAPHICS_EXPORT std::vector<Vector3f>;\n//template class SIBR_GRAPHICS_EXPORT std::vector<Vector3u>;\n\nnamespace sibr\n{\n\t/** Store both CPU and GPU data for a geometric mesh.\n\t\tProvide many processing and display methods.\n\t\\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT Mesh\n\t{\n\t\tSIBR_CLASS_PTR(Mesh);\n\n\tpublic:\n\n\t\ttypedef\tstd::vector<Vector3f>\tVertices;\n\t\ttypedef\tstd::vector<Vector3f>\tNormals;\n\t\ttypedef std::vector<Vector3u>\tTriangles;\n\t\ttypedef\tstd::vector<Vector3f>\tColors;\n\t\ttypedef\tstd::vector<Vector2f>\tUVs;\n\n\t\t/** Mesh rendering mode. */\n\t\tenum RenderMode\n\t\t{\n\t\t\tPointRenderMode,\n\t\t\tLineRenderMode,\n\t\t\tFillRenderMode\n\t\t};\n\n\t\t/** Mesh rendering options. */\n\t\tstruct RenderingOptions {\n\t\t\tbool depthTest = true; ///< Should depth test be performed.\n\t\t\tbool backFaceCulling = true; ///< Should back faces be culled.\n\t\t\tRenderMode mode = FillRenderMode; ///< Rendering mode: points, lines, filled.\n\t\t\tbool frontFaceCulling = false; ///< Cull fornt faces.\n\t\t\tbool invertDepthTest = false; ///< Invert the depth test.\n\t\t\tbool tessellation = false; ///< Is there a tessellation shader\n\t\t\tbool adjacency = false;\n\t\t};\n\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t\\param withGraphics init associated OpenGL buffers object (requires an openGL context)\n\t\t*/\n\t\tMesh( bool withGraphics = true );\n\n\t\t/** Set vertices.\n\t\t\\param vertices the new vertices\n\t\t*/\n\t\tinline void\tvertices(const Vertices& vertices);\n\n\t\t/** Set vertices from a vector of floats (linear).\n\t\t\\param vertices the new vertices\n\t\t*/\n\t\tvoid vertices( const std::vector<float>& vertices );\n\t\t\n\t\t/** \\return a reference to the vertices. */\n\t\tinline const Vertices& vertices( void ) const;\n\n\t\t/** Update a specific vertex position\n\t\t\\param vertex_id the vertex location in the list\n\t\t\\param v the new value\n\t\t\\note If the mesh is used by the GPU, data will be udpated.\n\t\t*/\n\t\tinline void replaceVertice(int vertex_id, const sibr::Vector3f & v) ;\n\n\t\t/** \\return a deep copy of the mesh. */\n\t\tMesh::Ptr clone() const;\n\n\t\t/** \\return vertices in an array using the following format:\n\t\t {0x, 0y, 0z, 1x, 1y, 1z, 2x, 2y, 2z, ...}.\n\t\t Useful for rendering and converting to another mesh\n\t\t struct.\n\t\t */\n\t\tinline const float* vertexArray( void ) const;\n\n\t\t/** Set triangles. Each triangle contains 3 indices and\n\t\t each indice correspond to a vertex in the vertices list.\n\t\t \\param triangles the list of indices to use\n\t\t */\n\t\tinline void\ttriangles(const Triangles& triangles);\n\t\t\n\t\t/** Set triangles. Using a flat vector of uints.\n\t\t\\param triangles the new indices\n\t\t*/\n\t\tvoid\ttriangles(const std::vector<uint>& triangles);\n\t\t\n\t\t/** \\return a reference to the triangles list. */\n\t\tinline const Triangles& triangles( void ) const;\n\t\t\n\t\t/** \\return triangles in an array using the following format:\n\t\t {0a, 0b, 0c, 1a, 1b, 1c, 2a, 2b, 2c, ...}.\n\t\t Useful for rendering and converting to another mesh\n\t\t struct.\n\t\t */\n\t\tinline const uint* triangleArray( void ) const;\n\n\t\t/** Set vertex colors.\n\t\t\\param colors the new vertex colors\n\t\t*/\n\t\tinline void\tcolors( const Colors& colors );\n\n\t\t/** \\return a reference to the vertex color list. */\n\t\tinline const Colors& colors( void ) const;\n\n\t\t/** \\return true if each vertex has a color assigned. */\n\t\tinline bool\thasColors( void ) const;\n\n\t\t/** \\return colors in an array using the following format:\n\t\t {0r, 0g, 0b, 1r, 1g, 1b, 2r, 2g, 2b, ...}.\n\t\t Useful for rendering and converting to another mesh\n\t\t struct.*/\n\t\tinline const float* colorArray( void ) const;\n\n\t\t/** Set vertex texture coordinates.\n\t\t\\param texcoords the new vertex texture coordinates\n\t\t*/\n\t\tinline void\ttexCoords( const UVs& texcoords );\n\n\t\t/** Set texture coordinates using a flat vector of floats.\n\t\t\\param texcoords the new vertex texture coordinates\n\t\t*/\n\t\tvoid\t\ttexCoords( const std::vector<float>& texcoords );\n\n\t\t/** \\return a reference to the vertex texture coordinates list. */\n\t\tinline const UVs& texCoords( void ) const;\n\n\t\t/** \\return true if each vertex has texture coordinates assigned. */\n\t\tinline bool\thasTexCoords( void ) const;\n\n\t\t/** \\return texture coordinate in an array using the following format:\n\t\t {0u, 0v, 1u, 1v, 2u, 2v, ...}\n\t\t Useful for rendering and converting to another mesh\n\t\t struct.*/\n\t\tinline const float* texCoordArray( void ) const;\n\n\t\t/** \\return texture image file name */\n\t\tstd::string getTextureImageFileName()\tconst { return _textureImageFileName; }\n\n\t\t/** Set vertex normals.\n\t\t\\param normals the new vertex normals\n\t\t*/\n\t\tinline void\tnormals(const Normals& normals);\n\n\t\t/** Set normals using a flat vector of floats.\n\t\t\\param normals the new vertex normals\n\t\t*/\n\t\tvoid\tnormals(const std::vector<float>& normals);\n\t\t\n\t\t/** \\return a reference to the vertex normals list. */\n\t\tinline const Normals& normals( void ) const;\n\n\t\t/** \\return true if each vertex has a normal assigned. */\n\t\tinline bool\thasNormals( void ) const;\n\t\t\n\t\t/** \\return normals in an array using the following format:\n\t\t {0x, 0y, 0z, 1x, 1y, 1z, 2x, 2y, 2z, ...}.\n\t\t Useful for rendering and converting to another mesh\n\t\t struct.*/\n\t\tinline const float* normalArray( void ) const;\n\n\t\t/** Make the mesh whole, ie it will have default values for all components (texture, materials, colors, etc)\n\t\t  It is useful when merging two meshes. If the second one is missing some attributes, the merging will break the mesh state if it isn't made whole.\n\t\t  */\n\t\tvoid\tmakeWhole(void);\n\n\t\t/** Generate vertex normals by using the average of\n\t\t all triangle normals around a each vertex.\n\t\t */\n\t\tvoid\tgenerateNormals( void );\n\n\t\t/** Generate smooth vertex normals by using the average of\n\t\t all triangle normals around a each vertex and iterating this process.\n\t\t Takes also the area of triangles as a weight.\n\t\t \\param numIter iteration count\n\t\t */\n\t\tvoid\tgenerateSmoothNormals(int numIter);\n\n\t\t/** Generate smooth vertex normals by using the average of\n\t\t all triangle normals around a each vertex and iterating this process.\n\t\t Takes also the area of triangles as a weight.\n\t\t This methods also consider the fact that because of texture coordinates we may have duplicates vertices\n\t\t \\param numIter iteration count\n\t\t */\n\t\tvoid\tgenerateSmoothNormalsDisconnected(int numIter);\n\n\t\t/** Perform laplacian smoothing on the mesh vertices.\n\t\t\\param numIter smoothing iteration count\n\t\t\\param updateNormals should the normals be recomputed after smoothing\n\t\t*/\n\t\tvoid laplacianSmoothing(int numIter, bool updateNormals);\n\n\t\t/** Perform adaptive Taubin smoothing on the mesh vertices.\n\t\t\\param numIter smoothing iteration count\n\t\t\\param updateNormals should the normals be recomputed after smoothing\n\t\t*/\n\t\tvoid adaptativeTaubinSmoothing(int numIter, bool updateNormals);\n\n\t\t/** Generate a new mesh given a boolean function that\n\t\t  state if each vertex should be kept or not.\n\t\t  \\param func the function that, based on a vertex ID, tells if it should be kept or not\n\t\t  \\return the submesh\n\t\t  */\n\t\tMesh generateSubMesh(std::function<bool(int)> func) const;\n\n\t\t/** Load a mesh from the disk.\n\t\t\\param filename the file path\n\t\t\\return a success flag\n\t\t\\note Supports OBJ and PLY for now.\n\t\t*/\n\t\tbool\tload( const std::string& filename, const std::string& dataset_path = \"\" );\n\t\t/* test for SfM */\n\t\tbool\tloadSfM( const std::string& filename, const std::string& dataset_path = \"\" );\n\t\t\n\t\t/** Load a scene from a set of mitsuba XML scene files (referencing multiple OBJs/PLYs). \n\t\tIt handles instances (duplicating the geoemtry and applying the per-instance transformation).\n\t\t\\param filename the file path\n\t\t\\return a success flag\n\t\t*/\n\t\tbool\tloadMtsXML(const std::string& filename);\n\n\t\t/** Save the mesh to the disk. When saving as a PLY, use the universal flag to indicate if you want this mesh to be readable\n\t\t by most 3d applications (e.g. MeshLab). In this other case, the mesh will be saved with higher-precision custom PLY attributes.\n\t\t\\param filename the file path\n\t\t\\param universal if true, the mesh will be compatible with external 3D viewers\n\t\t\\param textureName name of a texture to reference in the file (Meshlab compatible)\n\t\t\\note Supports OBJ (ignoring the universal flag) and PLY for now.\n\t\t */\n\t\tvoid\tsave( const std::string& filename, bool universal=false, const std::string& textureName=\"TEXTURE_NAME_TO_PUT_IN_THE_FILE\" ) const;\n\n\t\t/** Save the mesh to .ply file (using the binary version).\n\t\t \\param filename the file path\n\t\t \\param universal indicates if you want this mesh to be readable by most 3d viewer application (e.g. MeshLab). In this other case, the mesh will be saved with higher-precision custom PLY attributes.\n\t\t \\param textureName name of a texture to reference in the file (Meshlab compatible) \n\t\t*/\n\t\tbool\t\tsaveToBinaryPLY( const std::string& filename, bool universal=false, const std::string& textureName = \"TEXTURE_NAME_TO_PUT_IN_THE_FILE\") const;\n\t\t\n\t\t/** Save the mesh to .ply file (using the ASCII version).\n\t\t \\param filename the file path\n\t\t \\param universal indicates if you want this mesh to be readable by most 3d viewer application (e.g. MeshLab).\n\t\t \\param textureName name of a texture to reference in the file (Meshlab compatible)\n\t\t In this other case, the mesh will be saved with higher-precision custom PLY attributes.\n\t\t*/\n\t\tbool\t\tsaveToASCIIPLY( const std::string& filename, bool universal=false, const std::string& textureName=\"TEXTURE_NAME_TO_PUT_IN_THE_FILE\" ) const;\n\n\t\t/** Save the mesh to .obj file.\n\t\t \\param filename the file path\n\t\t \\warning the vertex colros won't be saved\n\t\t*/\n\t\tbool\t\tsaveToObj( const std::string& filename) const;\n\n\t\t/* Export to OFF file format stream, can be used to convert to CGAL mesh data structures.\n\t\t\\param verbose display additional info\n\t\t\\return a stream containing the content of the mesh in the OFF format\n\t\t*/\n\t\tstd::stringstream getOffStream(bool verbose = false) const;\n\n\t\t/* Load from OFF file format stream, can be used to convert from CGAL mesh data structures.\n\t\t\\param stream a stream containing the content of the mesh in the OFF format\n\t\t\\param generateNormals should the normals be generated\n\t\t*/\n\t\tvoid fromOffStream(std::stringstream& stream, bool generateNormals = true);\n\n\t\t/** Render the geometry using OpenGL.\n\t\t\\param depthTest should depth testing be performed\n\t\t\\param backFaceCulling should culling be performed\n\t\t\\param mode the primitives rendering mode\n\t\t\\param frontFaceCulling should the culling test be flipped\n\t\t\\param invertDepthTest should the depth test be flipped (GL_GREATER_THAN)\n\t\t\\param tessellation should the rendering call tesselation shaders\n\t\t\\param adjacency should we get adjacent triangles info in geometry shader\n\t\t*/\n\t\tvoid\trender(\n\t\t\tbool depthTest = true,\n\t\t\tbool backFaceCulling = true,\n\t\t\tRenderMode mode = FillRenderMode,\n\t\t\tbool frontFaceCulling = false,\n\t\t\tbool invertDepthTest = false,\n\t\t\tbool tessellation = false,\n\t\t\tbool adjacency = false\n\t\t) const;\n\n\t\t/** Render a part of the geometry (taken either from the index buffer or directly in the vertex buffer) using OpenGL.\n\t\t\\param begin first item to render index\n\t\t\\param end last item to render index\n\t\t\\param depthTest should depth testing be performed\n\t\t\\param backFaceCulling should culling be performed\n\t\t\\param mode the primitives rendering mode\n\t\t\\param frontFaceCulling should the culling test be flipped\n\t\t\\param invertDepthTest should the depth test be flipped (GL_GREATER_THAN)\n\t\t*/\n\t\tvoid\trenderSubMesh(unsigned int begin, unsigned int end,\n\t\t\tbool depthTest = true,\n\t\t\tbool backFaceCulling = true,\n\t\t\tRenderMode mode = FillRenderMode,\n\t\t\tbool frontFaceCulling = false,\n\t\t\tbool invertDepthTest = false\n\t\t) const;\n\n\t\t/** Force upload of data to the GPU.\n\t\t\\param adjacency should we give adjacent triangles info in buffer\n\t\t*/\n\t\tvoid\tforceBufferGLUpdate( bool adjacency = false ) const;\n\n\t\t/** Delete GPU mesh data. */\n\t\tvoid\tfreeBufferGLUpdate(void) const;\n\n\t\t/** Render the mesh vertices as points.\n\t\t\\param depthTest should depth testing be performed\n\t\t*/\n\t\tvoid\trender_points(bool depthTest) const;\n\n\t\t/** Render the mesh vertices as points.\n\t\t\\note The depth test state will be whatever it is currently set to.\n\t\t*/\n\t\tvoid\trender_points(void) const;\n\n\t\t/** Render the mesh vertices as successive lines.\n\t\t\\note The depth test state will be whatever it is currently set to.\n\t\t*/\n\t\tvoid\trender_lines(void) const;\n\n\t\t/** Merge another mesh into this one.\n\t\t\\param other the mesh to merge\n\t\t\\sa makeWhole\n\t\t*/\n\t\tvoid\t\tmerge( const Mesh& other );\n\n\t\t/** Erase some of the triangles.\n\t\t\\param faceIDList a list of triangle IDs to erase\n\t\t*/\n\t\tvoid\t\teraseTriangles(const std::vector<uint>& faceIDList);\n\n\t\t/** Submesh extraction options. */\n\t\tenum class VERTEX_LIST_CHOICE { KEEP, REMOVE };\n\t\t\n\t\t/** Submesh structure. */\n\t\tstruct SubMesh {\n\t\t\tstd::shared_ptr<sibr::Mesh> meshPtr; ///< Mesh composed of the extracted vertices.\n\t\t\tstd::vector<int> complementaryVertices; /// < Vertices present in at least one removed triangle, can be used as an arg to extractSubMesh() to get the complementary mesh.\n\t\t};\n\n\t\t/** Extract a submesh based on a list of vertices to keep/remove.\n\t\t\\param vertices a list of vertex indices\n\t\t\\param v_choice should the listed vertices be removed or kept\n\t\t\\return the extracted submesh with additional information\n\t\t*/\n\t\tSubMesh extractSubMesh(const std::vector<int> & vertices, VERTEX_LIST_CHOICE v_choice) const;\n\n\t\t/** \\return a copy of the mesh with inverted faces (flipping IDs). */\n\t\tsibr::Mesh invertedFacesMesh() const;\n\n\t\t/** \\return a copy of the mesh with \"doubled\" faces (obtained by merging the current mesh with a copy with inverted faces. */\n\t\tsibr::Mesh::Ptr invertedFacesMesh2() const;\n\n\t\t/** \\return the path the mesh was loaded from. */\n\t\tinline const std::string getMeshFilePath( void ) const;\n\n\t\t/** \\return the mesh bouding box. */\n\t\tEigen::AlignedBox<float,3>\tgetBoundingBox( void ) const;\n\n\t\t/** \\return the mesh centroid. */\n\t\tsibr::Vector3f centroid() const;\n\n\t\t/** Estimated the mesh bounding sphere.\n\t\t\\param outCenter will contain the sphere center\n\t\t\\param outRadius will contain the sphere radius\n\t\t\\param referencedOnly if true, only consider vertices that are part of at least one face\n\t\t\\param usePCcenter if true, only consider vertices for center computation. Intended to be true when using the function on point clouds.\n\t\t*/\n\t\tvoid getBoundingSphere(Vector3f& outCenter, float& outRadius, bool referencedOnly=false, bool usePCcenter=false) const;\n\n\t\t/** Subdivide a mesh triangles until an edge size threshold is reached.\n\t\t\\param limitSize the maximum edge length allowed\n\t\t\\param maxRecursion maximum subdivision iteration count\n\t\t\\return the subdivided mesh\n\t\t\\bug SR: Can be stuck in a loop in some cases.\n\t\t*/\n\t\tsibr::Mesh::Ptr subDivide(float limitSize, size_t maxRecursion = std::numeric_limits<size_t>::max()) const;\n\n\t\t/** \\return the mean edge size computed over all triangles. */\n\t\tfloat meanEdgeSize() const;\n\n\t\t/** Split a mesh in its connected components. \n\t\t\\return a list of list of vertex indices, each list defining a component\n\t\t*/\n\t\tstd::vector<std::vector<int> > removeDisconnectedComponents();\n\n\t\t/** Generate a simple cube with normals.\n\t\t\\param withGraphics should the mesh be on the GPU\n\t\t\\return a cube mesh\n\t\t*/\n\t\tstatic sibr::Mesh::Ptr getTestCube(bool withGraphics = true);\n\n\t\t/** Generate a sphere mesh.\n\t\t\\param center the sphere center\n\t\t\\param radius the sphere radius\n\t\t\\param withGraphics should the mesh be on the GPU\n\t\t\\param precision number of subdivisions on each dimension\n\t\t\\return the sphere mesh\n\t\t*/\n\t\tstatic Mesh::Ptr getSphereMesh(const Vector3f& center, float radius, bool withGraphics = true, int precision = 50);\n\n\t\t/** Environment sphere generation options: top/bottom parts or both. */\n\t\tenum class PartOfSphere {WHOLE, UP, BOTTOM};\n\n\t\t/* Generate a simple environment sphere with UVs coordinates to use with lat-long environment maps.\n\t\t\\param center the sphere center\n\t\t\\param radius the sphere radius\n\t\t\\param zenith the up axis to orient the sphere around\n\t\t\\param north the horizontal north axis\n\t\t\\param part which aprt of the sphere should be generated\n\t\t\\return the sphere mesh\n\t\t*/\n\t\tstatic sibr::Mesh::Ptr getEnvSphere(sibr::Vector3f center, float radius, sibr::Vector3f zenith, sibr::Vector3f north,\n\t\t\t\t\t\t\t\t\t\t\tPartOfSphere part = PartOfSphere::WHOLE);\n\n\tprotected:\n\n\t\t/** Wrapper around a MeshBuffer, used to prevent copying OpenGL object IDs. */\n\t\tstruct BufferGL\n\t\t{\n\t\t\t/** Default constructor. */\n\t\t\tBufferGL(void) : dirtyBufferGL(true), bufferGL(nullptr) { }\n\n\t\t\t/** Copy constructor.  Copy the GPU buffers wrapper if it exists.\n\t\t\t\\param other object to copy.\n\t\t\t*/\n\t\t\tBufferGL(const BufferGL& other) { operator =(other); }\n\n\t\t\t/** Copy operator.  Copy the GPU buffers wrapper if it exists.\n\t\t\t\\param other object to copy.\n\t\t\t\\return itself\n\t\t\t*/\n\t\t\tBufferGL& operator =(const BufferGL& other) {\n\t\t\t\tbufferGL.reset(other.bufferGL? new MeshBufferGL() : nullptr);\n\t\t\t\tdirtyBufferGL = (other.bufferGL!=nullptr);\n\t\t\t\treturn *this;\n\t\t\t}\n\n\t\t\tbool\t\t\tdirtyBufferGL; ///< Should GL data be updated.\n\t\t\tstd::unique_ptr<MeshBufferGL>\tbufferGL; ///< Internal OpenGL data.\n\t\t};\n\t\tpublic: mutable BufferGL\t_gl; ///< Internal OpenGL data.\n\n\t\t// Seb: It would be better if MeshBufferGL (and GL stuffs) were outside this class.\n\t\t// It should work exactly like Image (CPU, here it's Mesh) and Texture(GPU version\n\t\t// of Image, here it's MeshBufferGL)\n\n\t\tVertices\t_vertices; ///< Vertex positions.\n\t\tTriangles\t_triangles; ///< Triangle indices.\n\t\tNormals\t\t_normals; ///< Vertex normals.\n\t\tColors\t\t_colors; ///< Vertex colors.\n\t\tUVs\t\t\t_texcoords; ///< Vertex UVs.\n\n\tprivate:\n\t\tstd::string _meshPath; ///< Source path, can be used to reload the mesh with/without graphics option in constructor\n\t\tstd::string _textureImageFileName; // filename of texture image\n\t\tmutable RenderingOptions _renderingOptions; // Keeps last rendering options\n\t};\n\n\t///// DEFINITION /////\n\n\tvoid\tMesh::vertices( const Vertices& vertices ) {\n\t\t_vertices = vertices; _gl.dirtyBufferGL = true;\n\t}\n\n\tconst Mesh::Vertices& Mesh::vertices( void ) const {\n\t\treturn _vertices;\n\t}\n\n\tinline void Mesh::replaceVertice(int vertex_id, const sibr::Vector3f & v)\n\t{\n\t\tif (vertex_id >= 0 && vertex_id < (int)(vertices().size())) {\n\t\t\t_vertices[vertex_id] = v;\n\t\t\t_gl.dirtyBufferGL = true;\n\t\t}\n\t}\n\n\tvoid\tMesh::triangles( const Triangles& triangles ) {\n\t\t_triangles = triangles; _gl.dirtyBufferGL = true;\n\t}\n\n\tconst Mesh::Triangles& Mesh::triangles( void ) const {\n\t\treturn _triangles;\n\t}\n\n\tvoid\tMesh::colors( const Colors& colors ) {\n\t\t_colors = colors; _gl.dirtyBufferGL = true;\n\t}\n\tconst Mesh::Colors& Mesh::colors( void ) const {\n\t\treturn _colors;\n\t}\n\tbool\tMesh::hasColors( void ) const {\n\t\treturn (_vertices.size() > 0 && _vertices.size() == _colors.size());\n\t}\n\n\tvoid\tMesh::normals( const Normals& normals ) {\n\t\t_normals = normals; _gl.dirtyBufferGL = true;\n\t}\n\tconst Mesh::Normals& Mesh::normals( void ) const {\n\t\treturn _normals;\n\t}\n\tbool\tMesh::hasNormals( void ) const {\n\t\treturn (_vertices.size() > 0 && _vertices.size() == _normals.size());\n\t}\n\n\tconst float* Mesh::vertexArray( void ) const {\n\t\treturn _vertices.empty()? nullptr : &(_vertices[0][0]);\n\t}\n\tconst uint* Mesh::triangleArray( void ) const {\n\t\treturn _triangles.empty() ? nullptr : &(_triangles[0][0]);\n\t}\n\tconst float* Mesh::colorArray( void ) const {\n\t\treturn _colors.empty() ? nullptr : &(_colors[0][0]);\n\t}\n\tconst float* Mesh::normalArray( void ) const {\n\t\treturn _normals.empty() ? nullptr : &(_normals[0][0]);\n\t}\n\n\tvoid\tMesh::texCoords( const UVs& texcoords ) {\n\t\t_texcoords = texcoords; _gl.dirtyBufferGL = true;\n\t}\n\n\tconst Mesh::UVs& Mesh::texCoords( void ) const {\n\t\treturn _texcoords;\n\t}\n\n\tbool\tMesh::hasTexCoords( void ) const {\n\t\treturn (_vertices.size() > 0 && _vertices.size() == _texcoords.size());\n\t\t//return !_texcoords.empty();\n\t}\n\n\tconst float* Mesh::texCoordArray( void ) const {\n\t\treturn _texcoords.empty() ? nullptr : &(_texcoords[0][0]);\n\t}\n\n\t//*/\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/MeshBufferGL.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/Mesh.hpp\"\n#include \"core/graphics/MeshBufferGL.hpp\"\n\n#include <unordered_map>\n\nnamespace sibr\n{\n\ttemplate <typename TTo, typename TFrom>\n\tstatic std::vector<TTo> \tprepareVertexData( \n\t\tconst std::vector<TFrom>& fromData, uint numVertices )\n\t{\n\t\tstatic_assert(sizeof(TFrom) >= sizeof(TTo),\n\t\t\t\"Conversion not automatically managed with these two types\");\n\t\t/*constexpr*/ const uint typeSize = sizeof(TFrom)/sizeof(TTo);\n\t\tstatic_assert((typeSize*sizeof(TTo)) == sizeof(TFrom),\n\t\t\t\"Provided types have not a correct size\");\t\n\n\t\tif (fromData.empty())\n\t\t\treturn std::vector<TTo>(); // empty\n\t\t//// If no data available, generate null ones\n\t\t//if (fromData.empty())\n\t\t//\treturn std::vector<TTo>(numIndices*typeSize, 0);\n\n\t\t// We are supposed to have ONE data element per Vertex\n\t\tSIBR_ASSERT(fromData.size() == numVertices);\n\t\t\n\t\tconst TTo*\tbeginptr = reinterpret_cast<const TTo*>(fromData.data());\n\t\tconst TTo*\tendptr = beginptr + (fromData.size()*typeSize);\n\t\treturn std::vector<TTo>(beginptr, endptr);\n\t}\n\n\ttemplate <typename TTo, typename TFrom>\n\tstatic void \tappendVertexData( \n\t\tstd::vector<TTo>& toData,\n\t\tconst std::vector<TFrom>& fromData)\n\t{\n\t\tif (fromData.empty())\n\t\t\treturn;\n\n\t\t/*constexpr*/ const uint typeSize = sizeof(TFrom)/sizeof(TTo);\n\t\tstatic_assert((typeSize*sizeof(TTo)) == sizeof(TFrom),\n\t\t\t\"Provided types have not a correct size\");\t\n\n\t\tconst TTo* beginptr = reinterpret_cast<const TTo*>(fromData.data());\n\t\tconst TTo* endptr = beginptr + (fromData.size()*typeSize);\n\t\ttoData.insert(toData.end(), beginptr, endptr);\n\t}\n\n\ttemplate <typename T>\n\tstatic inline uint \tgetVectorDataSize( const std::vector<T>& v )\n\t{\n\t\treturn (uint)(sizeof(T)*v.size());\n\t}\n\t//===========================================================================\n\n\tMeshBufferGL::MeshBufferGL( void )\n\t\t: _vaoId(0), _indexCount(0), _adjacentIndexCount(0), _vertexCount(0)\n\t{\n\t\t_bufferIds.fill(0);\n\t}\n\n\tMeshBufferGL::~MeshBufferGL( void )\n\t{\n\t\tfree();\n\t}\n\n\tMeshBufferGL::MeshBufferGL( MeshBufferGL&& other ) :\n\t\t_vaoId\t\t\t\t(std::move(other._vaoId)),\n\t\t_bufferIds\t\t\t(std::move(other._bufferIds)),\n\t\t_indexCount\t\t\t(std::move(other._indexCount)),\n\t\t_adjacentIndexCount\t(std::move(other._adjacentIndexCount)),\n\t\t_vertexCount\t\t(std::move(other._vertexCount))\n\t{\n\t}\n\n\tMeshBufferGL& MeshBufferGL::operator =( MeshBufferGL&& other )\n\t{\n\t\t_vaoId\t\t\t\t= std::move(other._vaoId);\n\t\t_bufferIds\t\t\t= std::move(other._bufferIds);\n\t\t_indexCount\t\t\t= std::move(other._indexCount);\n\t\t_adjacentIndexCount\t= std::move(other._adjacentIndexCount);\n\t\t_vertexCount\t\t= std::move(other._vertexCount);\n\n\t\treturn *this;\n\t}\n\n\tvoid MeshBufferGL::fetchIndices( const Mesh& mesh, bool adjacency )\n\t{\n\t\t// Create buffer for indices (called elements in opengl)\n\t\tstd::vector<GLuint> indices;\n\n\t\tif(adjacency) {\n\n\t\t\tstd::unordered_map<std::pair<GLuint, GLuint>, GLuint, hash_pair> trianglesByEdges(mesh.triangles().size() * 3);\n\t\t\tindices.reserve(mesh.triangles().size() * 6);\n\n\t\t\tfor(int i = 0; i < mesh.triangles().size(); i++)\n\t\t\t{\n\t\t\t\t// store triangles vertices by edges\n\n\t\t\t\ttrianglesByEdges.insert({ { mesh.triangles()[i][0], mesh.triangles()[i][1] }, mesh.triangles()[i][2] });\n\t\t\t\ttrianglesByEdges.insert({ { mesh.triangles()[i][1], mesh.triangles()[i][2] }, mesh.triangles()[i][0] });\n\t\t\t\ttrianglesByEdges.insert({ { mesh.triangles()[i][2], mesh.triangles()[i][0] }, mesh.triangles()[i][1] });\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < mesh.triangles().size(); i++)\n\t\t\t{\n\t\t\t\t// input triangle\n\t\t\t\t//   1 - 2\n\t\t\t\t//    \\ /\n\t\t\t\t//     0\n\n\t\t\t\t// adjacency list\n\t\t\t\t//     3\n\t\t\t\t//    / \\\n\t\t\t\t//   2 - 4\n\t\t\t\t//  / \\ / \\\n\t\t\t\t// 1 - 0 - 5\n\n\t\t\t\t// use reverse edges to find adjacent triangles\n\t\t\t\t\n\t\t\t\t// 0\n\t\t\t\tindices.push_back(mesh.triangles()[i][0]);\n\t\t\t\t// 1\n\t\t\t\tindices.push_back(trianglesByEdges[{ mesh.triangles()[i][1], mesh.triangles()[i][0] }]);\n\t\t\t\t// 2\n\t\t\t\tindices.push_back(mesh.triangles()[i][1]);\n\t\t\t\t// 3\n\t\t\t\tindices.push_back(trianglesByEdges[{ mesh.triangles()[i][2], mesh.triangles()[i][1] }]);\n\t\t\t\t// 4\n\t\t\t\tindices.push_back(mesh.triangles()[i][2]);\n\t\t\t\t// 5\n\t\t\t\tindices.push_back(trianglesByEdges[{ mesh.triangles()[i][0], mesh.triangles()[i][2] }]);\n\t\t\t}\n\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFADJINDEX]);\n\t\t\tglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*indices.size(), indices.data(), GL_STATIC_DRAW);\n\t\t\t_adjacentIndexCount = (uint)indices.size();\n\n\t\t\tCHECK_GL_ERROR;\n\t\t}\n\t\telse {\n\n\t\t\tindices.insert(indices.begin(), mesh.triangleArray(), mesh.triangleArray() + mesh.triangles().size()*3);\n\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFINDEX]);\n\t\t\tglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*indices.size(), indices.data(), GL_STATIC_DRAW);\n\t\t\t_indexCount = (uint)indices.size();\n\n\t\t\tCHECK_GL_ERROR;\n\t\t}\n\t}\n\n\tvoid \tMeshBufferGL::build( const Mesh& mesh, bool adjacency )\n\t{\n\t\tif (!_vaoId)\n\t\t{\n\t\t\tglGenVertexArrays(1, &_vaoId);\n\t\t\tglGenBuffers(BUFCOUNT, &_bufferIds[0]);\n\t\t}\n\n\t\tglBindVertexArray(_vaoId);\n\n\t\tCHECK_GL_ERROR;\n\n\t\tfetchIndices(mesh, false);\n\t\t\n\t\tif(adjacency)\n\t\t\tfetchIndices(mesh, true);\n\n\t\tuint numVertices = (uint)mesh.vertices().size();\n\t\t_vertexCount = numVertices;\n\t\t//SIBR_DEBUG(mesh.triangles().size());\n\t\tstd::vector<GLfloat> vertices = prepareVertexData<GLfloat>(\n\t\t\tmesh.vertices(), numVertices);\n\t\tstd::vector<GLfloat> colors = prepareVertexData<GLfloat>(\n\t\t\tmesh.colors(), numVertices);\n\t\tstd::vector<GLfloat> texcoords = prepareVertexData<GLfloat>(\n\t\t\tmesh.texCoords(), numVertices);\n\t\tstd::vector<GLfloat> normals = prepareVertexData<GLfloat>(\n\t\t\tmesh.normals(), numVertices);\n\n\t\t// Following this order:\n\t\t//VertexAttribLocation\t\t= 0,\n\t\t//ColorAttribLocation\t\t= 1,\n\t\t//TexCoordAttribLocation\t= 2,\n\t\t//NormalAttribLocation\t\t= 3,\n\n\t\t// Every data (from different types) are all put together into vertexData\n\t\tstd::vector<uint8> \tvertexData;\n\t\tvertexData.reserve(\n\t\t\tgetVectorDataSize(vertices)\t+\n\t\t\tgetVectorDataSize(colors)\t+\n\t\t\tgetVectorDataSize(texcoords)+\n\t\t\tgetVectorDataSize(normals));\n\n\t\tappendVertexData(vertexData, vertices);\n\t\tappendVertexData(vertexData, colors);\n\t\tappendVertexData(vertexData, texcoords);\n\t\tappendVertexData(vertexData, normals);\n\n\t\tglBindBuffer(GL_ARRAY_BUFFER, _bufferIds[BUFVERTEX]);\n\t\tglBufferData(GL_ARRAY_BUFFER, sizeof(uint8)*vertexData.size(), vertexData.data(), GL_STATIC_DRAW);\n\t\tCHECK_GL_ERROR;\n\n\t\tglVertexAttribPointer(VertexAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, (uint8_t*)(0));\n\t\tglEnableVertexAttribArray(VertexAttribLocation);\n\t\tglVertexAttribPointer(ColorAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, (uint8_t*)(0) + getVectorDataSize(vertices));\n\t\tglEnableVertexAttribArray(ColorAttribLocation);\n\t\tglVertexAttribPointer(TexCoordAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, (uint8_t*)(0) + getVectorDataSize(vertices) + getVectorDataSize(colors));\n\t\tglEnableVertexAttribArray(TexCoordAttribLocation);\n\t\tglVertexAttribPointer(NormalAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, (uint8_t*)(0) + getVectorDataSize(vertices) + getVectorDataSize(colors) + getVectorDataSize(texcoords));\n\t\tglEnableVertexAttribArray(NormalAttribLocation);\n\n\t\t/// \\todo TODO:\n\t\t/// We could ignore attrib that are empty (where mesh.colors().empty() == true, don't do anything with this).\n\t\t/// This could improve a bit performances.\n\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid\tMeshBufferGL::free(void)\n\t{\n\t\tif (_bufferIds[0] && _bufferIds[1] && _bufferIds[2])\n\t\t{\n\t\t\tglDeleteBuffers(3, _bufferIds.data());\n\t\t\t_bufferIds.fill(0);\n\t\t}\n\n\t\tif (_vaoId)\n\t\t{\n\t\t\tglDeleteVertexArrays(1, &_vaoId);\n\t\t\t_vaoId = 0;\n\t\t}\n\t}\n\n\tvoid  MeshBufferGL::draw(bool adjacency) const\n\t{\n\t\tglBindVertexArray(_vaoId);\n\n\t\tif(adjacency)\n\t\t{\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFADJINDEX]);\n\t\t\tglDrawElements(GL_TRIANGLES_ADJACENCY, _adjacentIndexCount, GL_UNSIGNED_INT, (void*)0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFINDEX]);\n\t\t\tglDrawElements(GL_TRIANGLES, _indexCount, GL_UNSIGNED_INT, (void*)0); \n\t\t}\n\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid MeshBufferGL::draw(unsigned int begin, unsigned int end, bool adjacency) const\n\t{\n\t\tconst unsigned int count = end-begin;\n\t\tglBindVertexArray(_vaoId);\n\t\t\n\t\tif(adjacency)\n\t\t{\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFADJINDEX]);\n\t\t\tglDrawElements(GL_TRIANGLES_ADJACENCY, count, GL_UNSIGNED_INT, (void*)(sizeof(GLuint) * begin));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFINDEX]);\n\t\t\tglDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, (void*)(sizeof(GLuint) * begin)); \n\t\t}\n\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid  MeshBufferGL::drawTessellated(void) const\n\t{\n\t\tglBindVertexArray(_vaoId);\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFINDEX]);\n\t\tglDrawElements(GL_PATCHES, _indexCount, GL_UNSIGNED_INT, (void*)0);\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid  MeshBufferGL::draw_lines(void) const\n\t{\n\t\tglBindVertexArray(_vaoId);\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIds[BUFINDEX]);\n\t\tglDrawElements(GL_LINES, _indexCount, GL_UNSIGNED_INT, (void*)0);\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid  MeshBufferGL::draw_points() const\n\t{\n\t\tglBindVertexArray(_vaoId);\n\t\tglDrawArrays(GL_POINTS, 0, _vertexCount);\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid  MeshBufferGL::draw_points(unsigned int begin, unsigned int end) const\n\t{\n\t\tconst unsigned int count = end - begin + 1;\n\t\tglBindVertexArray(_vaoId);\n\t\tglDrawArrays(GL_POINTS, begin, count);\n\t\tglBindVertexArray(0);\n\t}\n\n\tvoid MeshBufferGL::bind(void) const\n\t{\n\t\tglBindVertexArray(_vaoId);\n\t}\n\n\tvoid MeshBufferGL::unbind(void) const\n\t{\n\t\tglBindVertexArray(0);\n\n\t}\n\n\t\n\n} // namespace sibr \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/MeshBufferGL.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <array>\n# include <vector>\n# include \"core/graphics/Config.hpp\"\n\n\nnamespace sibr\n{\n\ttemplate <typename TTo, typename TFrom>\n\tstatic std::vector<TTo> prepareVertexData(const std::vector<TFrom>& fromData, uint numVertices);\n\n\ttemplate <typename TTo, typename TFrom>\n\tstatic void appendVertexData(std::vector<TTo>& toData, const std::vector<TFrom>& fromData);\n\n\ttemplate <typename T>\n\tstatic uint getVectorDataSize(const std::vector<T>& v);\n\n\n\tclass Mesh;\n\n\t/**\n\t* This class is used to render mesh. It act like a vertex buffer object\n\t* (in reality there also a vertex array object and maybe other data).\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT MeshBufferGL\n\t{\n\tpublic:\n\n\t\t/** Predefined shader attribute location. */\n\t\tenum AttribLocation\n\t\t{\n\t\t\tVertexAttribLocation\t= 0,\n\t\t\tColorAttribLocation\t\t= 1,\n\t\t\tTexCoordAttribLocation\t= 2,\n\t\t\tNormalAttribLocation\t= 3,\n\n\t\t\tAttribLocationCount\n\t\t};\n\t\t\n\t\t/** Predefined buffer location. */\n\t\tenum\n\t\t{\n\t\t\tBUFINDEX\t= 0,\n\t\t\tBUFVERTEX\t= 1,\n\t\t\tBUFADJINDEX\t= 2,\n\n\t\t\tBUFCOUNT\n\t\t};\n\n\t\t// A hash function used to hash a pair of any kind \n\t\tstruct hash_pair { \n\t\t\ttemplate <class T1, class T2> \n\t\t\tsize_t operator()(const std::pair<T1, T2>& p) const\n\t\t\t{ \n\t\t\t\tauto hash1 = std::hash<T1>{}(p.first); \n\t\t\t\tauto hash2 = std::hash<T2>{}(p.second); \n\t\t\t\treturn hash1 ^ hash2; \n\t\t\t} \n\t\t}; \n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tMeshBufferGL( void );\n\n\t\t/// Destructor.\n\t\t~MeshBufferGL( void );\n\n\t\t/// Move constructor.\n\t\tMeshBufferGL( MeshBufferGL&& other );\n\n\t\t/// Move operator.\n\t\tMeshBufferGL& operator =( MeshBufferGL&& other );\n\n\t\t/** Fetch indices from a mesh to insert them in the element buffer\n\t\t* \\param mesh the mesh to upload\n\t\t* \\param adjacency tells whether the indices should contain adjacents vertices\n\t\t* \\note This function can't fail (errors stop the program with a message).\n\t\t*/\n\t\tvoid\tfetchIndices( const Mesh& mesh, bool adjacency = false );\n\t\t\n\t\t/** Build from a mesh so you can then draw() it to render it.\n\t\t* \\param mesh the mesh to upload\n\t\t* \\param adjacency tells whether the indices should contain adjacents vertices\n\t\t* \\note This function can't fail (errors stop the program with a message).\n\t\t*/\n\t\tvoid\tbuild( const Mesh& mesh, bool adjacency = false );\n\n\t\t/** Delete the GPU buffer, freeing memory. */\n\t\tvoid\tfree(void);\n\n\t\t/** This bind and draw elements stored in the buffer.\n\t\t\t\\param adjacency adds adjacent triangles info to geometry shader\n\t\t*/\n\t\tvoid\tdraw(bool adjacency = false) const;\n\n\t\t/** This bind and draw elements in [begin, end[ stored in the buffer.\n\t\t\t\\param begin ID of the first primitive to render\n\t\t\t\\param end ID after the last primitive to render\n\t\t\t\\param adjacency adds adjacent triangles info to geometry shader\n\t\t*/\n\t\tvoid\tdraw(unsigned int begin, unsigned int end, bool adjacency = false) const;\n\n\t\t/** This bind and draw elements stored in the buffer with tessellation shader enabled. */\n\t\tvoid  drawTessellated(void) const;\n\t\t\n\t\t/** This bind and draw elements stored in the buffer, using pairs of indices to draw lines. */\n\t\tvoid draw_lines(void) const;\n\n\t\t/** This bind and draw vertex points stored in the buffer. */\n\t\tvoid\tdraw_points( void ) const;\n\n\t\t/** This bind and draw vertex points in [begin, end[ stored in the buffer.\n\t\t\t\\param begin ID of the first primitive to render\n\t\t\t\\param end ID after the last primitive to render\n\t\t*/\n\t\tvoid\tdraw_points( unsigned int begin, unsigned int end ) const;\n\n\t\t/** Bind the vertex and index buffers. */\n\t\tvoid\tbind(void) const;\n\n\t\t/** Bind only indexes (useful for combining with other form of mesh. e.g. SlicedMesh) */\n\t\tvoid\tbindIndices(void) const;\n\n\t\t/** Unbind arrays and buffers. */\n\t\tvoid\tunbind(void) const;\n\n\t\t/** Copy constructor (disabled). */\n\t\tMeshBufferGL(const MeshBufferGL&) = delete;\n\n\t\t/** Copy operator (disabled). */\n\t\tMeshBufferGL& operator =(const MeshBufferGL&) = delete;\n\n\tprivate:\n\t\t\n\t\tGLuint \t\t\t\t\t\t\t_vaoId; ///< Vertex array object ID.\n\t\tstd::array<GLuint, BUFCOUNT>\t_bufferIds; ///< Buffers IDs.\n\t\tuint \t\t\t\t\t\t\t_indexCount; ///< Number of elements in the index buffer.\n\t\tuint\t\t\t\t\t\t\t_adjacentIndexCount; ///< Number of elements in the triangles_adjacency index buffer.\n\t\tuint\t\t\t\t\t\t\t_vertexCount; ///< Number of elements in the vertex buffer.\n\n\t\tbool initVertexBuffer = false,\n\t\t\t initIndexBuffer = false,\n\t\t\t initAdjacentIndexBuffer = false;\n\t};\n\n\t///// DEFINITION /////\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/RenderTarget.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/RenderTarget.hpp\"\n//#define HEADLESS\n\nnamespace sibr\n{\n\tvoid\t\t\tblit(const IRenderTarget& src, const IRenderTarget& dst, GLbitfield mask, GLenum filter)\n\t{\n#ifdef HEADLESS\n\t\tSIBR_ERR << \"No named blit frame buffer in headless \" << std::endl;\n#else\n\t\tglBlitNamedFramebuffer(\n\t\t\tsrc.fbo(), dst.fbo(),\n\t\t\t0, 0, src.w(), src.h(),\n\t\t\t0, 0, dst.w(), dst.h(),\n\t\t\tmask, filter);\n#endif\n\t}\n\n\tvoid\t\t\tblit_and_flip(const IRenderTarget& src, const IRenderTarget& dst, GLbitfield mask, GLenum filter)\n\t{\n#ifdef HEADLESS\n\t\tSIBR_ERR << \"No named blit frame buffer in headless \" << std::endl;\n#else\n\t\tglBlitNamedFramebuffer(\n\t\t\tsrc.fbo(), dst.fbo(),\n\t\t\t0, 0, src.w(), src.h(),\n\t\t\t0, dst.h(), dst.w(), 0,\n\t\t\tmask, filter);\n#endif\n\t}\n\n\t\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/RenderTarget.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/graphics/Image.hpp\"\n# include \"core/graphics/Types.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/graphics/RenderUtility.hpp\"\n\n\n# define SIBR_MAX_SHADER_ATTACHMENTS (1<<3)\n\nnamespace sibr\n{\n\n\n\n\t/** Rendertarget interface. A render target wraps an OpenGL framebuffer, \n\t* that can have one depth buffer, one stencil buffer, and one or more color attachments.\n\t* This generic interface is typeless, \\sa RenderTarget.\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT IRenderTarget\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<IRenderTarget>\tPtr;\n\t\ttypedef std::unique_ptr<IRenderTarget>\tUPtr;\n\tpublic:\n\t\t/// Destructor.\n\t\tvirtual ~IRenderTarget(void) { }\n\n\t\t/** Get the texture handle of the t-th color attachment. \n\t\t\\param t the color attachment slot\n\t\t\\return the texture handle\n\t\t\\deprecated Use handle instead.\n\t\t*/\n\t\tvirtual GLuint texture(uint t = 0) const = 0;\n\n\t\t/** Get the texture handle of the t-th color attachment.\n\t\t\\param t the color attachment slot\n\t\t\\return the texture handle\n\t\t*/\n\t\tvirtual GLuint handle(uint t = 0) const = 0;\n\n\t\t/** Bind the rendertarget for drawing. All color buffers are bound, along \n\t\t\twith the depth and optional stencil buffers.*/\n\t\tvirtual void bind(void) = 0;\n\n\t\t/** Unbind the rendertarget.\n\t\t\\note This will bind the window rendertarget. */\n\t\tvirtual void unbind(void) = 0;\n\n\t\t/** Clear the content of the rendertarget. */\n\t\tvirtual void clear(void) = 0;\n\n\t\t/** \\return the rendertarget width. */\n\t\tvirtual uint   w(void) const = 0;\n\n\t\t/** \\return the rendertarget height. */\n\t\tvirtual uint   h(void) const = 0;\n\n\t\t/** \\return the framebuffer handle. */\n\t\tvirtual GLuint fbo(void) const = 0;\n\t};\n\n\t/**\n\t* A render target wraps an OpenGL framebuffer, that can have one depth buffer, \n\t* one stencil buffer, and one or more color attachments.\n\t* \\sa IRenderTarget.\n\t* \\ingroup sibr_graphics\n\t*/\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tclass RenderTarget : public IRenderTarget {\n\t\tSIBR_DISALLOW_COPY(RenderTarget);\n\tpublic:\n\t\ttypedef\t\tImage<T_Type, T_NumComp>\t\tPixelImage;\n\t\ttypedef\t\ttypename PixelImage::Pixel\t\tPixelFormat;\n\t\ttypedef\t\tstd::shared_ptr<RenderTarget<T_Type, T_NumComp>>\tPtr;\n\t\ttypedef\t\tstd::unique_ptr<RenderTarget<T_Type, T_NumComp>>\tUPtr;\n\n\tprivate:\n\n\t\tGLuint m_fbo = 0; ///< Framebuffer handle.\n\t\tGLuint m_depth_rb = 0; ///< Depth renderbuffer handle.\n\t\tGLuint m_stencil_rb = 0; ///< Stencil renderbuffer handle.\n\t\tGLuint m_textures[SIBR_MAX_SHADER_ATTACHMENTS]; ///< Color texture handles.\n\t\tuint   m_numtargets = 0; ///< Number of active color attachments.\n\t\tbool   m_autoMIPMAP = false; ///< Generate mipmaps on the fly.\n\t\tbool   m_msaa = false; ///< Use multisampled targets.\n\t\tbool   m_stencil = false; ///< Has a stencil buffer.\n\t\tuint   m_W = 0; ///< Width.\n\t\tuint   m_H = 0; ///< Height.\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tRenderTarget(void);\n\t\t\n\t\t/** Constructor and allocation.\n\t\t\\param w the target width\n\t\t\\param h the target height\n\t\t\\param flags options\n\t\t\\param num the number of color attachments.\n\t\t*/\n\t\tRenderTarget(uint w, uint h, uint flags = 0, uint num = 1);\n\n\t\t/// Destructor.\n\t\t~RenderTarget(void);\n\n\t\t/** Get the texture handle of the t-th color attachment.\n\t\t\\param t the color attachment slot\n\t\t\\return the texture handle\n\t\t\\deprecated Use handle instead.\n\t\t*/\n\t\tGLuint texture(uint t = 0) const;\n\t\t\n\t\t/** Get the texture handle of the t-th color attachment. \n\t\t\\param t the color attachment slot\n\t\t\\return the texture handle\n\t\t*/\n\t\tGLuint handle(uint t = 0) const;\n\n\t\t/** \\return the depth buffer handle. */\n\t\tGLuint depthRB() const;\n\n\t\t/** Bind the rendertarget for drawing. All color buffers are bound, along\n\t\t\twith the depth and optional stencil buffers.*/\n\t\tvoid bind(void);\n\n\t\t/** Unbind the rendertarget.\n\t\t\\note This will bind the window rendertarget. */\n\t\tvoid unbind(void);\n\n\t\t/** Clear the rendertarget buffers with default values.\n\t\t * \\warning This function will unbind the render target after clearing.\n\t\t */\n\t\tvoid clear(void);\n\n\t\t/** Clear the rendertarget buffers, using a custom clear color.\n\t\t * \\param v the clear color\n\t\t * \\warning This function will unbind the render target after clearing.\n\t\t * \\bug This function does not rescale values for uchar (so background is either 0 or 1)\n\t\t */\n\t\tvoid clear(const typename RenderTarget<T_Type, T_NumComp>::PixelFormat& v);\n\n\t\t/** Clear the stencil buffer only. */\n\t\tvoid clearStencil(void);\n\n\t\t/** Clear the depth buffer only. */\n\t\tvoid clearDepth(void);\n\n\t\t/** Readback the content of a color attachment into an sibr::Image on the CPU.\n\t\t\\param image will contain the texture content\n\t\t\\param target the color attachment index to read\n\t\t\\warning Might cause a GPU flush/sync.\n\t\t*/\n\t\ttemplate <typename TType, uint NNumComp>\n\t\tvoid readBack(sibr::Image<TType, NNumComp>& image, uint target = 0) const;\n\n\t\t/** Readback the content of a color attachment into a cv::Mat on the CPU.\n\t\t\\param image will contain the texture content\n\t\t\\param target the color attachment index to read\n\t\t\\warning Might cause a GPU flush/sync.\n\t\t*/\n\t\ttemplate <typename TType, uint NNumComp>\n\t\tvoid readBackToCVmat(cv::Mat& image, uint target = 0) const;\n\n\t\t/** Readback the content of the depth attachment into an sibr::Image on the CPU.\n\t\t\\param image will contain the depth content\n\t\t\\warning Might cause a GPU flush/sync.\n\t\t\\warning Image orientation might be inconsistent with readBack (flip around horizontal axis).\n\t\t*/\n\t\ttemplate <typename TType, uint NNumComp>\n\t\tvoid readBackDepth(sibr::Image<TType, NNumComp>& image) const;\n\n\t\t/** \\return the number of active color targets. */\n\t\tuint   numTargets(void)  const;\n\n\t\t/** \\return the target width. */\n\t\tuint   w(void)  const;\n\n\t\t/** \\return the target height. */\n\t\tuint   h(void)  const;\n\n\t\t/** \\return the framebuffer handle. */\n\t\tGLuint fbo(void)  const;\n\t};\n\n\t/**\n\tCopy the content of a render target to another render target, resizing if needed.\n\t\\param src source rendertarget\n\t\\param dst destination rendertarget\n\t\\param mask which part of the buffer to copy (color, depth, stencil).\n\t\\param filter filtering mode if the two rendertargets have different dimensions (linear or nearest)\n\t\\note The blit can only happen for color attachment 0 in both src and dst.\n\t\\warning If the mask contains the depth or stencil, filter must be GL_NEAREST\n\t \\ingroup sibr_graphics\n\t*/\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblit(const IRenderTarget& src, const IRenderTarget& dst, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_LINEAR);\n\n\t/**\n\tCopy the content of a render target to another render target, resizing if needed and flipping the result.\n\t\\param src source rendertarget\n\t\\param dst destination rendertarget\n\t\\param mask which part of the buffer to copy (color, depth, stencil).\n\t\\param filter filtering mode if the two rendertargets have different dimensions (linear or nearest)\n\t\\note The blit can only happen for color attachment 0 in both src and dst.\n\t\\warning If the mask contains the depth or stencil, filter must be GL_NEAREST\n\t \\ingroup sibr_graphics\n\t*/\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblit_and_flip(const IRenderTarget& src, const IRenderTarget& dst, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_LINEAR);\n\n\t/** Display a rendertarget color content in a popup window (backed by OpenCV).\n\t\\param rt the rendertarget to display\n\t\\param layer the color attachment to display\n\t\\param windowTitle name of the window\n\t\\param closeWindow should the window be closed when pressing a key\n\t\\ingroup sibr_graphics\n\t*/\n\ttemplate <typename T_Type, unsigned T_NumComp>\n\tstatic void\t\tshow( const RenderTarget<T_Type, T_NumComp> & rt, uint layer=0, const std::string& windowTitle=\"sibr::show()\" , bool closeWindow = true ) {\n\t\tsibr::Image<T_Type, T_NumComp>\timg(rt.w(), rt.h());\n\t\trt.readBack(img, layer);\n\t\tshow(img, windowTitle, closeWindow);\n\t}\n\t\n\t/** Display a rendertarget depth content in a popup window (backed by OpenCV).\n\t\\param rt the rendertarget to display\n\t\\param windowTitle name of the window\n\t\\param closeWindow should the window be closed when pressing a key\n\t\\ingroup sibr_graphics\n\t*/\n\ttemplate <typename T_Type, unsigned T_NumComp>\n\tstatic void\t\tshowDepth( const RenderTarget<T_Type, T_NumComp> & rt, const std::string& windowTitle=\"sibr::show()\" , bool closeWindow = true ) {\n\t\tsibr::Image<float, 3>\timg(rt.w(), rt.h());\n\t\trt.readBackDepth(img);\n\t\tshow(img, windowTitle, closeWindow);\n\t}\n\t\n\t/** Display a rendertarget alpha content as a grey map in a popup window (backed by OpenCV).\n\t\\param rt the rendertarget to display\n\t\\param windowTitle name of the window\n\t\\param closeWindow should the window be closed when pressing a key\n\t\\ingroup sibr_graphics\n\t*/\n\ttemplate <typename T_Type, unsigned T_NumComp>\n\tstatic void\t\tshowDepthFromAlpha( const RenderTarget<T_Type, T_NumComp> & rt, const std::string& windowTitle=\"sibr::show()\" , bool closeWindow = true ) {\n\t\tsibr::Image<float, 4>\timg(rt.w(), rt.h());\n\t\trt.readBack(img);\n\n\t\tfor (uint y = 0; y < img.h(); ++y)\n\t\t{\n\t\t\tfor (uint x = 0; x < img.w(); ++x)\n\t\t\t{\n\t\t\t\tsibr::ColorRGBA c = img.color(x, y);\n\t\t\t\tc = sibr::ColorRGBA(1.f, 1.f, 1.f, 0.f) * c[3];\n\t\t\t\tc[3] = 1.f;\n\t\t\t\timg.color(x, y, c);\n\t\t\t}\n\t\t}\n\n\t\tshow(img, windowTitle, closeWindow);\n\t}\n\n\n\t// --- Typedef RenderTarget --------------------------------------------------\n\n\ttypedef RenderTarget<unsigned char, 3>  RenderTargetRGB;\n\ttypedef RenderTarget<unsigned char, 4>  RenderTargetRGBA;\n\ttypedef RenderTarget<unsigned char, 1>  RenderTargetLum;\n\n\ttypedef RenderTarget<unsigned short, 1>    RenderTargetLum16;\n\ttypedef RenderTarget<unsigned short, 2>    RenderTargetUV16;\n\ttypedef RenderTarget<unsigned short, 3>    RenderTargetRGB16;\n\ttypedef RenderTarget<unsigned short, 4>    RenderTargetRGBA16;\n\n\ttypedef RenderTarget<int, 1>\t\t\t   RenderTargetInt1;\n\n\ttypedef RenderTarget<float, 3>          RenderTargetRGB32F;\n\ttypedef RenderTarget<float, 4>          RenderTargetRGBA32F;\n\ttypedef RenderTarget<float, 1>          RenderTargetLum32F;\n\ttypedef RenderTarget<float, 2>          RenderTargetUV32F;\n\n\n\t// --- DEFINITIONS RenderTarget --------------------------------------------------\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tRenderTarget<T_Type, T_NumComp>::RenderTarget(void) {\n\t\tm_fbo = 0;\n\t\tm_depth_rb = 0;\n\t\tm_numtargets = 0;\n\t\tm_W = 0;\n\t\tm_H = 0;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tRenderTarget<T_Type, T_NumComp>::RenderTarget(uint w, uint h, uint flags, uint num) {\n\t\tRenderUtility::useDefaultVAO();\n\n\t\tm_W = w;\n\t\tm_H = h;\n\n\t\tbool is_depth = (GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::isdepth != 0);\n\n\t\tint maxRenterTargets = 0;\n\t\tglGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxRenterTargets);\n\n\t\tSIBR_ASSERT(num <= uint(maxRenterTargets) && num > 0);\n\t\tSIBR_ASSERT(!is_depth || num == 1);\n\n\t\tif (flags & SIBR_GPU_INTEGER) {\n\t\t\tif (GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_internal_format < 0) {\n\t\t\t\tthrow std::runtime_error(\"Integer render  - format does not support integer mapping\");\n\t\t\t}\n\t\t}\n\n\t\tglGenFramebuffers(1, &m_fbo);\n\n\t\tif (!is_depth) {\n\t\t\tglGenRenderbuffers(1, &m_depth_rb); // depth buffer for color rt\n\t\t\t//glGenRenderbuffers(1, &m_stencil_rb); // stencil buffer for color rt\n\t\t} else\n\t\t\tm_depth_rb = 0;\n\n\t\tm_numtargets = num;\n\t\tm_autoMIPMAP = ((flags & SIBR_GPU_AUTOGEN_MIPMAP) != 0);\n\n\t\tm_msaa = ((flags & SIBR_GPU_MULSTISAMPLE) != 0);\n\t\tm_stencil = ((flags & SIBR_STENCIL_BUFFER) != 0);\n\n\t\tif (m_msaa && (m_numtargets != 1))\n\t\t\tthrow std::runtime_error(\"Only one MSAA render target can be attached.\");\n\t\tfor (uint n = 0; n < m_numtargets; n++) {\n\t\t\tif (m_msaa)\n\t\t\t\tbreak;\n\n\t\t\tglGenTextures(1, &m_textures[n]);\n\n\n\t\t\tglBindTexture(GL_TEXTURE_2D, m_textures[n]);\n\n\t\t\tif (flags & SIBR_CLAMP_UVS) {\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t\t}\n\n\t\t\t/// \\todo: following causes enum compare warning -Wenum-compare\n\t\t\tglTexImage2D(GL_TEXTURE_2D,\n\t\t\t\t0,\n\t\t\t\t(flags & SIBR_GPU_INTEGER)\n\t\t\t\t? GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_internal_format\n\t\t\t\t: GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::internal_format,\n\t\t\t\tw, h,\n\t\t\t\t0,\n\t\t\t\t(flags & SIBR_GPU_INTEGER)\n\t\t\t\t? GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_format\n\t\t\t\t: GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::format,\n\t\t\t\tGLType<typename PixelFormat::Type>::type,\n\t\t\t\tNULL);\n\n\n\t\t\tif (!m_autoMIPMAP) {\n#if SIBR_COMPILE_FORCE_SAMPLING_LINEAR\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n#else\n\t\t\t\tif (flags & SIBR_GPU_LINEAR_SAMPLING) {\n\t\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\t\t} else {\n\t\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n\t\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n\t\t\t\t}\n#endif\n\t\t\t} else { /// \\todo TODO: this crashes with 16F RT\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\t}\n\t\t}\n\n\n\t\tif (!m_msaa) {\n\t\t\tif (!is_depth) {\n\t\t\t\tglBindRenderbuffer(GL_RENDERBUFFER, m_depth_rb);\n\t\t\t\tif (!m_stencil)\n\t\t\t\t\tglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, w, h);\n\t\t\t\telse\n\t\t\t\t\tglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);\n\n\t\t\t\t//CHECK_GL_ERROR;\n\t\t\t\t//glBindRenderbuffer(GL_RENDERBUFFER, m_stencil_rb);\n\t\t\t\t//glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h);\n\t\t\t\tCHECK_GL_ERROR;\n\t\t\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\t\t\t\tfor (uint n = 0; n < m_numtargets; n++) {\n\t\t\t\t\tglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + n, GL_TEXTURE_2D, m_textures[n], 0);\n\t\t\t\t}\n\t\t\t\tCHECK_GL_ERROR;\n\t\t\t\tif (!m_stencil)\n\t\t\t\t\tglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depth_rb);\n\t\t\t\telse\n\t\t\t\t\tglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depth_rb);\n\t\t\t\t//CHECK_GL_ERROR;\n\t\t\t\t//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencil_rb);\n\t\t\t} else {\n\t\t\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\t\t\t\tglFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textures[0], 0);\n\t\t\t\tglDrawBuffer(GL_NONE);\n\t\t\t\tglReadBuffer(GL_NONE);\n\t\t\t}\n\t\t}\n\n\t\tif (m_msaa) {\n\t\t\tuint msaa_samples = ((flags >> 7) & 0xF) << 2;\n\n\t\t\tif (msaa_samples == 0)\n\t\t\t\tthrow std::runtime_error(\"Number of MSAA Samples not set. Please use SIBR_MSAA4X, SIBR_MSAA8X, SIBR_MSAA16X or SIBR_MSAA32X as an additional flag.\");\n\n\t\t\tglGenTextures(1, &m_textures[0]);\n\t\t\tglBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_textures[0]);\n\t\t\tCHECK_GL_ERROR;\n\t\t\t/// TODO: following causes enum compare warning -Wenum-compare\n\t\t\tglTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,\n\t\t\t\tmsaa_samples,\n\t\t\t\t(flags & SIBR_GPU_INTEGER)\n\t\t\t\t? GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_internal_format\n\t\t\t\t: GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::internal_format,\n\t\t\t\tw, h,\n\t\t\t\tGL_TRUE\n\t\t\t);\n\t\t\tglBindRenderbuffer(GL_RENDERBUFFER, m_depth_rb);\n\t\t\tglRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_samples, GL_DEPTH_COMPONENT32, w, h);\n\t\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\t\t\tglFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textures[0], 0);\n\t\t\tglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depth_rb);\n\t\t}\n\n\t\tGLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n\t\tif (status != GL_FRAMEBUFFER_COMPLETE) {\n\t\t\tswitch (status) {\n\t\t\tcase GL_FRAMEBUFFER_UNSUPPORTED:\n\t\t\t\tthrow std::runtime_error(\"Cannot create FBO - GL_FRAMEBUFFER_UNSUPPORTED error\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tSIBR_DEBUG(status);\n\t\t\t\tthrow std::runtime_error(\"Cannot create FBO (unknow reason)\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (m_autoMIPMAP) {\n\t\t\tfor (uint i = 0; i < m_numtargets; i++) {\n\t\t\t\tglBindTexture(GL_TEXTURE_2D, m_textures[i]);\n\t\t\t\tglGenerateMipmap(GL_TEXTURE_2D);\n\t\t\t}\n\t\t}\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, 0);\n\t\tCHECK_GL_ERROR;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tRenderTarget<T_Type, T_NumComp>::~RenderTarget(void) {\n\t\tfor (uint i = 0; i < m_numtargets; i++)\n\t\t\tglDeleteTextures(1, &m_textures[i]);\n\t\tglDeleteFramebuffers(1, &m_fbo);\n\t\tglDeleteRenderbuffers(1, &m_depth_rb);\n\t\tCHECK_GL_ERROR;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tGLuint RenderTarget<T_Type, T_NumComp>::depthRB() const {\n\t\treturn m_depth_rb;\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tGLuint RenderTarget<T_Type, T_NumComp>::texture(uint t) const {\n\t\tSIBR_ASSERT(t < m_numtargets);\n\t\treturn m_textures[t];\n\t}\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tGLuint RenderTarget<T_Type, T_NumComp>::handle(uint t) const {\n\t\tSIBR_ASSERT(t < m_numtargets);\n\t\treturn m_textures[t];\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::bind(void) {\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\t\tbool is_depth = (GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::isdepth != 0);\n\t\tif (!is_depth) {\n\t\t\tif (m_numtargets > 0) {\n\t\t\t\tGLenum drawbuffers[SIBR_MAX_SHADER_ATTACHMENTS];\n\t\t\t\tfor (uint i = 0; i < SIBR_MAX_SHADER_ATTACHMENTS; i++)\n\t\t\t\t\tdrawbuffers[i] = GL_COLOR_ATTACHMENT0 + i;\n\t\t\t\tglDrawBuffers(m_numtargets, drawbuffers);\n\t\t\t}\n\t\t} else {\n\t\t\tglDrawBuffer(GL_NONE);\n\t\t\tglReadBuffer(GL_NONE);\n\t\t}\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::unbind(void) {\n\t\tif (m_autoMIPMAP) {\n\t\t\tfor (uint i = 0; i < m_numtargets; i++) {\n\t\t\t\tglBindTexture(GL_TEXTURE_2D, m_textures[i]);\n\t\t\t\tglGenerateMipmap(GL_TEXTURE_2D);\n\t\t\t}\n\t\t}\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, 0);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::clear(void) {\n\t\tclear(PixelFormat());\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::clear(const typename RenderTarget<T_Type, T_NumComp>::PixelFormat& v) {\n\t\tbind();\n\t\tif (PixelFormat::NumComp == 1) {\n\t\t\tglClearColor(GLclampf(v[0]), 0, 0, 0);\n\t\t} else if (PixelFormat::NumComp == 2) {\n\t\t\tglClearColor(GLclampf(v[0]), GLclampf(v[1]), 0, 0);\n\t\t} else if (PixelFormat::NumComp == 3) {\n\t\t\tglClearColor(GLclampf(v[0]), GLclampf(v[1]), GLclampf(v[2]), 0);\n\t\t} else if (PixelFormat::NumComp == 4) {\n\t\t\tglClearColor(GLclampf(v[0]), GLclampf(v[1]), GLclampf(v[2]), GLclampf(v[3]));\n\t\t}\n\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\tunbind();\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::clearStencil() {\n\t\tbind();\n\t\tglClearStencil(0);\n\t\tglClear(GL_STENCIL_BUFFER_BIT);\n\t\tunbind();\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::clearDepth() {\n\t\tbind();\n\t\tglClear(GL_DEPTH_BUFFER_BIT);\n\t\tunbind();\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\ttemplate <typename T_IType, uint N_INumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::readBack(sibr::Image<T_IType, N_INumComp>& img, uint target) const {\n\t\t//void RenderTarget<T_Type, T_NumComp>::readBack(PixelImage& img, uint target) const {\n\t\tglFinish();\n\t\tif (target >= m_numtargets)\n\t\t\tSIBR_ERR << \"Reading back texture out of bounds\" << std::endl;\n\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\t\tbool is_depth = (GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::isdepth != 0);\n\t\tif (!is_depth) {\n\t\t\tif (m_numtargets > 0) {\n\t\t\t\tsibr::Image<T_Type, T_NumComp> buffer(m_W, m_H);\n\n\t\t\t\tGLenum drawbuffers = GL_COLOR_ATTACHMENT0 + target;\n\t\t\t\tglDrawBuffers(1, &drawbuffers);\n\t\t\t\tglReadBuffer(drawbuffers);\n\n\t\t\t\tglReadPixels(0, 0, m_W, m_H,\n\t\t\t\t\tGLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::format,\n\t\t\t\t\tGLType<typename PixelFormat::Type>::type,\n\t\t\t\t\tbuffer.data()\n\t\t\t\t);\n\n\t\t\t\tsibr::Image<T_IType, N_INumComp>\tout;\n\t\t\t\timg.fromOpenCV(buffer.toOpenCV());\n\t\t\t}\n\t\t} else\n\t\t\tSIBR_ERR << \"RenderTarget::readBack: This function should be specialized \"\n\t\t\t\"for handling depth buffer.\" << std::endl;\n\t\timg.flipH();\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, 0);\n\n\t}\n\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\ttemplate <typename T_IType, uint N_INumComp>\n\tvoid RenderTarget<T_Type, T_NumComp>::readBackToCVmat(cv::Mat& img, uint target) const {\n\n\t\tusing Infos = GLTexFormat<cv::Mat, T_IType, N_INumComp>;\n\n\t\tif (target >= m_numtargets)\n\t\t\tSIBR_ERR << \"Reading back texture out of bounds\" << std::endl;\n\n\t\tcv::Mat tmp(m_H, m_W, Infos::cv_type());\n\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\t\tbool is_depth = (Infos::isdepth != 0);\n\t\tif (!is_depth) {\n\t\t\tif (m_numtargets > 0) {\n\t\t\t\tGLenum drawbuffers = GL_COLOR_ATTACHMENT0 + target;\n\t\t\t\tglDrawBuffers(1, &drawbuffers);\n\t\t\t\tglReadBuffer(drawbuffers);\n\n\t\t\t\tglReadPixels(0, 0, m_W, m_H,\n\t\t\t\t\tInfos::format,\n\t\t\t\t\tInfos::type,\n\t\t\t\t\tInfos::data(tmp)\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tSIBR_ERR << \"RenderTarget::readBack: This function should be specialized \"\n\t\t\t\t\"for handling depth buffer.\" << std::endl; \\\n\t\t}\n\t\timg = Infos::flip(tmp);\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, 0);\n\t}\n\n\ttemplate <typename TType, uint NNumComp>\n\ttemplate <typename T_IType, uint N_INumComp>\n\tvoid RenderTarget<TType, NNumComp>::readBackDepth(sibr::Image<T_IType, N_INumComp>& image) const {\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, m_fbo);\n\n\t\tglReadBuffer(GL_COLOR_ATTACHMENT0);\n\n\t\tsibr::Image<float, 1> buffer(m_W, m_H);\n\t\tglReadPixels(0, 0, m_W, m_H,\n\t\t\tGL_DEPTH_COMPONENT,\n\t\t\tGL_FLOAT,\n\t\t\tbuffer.data()\n\t\t);\n\n\t\tsibr::Image<T_IType, N_INumComp>\tout(buffer.w(), buffer.h());\n\t\tfor (uint y = 0; y < buffer.h(); ++y)\n\t\t\tfor (uint x = 0; x < buffer.w(); ++x)\n\t\t\t\tout.color(x, y, sibr::ColorRGBA(1, 1, 1, 1.f) * buffer(x, y)[0]);\n\t\timage = std::move(out);\n\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, 0);\n\t}\n\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint   RenderTarget<T_Type, T_NumComp>::numTargets(void)  const { return m_numtargets; }\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint   RenderTarget<T_Type, T_NumComp>::w(void)  const { return m_W; }\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint   RenderTarget<T_Type, T_NumComp>::h(void)  const { return m_H; }\n\ttemplate<typename T_Type, unsigned int T_NumComp>\n\tuint   RenderTarget<T_Type, T_NumComp>::fbo(void)  const { return m_fbo; }\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/RenderUtility.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/Shader.hpp\"\n#include \"core/graphics/RenderUtility.hpp\"\n#include \"core/graphics/Window.hpp\"\n\n#define SIBR_WRITESHADER(src) \"#version 420 core\\n\" #src\n\n\n//#define RenderUtility::camStubDrawSize() 0.10f\n\n\n\nnamespace sibr\n{\n\n\n\tstatic const std::vector<float>&\tgetCameraStubVertices(float camStubSize = 0.1f)\n\t{\n\t\tstd::vector<float> _vBuffer(3*5);\n\t\t_vBuffer[3*0+0]= 1*camStubSize; _vBuffer[3*0+1]= 1*camStubSize; _vBuffer[3*0+2]=-3*camStubSize;\n\t\t_vBuffer[3*1+0]=-1*camStubSize; _vBuffer[3*1+1]= 1*camStubSize; _vBuffer[3*1+2]=-3*camStubSize;\n\t\t_vBuffer[3*2+0]=-1*camStubSize; _vBuffer[3*2+1]=-1*camStubSize; _vBuffer[3*2+2]=-3*camStubSize;\n\t\t_vBuffer[3*3+0]= 1*camStubSize; _vBuffer[3*3+1]=-1*camStubSize; _vBuffer[3*3+2]=-3*camStubSize;\n\t\t_vBuffer[3*4+0]= 0*camStubSize; _vBuffer[3*4+1]= 0*camStubSize; _vBuffer[3*4+2]= 0*camStubSize;\n\t\treturn _vBuffer;\n\t}\n\n\tstatic const std::vector<uint>&\t\tgetCameraStubIndices( void )\n\t{\n\t\tstatic std::vector<uint>\t_iBuffer;\n\n\t\tif (_iBuffer.empty())\n\t\t{\n\t\t\t_iBuffer.resize(3*6);\n\n\t\t\t_iBuffer[3*0+0]=0; _iBuffer[3*0+1]=1; _iBuffer[3*0+2]=4;\n\t\t\t_iBuffer[3*1+0]=1; _iBuffer[3*1+1]=2; _iBuffer[3*1+2]=4;\n\t\t\t_iBuffer[3*2+0]=2; _iBuffer[3*2+1]=4; _iBuffer[3*2+2]=3;\n\t\t\t_iBuffer[3*3+0]=0; _iBuffer[3*3+1]=4; _iBuffer[3*3+2]=3;\n\t\t\t_iBuffer[3*4+0]=0; _iBuffer[3*4+1]=1; _iBuffer[3*4+2]=3;\n\t\t\t_iBuffer[3*5+0]=1; _iBuffer[3*5+1]=2; _iBuffer[3*5+2]=3;\n\t\t}\n\t\treturn _iBuffer;\n\t}\n\n\n\tvoid RenderUtility::sendVertsTexToGPU(GLuint vertTexVBO, GLfloat vert[], GLfloat tcoord[], int svert, int stcoord) {\n\t\tglBindBuffer(GL_ARRAY_BUFFER, vertTexVBO);\n\t\tglBufferData(GL_ARRAY_BUFFER, svert+stcoord, NULL, GL_STATIC_DRAW);\n\t\tglBufferSubData(GL_ARRAY_BUFFER, 0, svert, vert);\n\t\tglBufferSubData(GL_ARRAY_BUFFER, svert, stcoord, tcoord);\n\t}\n\n\n\t/*static*/ void\t\tRenderUtility::renderScreenQuad( bool reverse, GLfloat tex_coor[] )\n\t{\n\t\tstatic GLfloat vert[] = { -1,-1,0,  1,-1,0,  1,1,0,  -1,1,0 };\n\t\tstatic GLfloat tcoord[8];\n\t\tstatic GLuint indexVBO, VAO, vertTexVBO;\n\n\t\tstatic bool firstTime = true;\n\t\t\n\t\tif(reverse)\n\t\t{\n\t\t\tGLfloat tmp[] = { 0,1,  0,0,  1,0,  1,1 };\n\t\t\tif( tex_coor )\n\t\t\t\tstd::memcpy(tmp, tex_coor, sizeof tmp );\n\n\t\t\tstd::memcpy(tcoord, tmp, sizeof tcoord);\n\t\t\tif( !firstTime )  // re-transfer to GPUs\n\t\t\t\tsendVertsTexToGPU(vertTexVBO, vert, tcoord, sizeof(vert), sizeof(tcoord));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tGLfloat tmp[] = { 0,0,  1,0,  1,1,  0,1 };\n\t\t\tif( tex_coor )\n\t\t\t\tstd::memcpy(tmp, tex_coor, sizeof tmp );\t\n\n\t\t\tstd::memcpy(tcoord, tmp, sizeof tcoord);\n\t\t\tif( !firstTime )  // re-transfer to GPU\n\t\t\t\tsendVertsTexToGPU(vertTexVBO, vert, tcoord, sizeof(vert), sizeof(tcoord));\n\t\t}\n\n\t\tstatic GLuint  ind[] = { 0,1,2,  0,2,3 };\n\n\t\tif( firstTime ) {\n\t\t\tfirstTime = false;\n\n\t\t\tglGenVertexArrays(1, &VAO);\n\t\t\tglBindVertexArray(VAO);\n\n\t\t\tglGenBuffers(1, &indexVBO);\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO);\n\t\t\tglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ind), ind, GL_STATIC_DRAW);\n\n\t\t\tglGenBuffers(1, &vertTexVBO);\n\t\t\tsendVertsTexToGPU(vertTexVBO, vert, tcoord, sizeof(vert), sizeof(tcoord));\n\t\t}\n\n\t\tglBindVertexArray(VAO);\n\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO);\n\t\tglBindBuffer(GL_ARRAY_BUFFER, vertTexVBO);\n\n\t\tglEnableVertexAttribArray(0);\n\t\tglEnableVertexAttribArray(1);\n\t\tglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);\n\t\tglVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(vert)\t);\n\n\t\tglDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0);\n\n\t\tglDisableVertexAttribArray(1);\n\t\tglDisableVertexAttribArray(0);\n\t}\n\n\tvoid\t\tRenderUtility::renderScreenQuad()\n\t{\n\t\tstatic GLfloat Fvert[] = { -1,-1,0,  1,-1,0,  1,1,0,  -1,1,0 };\n\t\tstatic GLfloat Ftcoord[] = { 0, 0, 1, 0, 1, 1, 0, 1 };\n\t\tstatic GLuint  Find[] = { 0,1,2,  0,2,3 };\n\t\tstatic GLuint FindexVBO, FVAO, FvertTexVBO;\n\t\tstatic bool FfirstTime = true;\n\t\tstatic int lastContextId = -1;\n\n\t\t//std::cout << lastContextId << \" \" << Window::contextId << std::endl;\n\t\tif (lastContextId != Window::contextId || FfirstTime) {\n\t\t\tlastContextId = Window::contextId;\n\t\t\tFfirstTime = false;\n\n\t\t\tglGenVertexArrays(1, &FVAO);\n\t\t\tglBindVertexArray(FVAO);\n\n\t\t\tglGenBuffers(1, &FindexVBO);\n\t\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, FindexVBO);\n\t\t\tglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Find), Find, GL_STATIC_DRAW);\n\n\t\t\tglGenBuffers(1, &FvertTexVBO);\n\t\t\tsendVertsTexToGPU(FvertTexVBO, Fvert, Ftcoord, sizeof(Fvert), sizeof(Ftcoord));\n\n\t\t\tglBindBuffer(GL_ARRAY_BUFFER, FvertTexVBO);\n\t\t\tglEnableVertexAttribArray(0);\n\t\t\tglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);\n\t\t\tglEnableVertexAttribArray(1);\n\t\t\tglVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(Fvert));\n\n\t\t\tglBindVertexArray(0);\n\t\t}\n\n\t\t\n\t\tglBindVertexArray(FVAO);\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, FindexVBO);\n\t\t\n\t\tconst GLboolean cullingWasEnabled = glIsEnabled(GL_CULL_FACE);\n\t\tglEnable(GL_CULL_FACE);\n\t\tglCullFace(GL_BACK);\n\n\t\tglDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0);\n\n\t\tif (!cullingWasEnabled) {\n\t\t\tglDisable(GL_CULL_FACE);\n\t\t}\n\n\t\tglBindVertexArray(0);\n\t}\n\n\n\t/*static*/ Mesh\t\tRenderUtility::createCameraStub(float camStubSize)\n\t{\n\t\t\n\t\tMesh m;\n\t\tm.vertices( getCameraStubVertices(camStubSize) );\n\t\tm.triangles( getCameraStubIndices() );\n\t\treturn m;\n\t}\n\n\t/*static*/ Mesh\t\tRenderUtility::createScreenQuad( void )\n\t{\n\t\tMesh::Vertices v;\n\t\tv.emplace_back(-1.0f, -1.0f, 0.0f);\n\t\tv.emplace_back( 1.0f, -1.0f, 0.0f);\n\t\tv.emplace_back( 1.0f,  1.0f, 0.0f);\n\t\tv.emplace_back(-1.0f,  1.0f, 0.0f);\n\t\tMesh::UVs tc;\n\t\ttc.emplace_back(0.0f,0.0f);\n\t\ttc.emplace_back(1.0f,0.0f);\n\t\ttc.emplace_back(1.0f,1.0f);\n\t\ttc.emplace_back(0.0f,1.0f);\n\t\tMesh::Triangles t;\n\t\tt.emplace_back(0);\n\t\tt.emplace_back(1);\n\t\tt.emplace_back(2);\n\t\tt.emplace_back(0);\n\t\tt.emplace_back(2);\n\t\tt.emplace_back(3);\n\n\n\t\tMesh m;\n\t\tm.vertices(v);\n\t\tm.texCoords(tc);\n\t\tm.triangles(t);\n\t\treturn m;\n\t}\n\n\tMesh::Ptr RenderUtility::createAxisGizmo()\n\t{\n\t\tconst float arrowShift = 0.2f;\n\t\tconst float arrowSpread = 0.1f;\n\n\t\tMesh::Vertices v = {\n\t\t\t// Axis X\n\t\t\t{-1,0,0}, {1,0,0},\n\t\t\t// Arrow X\n\t\t\t{1.0f - arrowShift, -arrowSpread, 0.0f},\n\t\t\t{1.0f - arrowShift, 0.0f, -arrowSpread},\n\t\t\t{1.0f - arrowShift, arrowSpread, 0.0f},\n\t\t\t{1.0f - arrowShift, 0.0f, arrowSpread},\n\n\t\t\t// Axis Y\n\t\t\t{0,-1,0}, {0,1,0},\n\t\t\t// Arrow Y\n\t\t\t{-arrowSpread, 1.0f - arrowShift, 0.0f},\n\t\t\t{0.0f, 1.0f - arrowShift, -arrowSpread},\n\t\t\t{arrowSpread, 1.0f - arrowShift, 0.0f},\n\t\t\t{0.0f, 1.0f - arrowShift, arrowSpread},\n\n\t\t\t// Axis Z\n\t\t\t{0, 0, -1}, {0, 0, 1},\n\t\t\t// Arrow Z\n\t\t\t{-arrowSpread, 0.0f, 1.0f - arrowShift},\n\t\t\t{0.0f, -arrowSpread, 1.0f - arrowShift},\n\t\t\t{arrowSpread, 0.0f, 1.0f - arrowShift},\n\t\t\t{0.0f, arrowSpread, 1.0f - arrowShift},\n\n\t\t\t// Letter X\n\t\t\t{1.0f + arrowShift - arrowSpread, -arrowSpread, 0.0f},\n\t\t\t{1.0f + arrowShift + arrowSpread, arrowSpread, 0.0f},\n\t\t\t{1.0f + arrowShift - arrowSpread, arrowSpread, 0.0f},\n\t\t\t{1.0f + arrowShift + arrowSpread, -arrowSpread, 0.0f},\n\t\t\t// Letter Y\n\t\t\t{0.0f, 1.0f + arrowShift - arrowSpread, 0.0f},\n\t\t\t{0.0f, 1.0f + arrowShift, 0.0f},\n\t\t\t{-arrowSpread, 1.0f + arrowShift + arrowSpread, 0.0f},\n\t\t\t{arrowSpread, 1.0f + arrowShift + arrowSpread, 0.0f},\n\t\t\t// Letter Z\n\t\t\t{0.0f, -arrowSpread, 1.0f + arrowShift - arrowSpread},\n\t\t\t{0.0f, -arrowSpread, 1.0f + arrowShift + arrowSpread},\n\t\t\t{0.0f, arrowSpread, 1.0f + arrowShift - arrowSpread},\n\t\t\t{0.0f, arrowSpread, 1.0f + arrowShift + arrowSpread}\n\t\t};\n\n\t\tMesh::Colors c = {\n\t\t\t// Colors X\n\t\t\t{1, 0, 0}, {1, 0, 0}, {1, 0, 0},\n\t\t\t{1, 0, 0}, {1, 0, 0}, {1, 0, 0},\n\t\t\t// Colors Y\n\t\t\t{0, 1, 0}, {0, 1, 0}, {0, 1, 0},\n\t\t\t{0, 1, 0}, {0, 1, 0}, {0, 1, 0},\n\t\t\t// Colors Z\n\t\t\t{0, 0, 1}, {0, 0, 1}, {0, 0, 1},\n\t\t\t{0, 0, 1}, {0, 0, 1}, {0, 0, 1},\n\t\t\t// Colors Letter X\n\t\t\t{1, 0, 0}, {1, 0, 0},\n\t\t\t{1, 0, 0}, {1, 0, 0},\n\t\t\t// Colors Letter Y\n\t\t\t{0, 1, 0}, {0, 1, 0},\n\t\t\t{0, 1, 0}, {0, 1, 0},\n\t\t\t// Colors Letter Z\n\t\t\t{0, 0, 1}, {0, 0, 1},\n\t\t\t{0, 0, 1}, {0, 0, 1}\n\t\t};\n\n\t\tMesh::Triangles t = {\n\t\t\t// Axis X\n\t\t\t{0, 1, 0},\n\t\t\t// Arrow X\n\t\t\t{1, 2, 3}, {1, 3, 4}, {1, 4, 5},\n\t\t\t{1, 5, 2}, {2, 3, 4}, {2, 3, 5},\n\t\t\t// Axis Y\n\t\t\t{6, 7, 6},\n\t\t\t// Arrow Y\n\t\t\t{7, 8, 9}, {7, 9, 10}, {7, 10, 11},\n\t\t\t{7, 11, 8}, {8, 9, 10}, {8, 9, 11},\n\t\t\t// Axis Z\n\t\t\t{12, 13, 12},\n\t\t\t// Arrow Z\n\t\t\t{13, 14, 15}, {13, 15, 16}, {13, 16, 17},\n\t\t\t{13, 17, 14}, {14, 15, 16}, {14, 15, 17},\n\n\t\t\t// Letter X\n\t\t\t{18, 19, 18}, {20, 21, 20},\n\t\t\t//Letter Y\n\t\t\t{22, 23, 22}, {24, 23, 24}, {25, 23, 25},\n\t\t\t//Letter Z\n\t\t\t{26, 28, 26}, {26, 29, 26}, {27, 29, 27}\n\t\t};\n\n\n\t\tauto out = std::make_shared<sibr::Mesh>();\n\t\tout->vertices(v);\n\t\tout->colors(c);\n\t\tout->triangles(t);\n\t\treturn out;\n\t}\n\n\n\t/*static*/ void\t\tRenderUtility::useDefaultVAO( void )\n\t{\n\t\tstatic GLuint gDefaultVAO = 0;\n\n\t\tif (!gDefaultVAO)\n\t\t\tglGenVertexArrays(1, &gDefaultVAO);\n\n\t\tglBindVertexArray(gDefaultVAO);\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/RenderUtility.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Matrix.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/graphics/Mesh.hpp\"\n\nnamespace sibr\n{\n\n\t/**\n\t\tHelpers for rendering basic debug objects (cameras, simple meshes,...)\n\t\t\\todo Clarify duplication with functionality in SceneDebugView.\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT RenderUtility\n\t{\n\tpublic:\n\n\t\t/** Create a basic cmaera stub.\n\t\t\\param camStubSize the stub scale\n\t\t\\return the mesh\n\t\t*/\n\t\tstatic Mesh\t\tcreateCameraStub( float camStubSize = 0.1f);\n\n\t\t/** Create a screenquad.\n\t\t\\return the mesh\n\t\t*/\n\t\tstatic Mesh\t\tcreateScreenQuad();\n\n\t\t/** Create a gizmo with X,Y,Z axis and labels (using R,G,B respectively).\n\t\t\\return the gizmo mesh\n\t\t*/\n\t\tstatic Mesh::Ptr createAxisGizmo();\n\n\t\t/** Bind a static VAO for which you can redefine vertices or do vertex pulling. */\n\t\tstatic void\t\tuseDefaultVAO( void );\n\n\t\t/** Draw a screenquad. */\n\t\tstatic void\t\trenderScreenQuad();\n\n\t\t/** Draw a screenquad.\n\t\t\\param reverse should the triangles be flipped\n\t\t\\param texCoor custom texture coordinates to use.\n\t\t*/\n\t\tstatic void\t\trenderScreenQuad( bool reverse, GLfloat texCoor[] = NULL );\n\n\tprivate:\n\n\t\t/** Send vertices to the GPU.\n\t\t\\param vertTexVBO the VBO id\n\t\t\\param vert the vertices data\n\t\t\\param tcoord the UV data\n\t\t\\param svert the vert size\n\t\t\\param stcoord the tcoord size\n\t\t*/\n\t\tstatic void sendVertsTexToGPU(GLuint vertTexVBO, GLfloat vert[], GLfloat tcoord[], int svert, int stcoord);\n\n\n\t};\n\n\t///// DEFINITIONS /////\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Shader.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n# include \"core/graphics/Shader.hpp\"\n# include \"core/system/Matrix.hpp\"\n#include \"core/system/String.hpp\"\n\n\n# ifndef SIBR_MAXIMIZE_INLINE\n#  include \"Shader.inl\"\n# endif\n\nnamespace sibr\n{\n\tGLuint GLShader::compileShader(const char* shader_code, GLuint type)\n\t{\n\t\tstd::string shader_type;\n\t\tswitch (type) {\n\t\tcase GL_VERTEX_SHADER:    shader_type = \"vertex\";   break;\n\t\tcase GL_FRAGMENT_SHADER:  shader_type = \"fragment\"; break;\n\t\tcase GL_GEOMETRY_SHADER:  shader_type = \"geometry\"; break;\n\t\tdefault: std::runtime_error(\"Shader types other than vertex/fragment/geometry not supported\");\n\t\t}\n\n\t\tGLuint id = glCreateShader(type);\n\t\tglShaderSource(id,1,&shader_code,NULL);\n\t\tglCompileShader(id);\n\n\t\tGLint compiled;\n\t\tglGetShaderiv(id,GL_COMPILE_STATUS, &compiled);\n\t\tif (!compiled) {\n\t\t\tGLint maxLength;\n\t\t\tglGetShaderiv(id, GL_INFO_LOG_LENGTH, &maxLength);\n\t\t\tchar* infoLog = new char[maxLength+1];\n\t\t\tGLint len = 0;\n\t\t\tglGetShaderInfoLog(id, maxLength, &len, infoLog);\n\t\t\tSIBR_WRG << \"GLSL \" << shader_type.c_str() << \" shader compilation failed for program \"\n\t\t\t\t<< m_Name.c_str() << std::endl\n\t\t\t\t<< infoLog << std::endl;\n\t\t\tdelete [] infoLog;\n\t\t\t(void)glGetError();\n\t\t\treturn 0;\n\t\t}\n\t\treturn id;\n\t}\n\n\tGLShader::GLShader(void) :\n\t\tm_Shader(0),\n\t\tm_Name(\"\"),\n\t\tm_Strict(false),\n\t\tm_Active(false)\n\t{}\n\n\tGLShader::~GLShader(void) {\n\t\tterminate();\n\t}\n\n\tvoid\t\t\tGLShader::init      ( GLuint s_handle )\n\t{\n\t\tm_Shader = s_handle;\n\t}\n\n\tvoid\t\t\tGLShader::setStrict ( bool s )\n\t{\n\t\tm_Strict = s;\n\t}\n\n\tGLuint\t\t\tGLShader::shader    ( void )  const\n\t{\n\t\treturn m_Shader;\n\t}\n\n\tstd::string\t\tGLShader::name      ( void )  const\n\t{\n\t\treturn m_Name;\n\t}\n\n\tbool\t\t\tGLShader::isReady   ( void )  const\n\t{\n\t\treturn m_Shader!=0;\n\t}\n\n\tbool\t\t\tGLShader::isActive  ( void )  const\n\t{\n\t\treturn m_Active;\n\t}\n\n\tbool\t\t\tGLShader::isStrict  ( void )  const\n\t{\n\t\treturn m_Strict;\n\t}\n\n\tbool GLShader::init(std::string name,\n\t\tstd::string vp_code,\n\t\tstd::string fp_code,\n\t\tstd::string gp_code,\n\t\tbool exitOnError,\n\t\tstd::string tcs_code,\n\t\tstd::string tes_code)\n\t{\n\t\tterminate();\n\n\t\tm_Name = name;\n\t\tm_Shader = glCreateProgram();\n\n\t\tCHECK_GL_ERROR;\n\n\t\tGLint vp = 0, fp = 0, gp = 0, tcs = 0, tes = 0;\n\n\t\tif (!vp_code.empty()) {\n\t\t\tvp = compileShader(vp_code.c_str(), GL_VERTEX_SHADER);\n\t\t\tif (!vp) return false;\n\t\t\tglAttachShader(m_Shader, vp);\n\t\t}\n\n\t\tif (!fp_code.empty()) {\n\t\t\tfp = compileShader(fp_code.c_str(), GL_FRAGMENT_SHADER);\n\t\t\tif (!fp) return false;\n\t\t\tglAttachShader(m_Shader, fp);\n\t\t}\n\n\t\tif (!gp_code.empty()) {\n\t\t\tgp = compileShader(gp_code.c_str(), GL_GEOMETRY_SHADER);\n\t\t\tif (!gp) return false;\n\t\t\tglAttachShader(m_Shader, gp);\n\t\t}\n\n\t\tif (!tcs_code.empty()) {\n\t\t\ttcs = compileShader(tcs_code.c_str(), GL_TESS_CONTROL_SHADER);\n\t\t\tif (!tcs) return false;\n\t\t\tglAttachShader(m_Shader, tcs);\n\t\t}\n\n\t\tif (!tes_code.empty()) {\n\t\t\ttes = compileShader(tes_code.c_str(), GL_TESS_EVALUATION_SHADER);\n\t\t\tif (!tes) return false;\n\t\t\tglAttachShader(m_Shader, tes);\n\t\t}\n\n\t\tCHECK_GL_ERROR;\n\n\t\tglLinkProgram(m_Shader);\n\n\t\tCHECK_GL_ERROR;\n\n\t\tGLint shader_linked;\n\n\t\tCHECK_GL_ERROR;\n\n\t\tglGetProgramiv(m_Shader, GL_LINK_STATUS, &shader_linked);\n\t\tif (!shader_linked) {\n\t\t\tGLint maxLength;\n\t\t\tglGetProgramiv(m_Shader, GL_INFO_LOG_LENGTH, &maxLength);\n\t\t\tchar* infoLog = new char[maxLength + 1];\n\t\t\tglGetProgramInfoLog(m_Shader, maxLength, NULL, infoLog);\n\t\t\tSIBR_WRG << \"GLSL program failed to link \" << m_Name.c_str() << std::endl\n\t\t\t\t<< \"Shader linking log:\" << std::endl\n\t\t\t\t<< infoLog << std::endl;\n\t\t\tdelete[] infoLog;\n\n\t\t\tif (exitOnError)\n\t\t\t\tSIBR_ERR << \"GLSL program failed to link\" << std::endl;\n\t\t}\n\n\t\tif (vp) glDeleteShader(vp);\n\t\tif (fp) glDeleteShader(fp);\n\t\tif (gp) glDeleteShader(gp);\n\t\tif (tcs) glDeleteShader(tcs);\n\t\tif (tes) glDeleteShader(tes);\n\n\t\tglUseProgram(0);\n\n\t\tCHECK_GL_ERROR;\n\t\treturn true;\n\t}\n\n\n\tbool GLShader::reload(\n\t\tstd::string vp_code,\n\t\tstd::string fp_code,\n\t\tstd::string gp_code ) {\n\t\t\t{ // Simple way to test if it compiles\n\t\t\t\tsibr::GLShader tester;\n\t\t\t\tif (tester.init(m_Name, vp_code, fp_code, gp_code) == false)\n\t\t\t\t{\n\t\t\t\t\tSIBR_WRG << \"Can't reload shader '\" << m_Name << \"' (see previous log entries)\" << std::endl;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn init(m_Name, vp_code, fp_code, gp_code);\n\t}\n\n\tvoid GLShader::getBinary(std::vector<char> & binary)\n\t{\n\t\tint count = 0;\n\t\tglGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &count);\n\t\tif (count <= 0) {\n\t\t\tSIBR_WRG << \"GL driver does not support program binary export.\" << std::endl;\n\t\t\treturn;\n\t\t}\n\t\tint length = 0;\n\t\tglGetProgramiv(m_Shader, GL_PROGRAM_BINARY_LENGTH, &length);\n\t\tif (length <= 0) {\n\t\t\tSIBR_WRG << \"No binary for program \" << m_Name << \".\" << std::endl;\n\t\t\treturn;\n\t\t}\n\t\tGLenum format;\n\t\tbinary.clear();\n\t\tbinary.resize(length);\n\t\tglGetProgramBinary(m_Shader, length, NULL, &format, &binary[0]);\n\t\n\t}\n\n\tvoid GLShader::terminate( void )\n\t{\n\t\tif (m_Shader) {\n\t\t\tglUseProgram(0);\n\t\t\tglDeleteProgram(m_Shader);\n\t\t\tm_Shader = 0;\n\t\t\tCHECK_GL_ERROR;\n\t\t}\n\t}\n\n\tGLParameter::GLParameter(void) :\n\t\tm_Shader(NULL),\n\t\tm_Handle(-1),\n\t\tm_Name(\"\"),\n\t\tm_Strict(false)\n\t{}\n\n\tbool\tGLParameter::isInitialized( void ) const\n\t{\n\t\treturn (m_Handle != -1 && m_Shader != NULL);\n\t}\n\n\tGLint\tGLParameter::handle( void ) const\n\t{\n\t\treturn m_Handle;\n\t}\n\n\tvoid\tGLParameter::init( sibr::GLShader& shader, std::string name )\n\t{\n\t\tm_Shader = &shader;\n\t\tm_Name   = name;\n\t\tm_Handle = glGetUniformLocation(m_Shader->shader(),name.c_str());\n\t\tm_Strict = m_Shader->isStrict();\n\t\tif (m_Handle == -1) {\n\t\t\tstd::string message = \"GLParameter \" + m_Name + \" does not exist in shader \" + shader.name();\n\t\t\tif (m_Strict) {\n\t\t\t\tthrow std::runtime_error(message);\n\t\t\t} else {\n\t\t\t\tstd::cerr << \"Warning: \" << message.c_str() << std::endl;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic\tstd::string\t\tstrRemoveSpaces( const std::string& str )\n\t{\n\t\tstd::string out;\n\t\tfor (char c : str)\n\t\t\tif (c != ' ' && c != '\\t')\n\t\t\t\tout.push_back(c);\n\t\treturn out;\n\t}\n\n\tstd::string\tloadFile( const std::string& filename, const GLShader::Define::List& defines )\n\t{\n\t\tstd::string file = loadFile(filename);\n\n\t\tif (file.empty())\n\t\t\treturn file;\n\n\t\tstd::vector<std::string>\tlines = sibr::split(file, '\\n');\n\t\tfor ( const GLShader::Define& define : defines )\n\t\t{\n\t\t\tstd::string tag = \"#define\"+define.nameToSearch;\n\t\t\tfor (std::string& line : lines)\n\t\t\t{\n\t\t\t\tstd::string formatted = strRemoveSpaces(line);\n\t\t\t\tif (formatted.find(tag) == 0)\n\t\t\t\t{\n\t\t\t\t\tstd::size_t pos = line.find(define.nameToSearch) + define.nameToSearch.size();\n\t\t\t\t\tline.insert(pos, std::string(\" (\") + define.valueToSet + \") //\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstd::string out;\n\t\tfor ( std::string& line : lines )\n\t\t\tout = out + line + '\\n';\n\t\treturn out;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Shader.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <vector>\n# include <string>\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Matrix.hpp\"\n\n#define SIBR_SHADER(version, shader)  std::string(\"#version \" #version \"\\n\" #shader)\n\nnamespace sibr\n{\n\t/**\n\t  OpenGL shader wrapper.\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT GLShader\n\t{\n\t\tSIBR_DISALLOW_COPY( GLShader );\n\t\tSIBR_CLASS_PTR(GLShader);\n\tpublic:\n\n\t\t/** Macro-like substitution in shaders. */\n\t\tstruct Define\n\t\t{\n\t\t\ttypedef std::vector<Define>\tList;\n\n\t\t\t/** Constructor.\n\t\t\t\\param nameToSearch_ the macro value to replace\n\t\t\t\\param valueToSet_ the replacement value (stringified)\n\t\t\t*/\n\t\t\ttemplate <typename TValue>\n\t\t\tDefine( const std::string& nameToSearch_, const TValue& valueToSet_ ) :\n\t\t\t\tnameToSearch(nameToSearch_) {\n\t\t\t\t\tstd::ostringstream oss;\n\t\t\t\t\toss << valueToSet_;\n\t\t\t\t\tvalueToSet = oss.str();\n\t\t\t}\n\n\t\t\tstd::string nameToSearch; ///< the macro value to replace\n\t\t\tstd::string valueToSet; ///< String representation of the replacement value.\n\t\t};\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tGLShader( void );\n\n\t\t/// Destructor.\n\t\t~GLShader( void );\n\n\t\t/** Create and compile a GPU program composed of a vertex/fragment shader (and optionally geometry/tesselation shaders).\n\t\t\\param name the name of the shader (for logging)\n\t\t\\param vp_code vertex shader code string\n\t\t\\param fp_code fragment shader code string\n\t\t\\param gp_code geometry shader code string\n\t\t\\param exitOnError should the application exit on a shader compilation error\n\t\t\\param tcs_code tesselation control shader code string\n\t\t\\param tes_code tesselation evaluation shader code string\n\t\t\\return a success flag\n\t\t*/\n\t\tbool init(std::string name,\n\t\t\tstd::string vp_code, std::string fp_code,\n\t\t\tstd::string gp_code = std::string(),\n\t\t\tbool exitOnError = true,\n\t\t\tstd::string tcs_code = std::string(),\n\t\t\tstd::string tes_code = std::string());\n\n\t\t/** Recompile a GPU program with updated shaders.\n\t\t\\param vp_code vertex shader code string\n\t\t\\param fp_code fragment shader code string\n\t\t\\param gp_code geometry shader code string\n\t\t\\return a success flag\n\t\t*/\n\t\tbool reload(\n\t\t\tstd::string vp_code, std::string fp_code,\n\t\t\tstd::string gp_code = std::string());\n\n\t\t/**\n\t\tQuery the dissassembly of the shader program.\n\t\t\\param binary will contain the compiled shader code\n\t\t\\note This is not supported on all GPUs.\n\t\t*/\n\t\tvoid getBinary(std::vector<char> & binary);\n\n\t\t/** Bind (activate) the sahder for rendering. */\n\t\tSIBR_OPT_INLINE\t\tvoid\tbegin( void );\n\n\t\t/** Unbind the shader. */\n\t\tSIBR_OPT_INLINE\t\tvoid\tend( void );\n\n\t\t/** Init from an existing GPU program.\n\t\t\\param s_handle the existing program handle\n\t\t*/\n\t\tvoid\t\t\tinit      ( GLuint s_handle );\n\n\t\t/** Cleanup and delete the program. */\n\t\tvoid\t\t\tterminate( void );\n\n\t\t/** If set to true, uniforms that are linked but not referenced \n\t\tby the shader will cause an error to be raised.\n\t\t\\param s the validation level\n\t\t*/\n\t\tvoid\t\t\tsetStrict ( bool s );\n\n\t\t/** \\return the program handle. */\n\t\tGLuint\t\t\tshader    ( void )  const;\n\n\t\t/** \\return the shader name. */\n\t\tstd::string\t\tname      ( void )  const;\n\n\t\t/** \\return true if the shader is properly setup. */\n\t\tbool\t\t\tisReady   ( void )  const;\n\n\t\t/** \\return true if the shader is currently bound for drawing. */\n\t\tbool\t\t\tisActive  ( void )  const;\n\n\t\t/** \\return true if the shader will validate linked uniforms. */\n\t\tbool\t\t\tisStrict  ( void )  const;\n\n\tprivate:\n\n\t\t/** Compile a shader for a given stage.\n\t\t\\param shader_code the string containing the sahder code\n\t\t\\param type the stage to compile for\n\t\t\\return the compiled shader stage handle.\n\t\t*/\n\t\tGLuint\tcompileShader( const char* shader_code, GLuint type );\n\n\t\t/** Check if the shader is properly setup, or raise an error. */\n\t\tSIBR_OPT_INLINE\t\tvoid\tauthorize( void ) const;\n\n\t\tGLuint      m_Shader; ///< Shader program handle.\n\t\tstd::string m_Name; ///< Shader name.\n\t\tbool        m_Strict; ///< Should uniforms be validated.\n\t\tbool        m_Active; ///< Is the shader currently bound.\n\t};\n\n\t// ------------------------------------------------------------------------\n\n\n\t/**\n\t  OpenGL shader uniform wrapper. Prefer using GLuniform instead.\n\t  \\sa GLuniform\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT GLParameter\n\t{\n\tpublic:\n\n\t\t/// Constructor.\n\t\tGLParameter( void );\n\n\t\t/** Link the uniform to a shader.\n\t\t\\param shader the shader to link to\n\t\t\\param name the name of the uniform in the shader\n\t\t*/\n\t\tvoid\tinit( sibr::GLShader& shader, std::string name );\n\n\t\t/** \\return true if the uniform was linked to a shader. */\n\t\tbool\tisInitialized( void ) const;\n\n\t\t/** \\return the OpenGL uniform location handle. */\n\t\tGLint\thandle( void ) const;\n\n\t\t/** Set the uniform float value.\n\t\t\\param f the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( float f );\n\n\t\t/** Set the uniform vec2 value.\n\t\t\\param a first component\n\t\t\\param b second component\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( float a, float b );\n\t\t\n\t\t/** Set the uniform vec3 value.\n\t\t\\param a first component\n\t\t\\param b second component\n\t\t\\param c third component\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( float a, float b, float c );\n\t\t\n\t\t/** Set the uniform vec4 value.\n\t\t\\param a first component\n\t\t\\param b second component\n\t\t\\param c third component\n\t\t\\param d fourth component\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( float a, float b, float c, float d );\n\t\t\n\t\t/** Set the uniform mat4 value.\n\t\t\\param matrix the 16 matrix components, in row order\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( const float *matrix );\n\n\t\t/** Set the uniform sampler value.\n\t\t\\param tex the new value (ie the binding location of the texture)\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( GLuint tex );\n\n\t\t/** Set the uniform integer value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( int v );\n\n\t\t/** Set the uniform boolean value (converted to an int).\n\t\t\\param b the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( bool b);\n\n\t\t/** Set the uniform values defined as an array of floats.\n\t\t\\param pv pointer to the float array\n\t\t\\param size number of elements\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tsetArray( const float *pv, int size );\n\n\t\t/** Set the uniform values defined as an array of integers.\n\t\t\\param pv pointer to the int array\n\t\t\\param size number of elements\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tsetArray( const std::vector<int>& pv, int size );\n\n\t\t/** Set the uniform ivec2 value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset(const Vector2i& v);\n\n\t\t/** Set the uniform ivec3 value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset(const Vector3i& v);\n\n\t\t/** Set the uniform ivec4 value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset(const Vector4i& v);\n\t\t\n\t\t/** Set the uniform vec2 value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( const Vector2f& v );\n\n\t\t/** Set the uniform vec3 value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( const Vector3f& v );\n\n\t\t/** Set the uniform vec4 value.\n\t\t\\param v the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( const Vector4f& v );\n\n\t\t/** Set the uniform mat4 value.\n\t\t\\param m the new value\n\t\t*/\n\t\tSIBR_OPT_INLINE void\tset( const Matrix4f& m );\n\n\t\t/** Set the uniform values defined as an array of mat4s  (row major).\n\t\t\\param m pinter to the beginning of the matrix array\n\t\t\\param num number of matrices\n\t\t*/\n\t\tSIBR_OPT_INLINE void setMatrixArray(const float * m, int num);\n\n\tprivate:\n\n\t\tsibr::GLShader*\t\tm_Shader; ///< Linked shader.\n\t\tGLint\t\t\t\tm_Handle; ///< Uniform location.\n\t\tstd::string\t\t\tm_Name; ///< Uniform name.\n\t\tbool\t\t\t\tm_Strict; ///< Should the program raise an error if the uniform is not found in the linked shader.\n\t\t\n\t\t/** Check if the uniform/shader link is valid. */\n\t\tSIBR_OPT_INLINE void\tauthorize( void ) const;\n\t};\n\n\t/** Load a file from disk and apply macro-like substitutions.\n\t\\param filename the file path\n\t\\param defines a list of substitutions to apply\n\t\\return the loaded string\n\t\\ingroup sibr_graphics\n\t*/\n\tSIBR_GRAPHICS_EXPORT std::string\tloadFile( const std::string& filename, const GLShader::Define::List& defines );\n\n\t/**\n\t  OpenGL shader uniform wrapper with additional update/storage functions.\n\t  It will behave as an element of the type it wraps, but can also be set/sent to the GPU.\n\t  This limits duplication, as you can replace a float+GLParameter by a GLuniform<float>\n\t  When you need a reference to the value (for instance in imGui), use uniform.get().\n\t  \\sa GLParameter\n\t* \\ingroup sibr_graphics\n\t*/\n\ttemplate<typename T> class GLuniform {\n\n\tpublic:\n\t\t/** Constructor.\n\t\t\\param t initial value to use\n\t\t*/\n\t\tGLuniform(const T & t) : value(t) {}\n\n\t\t/** \\return a reference to the value. */\n\t\toperator T & () { return value; }\n\n\t\t/** \\return a reference to the value. */\n\t\tT & get() { return value; }\n\n\t\t/** Copy operator. Update the stored value.\n\t\t\\param t the new value\n\t\t\\return a reference to the value.\n\t\t*/\n\t\tT & operator=(const T & t) { value = t; return *this; }\n\t\t\n\t\t/** Copy operator. Update the stored value using the one of the other uniform.\n\t\t\\param other uniform to get the new value from\n\t\t\\return a reference to itself\n\t\t*/\n\t\tGLuniform & operator=(const GLuniform& other) { value = other.value; return *this; };\n\n\t\t/** not-equal-to operator. Compares the stored values with the argument and returns\n\t\tthe not-equal-to operator.\n\t\t\\param t value to compare to.\n\t\t\\return the boolean result of the operation.\n\t\t*/\n\t\tbool operator!=(const T& t) { return value != t; }\n\n\t\t/** Copy constructor. Update the stored value using the one of the other uniform.\n\t\t\\param other uniform to get the new value from\n\t\t*/\n\t\texplicit GLuniform(const GLuniform&other) : value(other.value) { };\n\n\t\t/// Default constructor.\n\t\tGLuniform() = default;\n\n\t\t/** Link the uniform to a shader.\n\t\t\\param shader the shader to link to\n\t\t\\param name the name of the uniform in the shader\n\t\t*/\n\t\tvoid init(sibr::GLShader& shader, std::string name) {\n\t\t\tparameter.init(shader, name);\n\t\t}\n\n\t\t/** Send the value to the shader if it was initialized.\n\t\t\\note the shader has to be active\n\t\t*/\n\t\tvoid send() {\n\t\t\tif (parameter.isInitialized()) {\n\t\t\t\tparameter.set(value);\n\t\t\t}\n\t\t}\n\n\t\t/** Set the value and send it to the shader if it was initialized.\n\t\t\\param t the new value\n\t\t\\note the shader has to be active\n\t\t*/\n\t\tvoid set(const T & t) {\n\t\t\tvalue = t;\n\t\t\tsend();\n\t\t}\n\t\t\n\t\t/** Set a list of values and send it to the shader if it was initialized.\n\t\t\\param t the new values\n\t\t\\param size the number of values\n\t\t\\note the shader has to be active\n\t\t*/\n\t\tvoid setArray(const T & t,int size) {\n\t\t\tvalue = t;\n\t\t\tif (parameter.isInitialized()) {\n\t\t\t\tparameter.setArray(value,size);\n\t\t\t}\n\t\t}\n\n\tprotected :\n\t\tT value = T(); ///< The underlying value.\n\t\tsibr::GLParameter parameter; ///< The underlying uniform.\n\t};\n\n} // namespace sibr\n\n# ifdef SIBR_MAXIMIZE_INLINE\n#  include \"Shader.inl\"\n# endif\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Shader.inl",
    "content": "\nnamespace sibr\n{\n\tvoid GLShader::authorize(void) const\n\t{\n\t\tif (!m_Shader) {\n\t\t\tstd::string message = \"sibr::GLShader \" + m_Name + \" used without having been initialized\";\n\t\t\tthrow std::runtime_error(message);\n\t\t}\n\t}\n\n\tvoid GLShader::begin( void )\n\t{\n\t\tCHECK_GL_ERROR;\n\t\tauthorize();\n\t\tglUseProgram(m_Shader);\n\t\tm_Active = true;\n\t\tCHECK_GL_ERROR;\n\t}\n\n\tvoid GLShader::end( void )\n\t{\n\t\tglUseProgram(0);\n\t\tm_Active = false;\n\t\tCHECK_GL_ERROR;\n\t}\n\n\tvoid GLParameter::authorize(void) const\n\t{\n\t\tif (m_Shader == NULL) {\n\t\t\tstd::string message = \"GLParameter \" + m_Name + \" does not have a valid shader program\";\n\t\t\tthrow std::runtime_error(message);\n\t\t}\n\t\tif (m_Strict && m_Handle == -1) {\n\t\t\tstd::string message = \"GLParameter \" + m_Name + \" used without having been initialized\";\n\t\t\tthrow std::runtime_error(message);\n\t\t}\n\t\tif (!m_Shader->isActive()) {\n\t\t\tstd::string message = \"GLParameter \" + m_Name + \" used with shader is not active\";\n\t\t\tthrow std::runtime_error(message);\n\t\t}\n\t}\n\n\tvoid\tGLParameter::set( float f )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform1f(m_Handle,f);\n\t}\n\n\tvoid\tGLParameter::set( float a,float b )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform2f(m_Handle,a,b);\n\t}\n\n\tvoid\tGLParameter::set( float a, float b, float c )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform3f(m_Handle,a,b,c);\n\t}\n\n\tvoid\tGLParameter::set( float a, float b, float c, float d )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform4f(m_Handle,a,b,c,d);\n\t}\n\n\tvoid\tGLParameter::set( const float *matrix )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniformMatrix4fv(m_Handle,1,GL_TRUE,matrix); // row major\n\t}\n\n\tvoid\tGLParameter::set( GLuint tex )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform1i(m_Handle,tex);\n\t}\n\n\tvoid\tGLParameter::set( int v )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform1i(m_Handle,v);\n\t}\n\n\tvoid\tGLParameter::set(bool b)\n\t{\n\t\tset((int)b);\n\t}\n\n\tvoid\tGLParameter::setArray( const float *pv, int size )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform1fv(m_Handle,size,pv);\n\t}\n\t\n\tvoid\tGLParameter::setArray( const std::vector<int>& pv, int size )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform1iv(m_Handle,size,&pv[0]);\n\t}\n\n\tvoid\tGLParameter::set(const Vector2i& v)\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform2i(m_Handle, v[0], v[1]);\n\t}\n\n\tvoid\tGLParameter::set(const Vector3i& v)\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform3i(m_Handle, v[0], v[1], v[2]);\n\t}\n\n\tvoid\tGLParameter::set(const Vector4i& v)\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform4i(m_Handle, v[0], v[1], v[2], v[3]);\n\t}\n\n\tvoid\tGLParameter::set( const Vector2f& v )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform2f(m_Handle,v[0],v[1]);\n\t}\n\n\tvoid\tGLParameter::set( const Vector3f& v )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform3f(m_Handle,v[0],v[1],v[2]);\n\t}\n\n\tvoid\tGLParameter::set( const Vector4f& v )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniform4f(m_Handle,v[0],v[1],v[2],v[3]);\n\t}\n\n\tvoid\tGLParameter::set( const Matrix4f& m )\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniformMatrix4fv(m_Handle,1,GL_FALSE,m.data()); // row major\n\t}\n\n\tvoid\tGLParameter::setMatrixArray(const float* m, int num)\n\t{\n\t\tauthorize();\n\t\tif (!m_Strict && m_Handle == -1) return;\n\t\tglUniformMatrix4fv(m_Handle, num, GL_FALSE, m); // row major\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Texture.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/Texture.hpp\"\n//#define HEADLESS\n\nnamespace sibr\n{\n\tvoid\t\t\tblit(const ITexture2D& src, const IRenderTarget& dst, GLbitfield mask, GLenum filter, bool flip)\n\t{\n\t\tGLuint sourceFrameBuffer = 0;\n\t\tglGenFramebuffers(1, &sourceFrameBuffer);\n\t\tglBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFrameBuffer);\n\t\tglFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src.handle(), 0);\n\n\t\tSIBR_ASSERT(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);\n\n#ifdef HEADLESS\n\n\t\tSIBR_ERR << \"No named frame buffers in headless \" << std::endl;\n#else\n\t\tglBlitNamedFramebuffer(\n\t\t\tsourceFrameBuffer, dst.fbo(),\n\t\t\t0, 0, src.w(), src.h(),\n\t\t\t0, (flip ? dst.h() : 0), dst.w(), (flip ? 0 : dst.h()),\n\t\t\tmask, filter);\n\n\t\tglDeleteFramebuffers(1, &sourceFrameBuffer);\n#endif\n\t}\n\n\tvoid\t\t\tblit_and_flip(const ITexture2D& src, const IRenderTarget& dst, GLbitfield mask, GLenum filter)\n\t{\n\t\tblit(src, dst, mask, filter, true);\n\t}\n\n\tvoid\t\t\tblitToColorAttachment(const ITexture2D& src, IRenderTarget& dst, int location, GLenum filter, bool flip)\n\t{\n\t\t// To blit only to a specific color attachment, it should be the only draw buffer registered.\n\t\t// So we override the drawbuffer from dst temporarily.\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, dst.fbo());\n\t\tglDrawBuffer(GL_COLOR_ATTACHMENT0 + location);\n\t\t\n\t\tGLuint sourceFrameBuffer = 0;\n\t\tglGenFramebuffers(1, &sourceFrameBuffer);\n\t\tglBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFrameBuffer);\n\t\tglFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src.handle(), 0);\n\n\t\tSIBR_ASSERT(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);\n\n#ifdef HEADLESS\n\t\tSIBR_ERR << \"No named frame buffers in headless \" << std::endl;\n#else\n\t\tglBlitNamedFramebuffer(\n\t\t\tsourceFrameBuffer, dst.fbo(),\n\t\t\t0, 0, src.w(), src.h(),\n\t\t\t0, (flip ? dst.h() : 0), dst.w(), (flip ? 0 : dst.h()),\n\t\t\tGL_COLOR_BUFFER_BIT, filter);\n\n\t\tglDeleteFramebuffers(1, &sourceFrameBuffer);\n#endif\n\n\t\t// Restore the drawbuffers.\n\t\t// We use bind() as it guarantees that all color buffers will be bound.\n\t\tdst.bind();\n\t\tdst.unbind();\n\t}\n\n\tvoid\t\t\tblit(const IRenderTarget& src, const ITexture2D& dst, GLbitfield mask, GLenum filter)\n\t{\n\t\tGLuint dstFrameBuffer = 0;\n\t\tglGenFramebuffers(1, &dstFrameBuffer);\n\t\tglBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFrameBuffer);\n\t\tglFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst.handle(), 0);\n\n\t\tSIBR_ASSERT(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);\n\n#ifdef HEADLESS\n\t\tSIBR_ERR << \"No named frame buffers in headless \" << std::endl;\n#else\n\t\tglBlitNamedFramebuffer(\n\t\t\tsrc.fbo(), dstFrameBuffer,\n\t\t\t0, 0, src.w(), src.h(),\n\t\t\t0, 0, dst.w(), dst.h(),\n\t\t\tmask, filter);\n\t\tglDeleteFramebuffers(1, &dstFrameBuffer);\n#endif\n\t}\n\n\tvoid\t\t\tblit(const ITexture2D& src, const ITexture2D& dst, GLbitfield mask, GLenum filter)\n\t{\n\t\tGLuint fbo[2];\n\t\tglGenFramebuffers(2, fbo);\n\t\tglBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);\n\t\tglFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src.handle(), 0);\n\t\tglBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[0]);\n\t\tglFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst.handle(), 0);\n\n\t\tSIBR_ASSERT(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);\n\t\tSIBR_ASSERT(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);\n\n#ifdef HEADLESS\n\t\tSIBR_ERR << \"No named frame buffers in headless \" << std::endl;\n#else\n\t\tglBlitNamedFramebuffer(\n\t\t\tfbo[0], fbo[1],\n\t\t\t0, 0, src.w(), src.h(),\n\t\t\t0, 0, dst.w(), dst.h(),\n\t\t\tmask, filter);\n\t\tglDeleteFramebuffers(2, fbo);\n#endif\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Texture.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include <type_traits>\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n# include \"core/graphics/Image.hpp\"\r\n# include \"core/graphics/Types.hpp\"\r\n# include \"core/graphics/RenderTarget.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/** Interface for a generic GPU 2D texture.\r\n\t* \\sa Texture2D\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tclass ITexture2D\r\n\t{\r\n\tpublic:\r\n\t\ttypedef std::shared_ptr<ITexture2D>\tPtr;\r\n\t\ttypedef std::unique_ptr<ITexture2D>\tUPtr;\r\n\tpublic:\r\n\r\n\t\t/// Destructor.\r\n\t\tvirtual ~ITexture2D(void) { }\r\n\r\n\t\t/** \\return the texture handle. */\r\n\t\tvirtual GLuint handle(void) const = 0;\r\n\r\n\t\t/** \\return the texture width. */\r\n\t\tvirtual uint   w(void) const = 0;\r\n\r\n\t\t/** \\return the texture height. */\r\n\t\tvirtual uint   h(void) const = 0;\r\n\t};\r\n\r\n\t/** Represent a 2D texture on the GPU, with custom format and type.\r\n\t* \\sa ITexture2D\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tclass Texture2D : public ITexture2D {\r\n\t\tSIBR_DISALLOW_COPY(Texture2D);\r\n\tpublic:\r\n\t\ttypedef\t\tImage<T_Type, T_NumComp>\t\t\tPixelImage;\r\n\t\ttypedef\t\ttypename PixelImage::Pixel\t\t\tPixelFormat;\r\n\t\ttypedef\t\tstd::shared_ptr<Texture2D<T_Type, T_NumComp>>\tPtr;\r\n\t\ttypedef\t\tstd::unique_ptr<Texture2D<T_Type, T_NumComp>>\tUPtr;\r\n\r\n\tpublic:\r\n\r\n\t\t/// Constructor.\r\n\t\tTexture2D(void);\r\n\r\n\t\t/** Constructor from an image.\r\n\t\t\\param img the image to upload to the GPU\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType> Texture2D(const ImageType& img, uint flags = 0);\r\n\r\n\t\t/** Constructor from a list of images, one for each mip level.\r\n\t\t\\param miparray the images to upload to the GPU\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tTexture2D(const std::vector<PixelImage>& miparray, uint flags = 0);\r\n\r\n\t\t/// Destructor.\r\n\t\t~Texture2D(void);\r\n\r\n\t\t/** \\return the texture handle. */\r\n\t\tGLuint handle(void) const;\r\n\r\n\t\t/** \\return the texture width. */\r\n\t\tuint   w(void) const;\r\n\r\n\t\t/** \\return the texture height. */\r\n\t\tuint   h(void) const;\r\n\r\n\t\t/** \\return a CPU image containing the texture content.\r\n\t\t\t\\warning Can cause a GPU flush/sync.\r\n\t\t*/\r\n\t\tsibr::Image<T_Type, T_NumComp>\t\treadBack(void) const;\r\n\r\n\t\t/** Update the content of the txeture with a new image.\r\n\t\t\\param img the new content.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType> void update(const ImageType& img);\r\n\r\n\t\t/** Trigger an update of the mipmaps for level 0 to maxLOD.\r\n\t\t\\param maxLOD the maximum level of mipmap to generate. If -1, as many as possible based on the texture size.\r\n\t\t*/\r\n\t\tvoid mipmap(int maxLOD = -1);\r\n\r\n\tprivate:\r\n\t\tGLuint  m_Handle = 0; ///< Texture handle.\r\n\t\tuint    m_W = 0; ///< Texture width.\r\n\t\tuint    m_H = 0; ///< Texture height.\r\n\t\tuint    m_Flags = 0; ///< Options.\r\n\t\tbool\tm_autoMIPMAP = false; ///< Should the mipmaps be generated automatically.\r\n\r\n\t\t/** Create 2D texture from a generic image (sibr::image or cv::Mat).\r\n\t\t\\param array the image\r\n\t\t\\param flags options\r\n\t\t\\return the handle of the texture\r\n\t\t*/\r\n\t\ttemplate<typename ImageType> static GLuint create2D(const ImageType& array, uint flags);\r\n\r\n\t\t/** Create 2D texture with custom mipmaps from a list of generic images (sibr::image or cv::Mat).\r\n\t\t\\param miparray the images\r\n\t\t\\param flags options\r\n\t\t\\return the handle of the texture\r\n\t\t*/\r\n\t\tstatic GLuint create2D(const std::vector<PixelImage>& miparray, uint flags);\r\n\r\n\t\t/** Send the CPU image data to the GPU.\r\n\t\t\\param id the created texture\r\n\t\t\\param array the image data\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType> static void send2D(GLuint id, const ImageType& array, uint flags);\r\n\r\n\t\t/** Send the CPU images data for each mipmap to the GPU.\r\n\t\t\\param id the created texture\r\n\t\t\\param miparray the image data\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tstatic void send2Dmipmap(GLuint id, const std::vector<PixelImage>& miparray, uint flags);\r\n\r\n\t};\r\n\r\n\r\n\t/** Interface for a generic GPU 2D array texture.\r\n\t* \\sa Texture2DArray\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tclass ITexture2DArray\r\n\t{\r\n\tpublic:\r\n\t\ttypedef std::shared_ptr<ITexture2DArray>\tPtr;\r\n\t\ttypedef std::unique_ptr<ITexture2DArray>\tUPtr;\r\n\tpublic:\r\n\t\t/// Destructor.\r\n\t\tvirtual ~ITexture2DArray(void) { }\r\n\r\n\t\t/** \\return the texture handle. */\r\n\t\tvirtual GLuint\thandle(void) const = 0;\r\n\r\n\t\t/** \\return the texture width. */\r\n\t\tvirtual uint\tw(void) const = 0;\r\n\r\n\t\t/** \\return the texture height. */\r\n\t\tvirtual uint\th(void) const = 0;\r\n\r\n\t\t/** \\return the texture layer count. */\r\n\t\tvirtual uint\tdepth(void) const = 0;\r\n\r\n\t\t/** \\return the number of mipmap levels. */\r\n\t\tvirtual uint\tnumLODs(void) const = 0;\r\n\r\n\t\t/** Read back the value of a given pixel to the CPU.\r\n\t\t\\param i layer\r\n\t\t\\param x x coordinate in [0,w-1]\r\n\t\t\\param y y coordinate in [0,h-1]\r\n\t\t\\param lod the mip level\r\n\t\t\\return a converted RGBA float color\r\n\t\t\\warning Use only for debugging, can cause a GPU flush/sync.\r\n\t\t*/\r\n\t\tvirtual Vector4f\treadBackPixel(int i, int x, int y, uint lod = 0) const = 0;\r\n\t};\r\n\r\n\t/**\r\n\t* Represent an array of 2D textures on the GPU, with custom format, type and slice count.\r\n\t* \\sa ITexture2DArray\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tclass Texture2DArray : public ITexture2DArray {\r\n\t\tSIBR_DISALLOW_COPY(Texture2DArray);\r\n\tpublic:\r\n\t\ttypedef\t\tImage<T_Type, T_NumComp>\t\t\tPixelImage;\r\n\t\ttypedef\t\ttypename PixelImage::Pixel\t\t\tPixelFormat;\r\n\t\ttypedef\t\tRenderTarget<T_Type, T_NumComp>\t\t\tPixelRT;\r\n\t\ttypedef\t\tstd::shared_ptr<Texture2DArray<T_Type, T_NumComp>>\tPtr;\r\n\t\ttypedef\t\tstd::unique_ptr<Texture2DArray<T_Type, T_NumComp>>\tUPtr;\r\n\r\n\tpublic:\r\n\r\n\t\t/** Constructor.\r\n\t\t\\param d number of layers\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tTexture2DArray(const uint d = 0, uint flags = 0);\r\n\r\n\t\t/** Constructor.\r\n\t\t\\param w width\r\n\t\t\\param h height\r\n\t\t\\param d number of layers\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tTexture2DArray(const uint w, const uint h, const uint d, uint flags = 0);\r\n\r\n\t\t/** Constructor from a set of rendertargets.\r\n\t\t\\param images list of rendertargets, one for each layer\r\n\t\t\\param flags options\r\n\t\t\\warning RTs should be of the same size.\r\n\t\t*/\r\n\t\tTexture2DArray(const std::vector<typename PixelRT::Ptr>& images, uint flags = 0);\r\n\r\n\t\t/** Constructor from a set of CPU images.\r\n\t\t\\param images list of images, one for each layer\r\n\t\t\\param flags options\r\n\t\t\\note All images will be resized to the dimensions of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tTexture2DArray(const std::vector<ImageType>& images, uint flags = 0);\r\n\r\n\t\t/** Constructor from a set of CPU images that will be resized to a fix size.\r\n\t\t\\param images list of images, one for each layer\r\n\t\t\\param w the target width\r\n\t\t\\param h the target height\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tTexture2DArray(const std::vector<ImageType>& images, uint w, uint h, uint flags = 0);\r\n\r\n\t\t/** Constructor from a set of CPU images, with custom mipmaps.\r\n\t\t\\param images list of lists of images, one for each mip level, each containing an image for each layer\r\n\t\t\\param flags options\r\n\t\t\\note All images will be resized to the dimensions of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tTexture2DArray(const std::vector<std::vector<ImageType>>& images, uint flags = 0);\r\n\r\n\t\t/** Constructor from a set of CPU images, with custom mipmaps.\r\n\t\t\\param images list of lists of images, one for each mip level, each containing an image for each layer\r\n\t\t\\param w the target width\r\n\t\t\\param h the target height\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tTexture2DArray(const std::vector<std::vector<ImageType>>& images, uint w, uint h, uint flags = 0);\r\n\r\n\t\t/** Create the texture from a set of images and send it to GPU.\r\n\t\t\\param images list of images, one for each layer\r\n\t\t\\param flags options\r\n\t\t\\note All images will be resized to the dimensions of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid createFromImages(const std::vector<ImageType>& images, uint flags = 0);\r\n\r\n\t\t/** Create the texture from a set of images and send it to GPU. images will be resized to the target size.\r\n\t\t\\param images list of images, one for each layer\r\n\t\t\\param w the target width\r\n\t\t\\param h the target height\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid createFromImages(const std::vector<ImageType>& images, uint w, uint h, uint flags = 0);\r\n\r\n\t\t/** Create the texture from a set of images and send it to GPU while compressing them.\r\n\t\t\\param images list of images, one for each layer\r\n\t\t\\param compression the GL_COMPRESSED format. It must be choosen accordingly to the texture internal format.\r\n\t\t\\param flags options\r\n\t\t\\note All images will be resized to the dimensions of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid createCompressedFromImages(const std::vector<ImageType>& images, uint compression, uint flags = 0);\r\n\r\n\t\t/** Create the texture from a set of images and send it to GPU while compressing them. images will be resized to the target size.\r\n\t\t\\param images list of images, one for each layer\r\n\t\t\\param w the target width\r\n\t\t\\param h the target height\r\n\t\t\\param compression the GL_COMPRESSED format. It must be choosen accordingly to the texture internal format.\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid createCompressedFromImages(const std::vector<ImageType>& images, uint w, uint h, uint compression, uint flags = 0);\r\n\r\n\t\t/** Create the texture from a set of images with custom mipmaps and send it to GPU.\r\n\t\t\\param images list of lists of images, one for each mip level, each containing an image for each layer\r\n\t\t\\param flags options\r\n\t\t\\note All images will be resized to the dimensions of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid createFromImages(const std::vector<std::vector<ImageType>>& images, uint flags = 0);\r\n\r\n\t\t/** Create the texture from a set of images with custom mipmaps and send it to GPU.\r\n\t\t\\param images list of lists of images, one for each mip level, each containing an image for each layer\r\n\t\t\\param w the target width\r\n\t\t\\param h the target height\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid createFromImages(const std::vector<std::vector<ImageType>>& images, uint w, uint h, uint flags = 0);\r\n\r\n\t\t/** Update the content of all layers of the texture.\r\n\t\t\\param images the new content to use\r\n\t\t\\note All images will be resized to the size of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid updateFromImages(const std::vector<ImageType>& images);\r\n\r\n\t\t/** Create the texture from a set of rendertargets and send it to GPU.\r\n\t\t\\param RTs list of rendertargets, one for each layer\r\n\t\t\\param flags options\r\n\t\t\\warning RTs should be of the same size.\r\n\t\t*/\r\n\t\tvoid createFromRTs(const std::vector<typename PixelRT::Ptr>& RTs, uint flags = 0);\r\n\r\n\t\t/** Update the content of specific layers of the texture.\r\n\t\t\\param images the new content to use\r\n\t\t\\param slices the indices of the slices to update\r\n\t\t\\note All images will be resized to the size of the largest one.\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid updateSlices(const std::vector<ImageType>& images, const std::vector<int>& slices);\r\n\r\n\t\t/// Destructor.\r\n\t\t~Texture2DArray(void);\r\n\r\n\t\t/** \\return the texture handle. */\r\n\t\tGLuint\thandle(void) const;\r\n\r\n\t\t/** \\return the texture width. */\r\n\t\tuint\tw(void) const;\r\n\r\n\t\t/** \\return the texture height. */\r\n\t\tuint\th(void) const;\r\n\r\n\t\t/** \\return the texture layer count. */\r\n\t\tuint\tdepth(void) const;\r\n\r\n\t\t/** \\return the number of mipmap levels. */\r\n\t\tuint\tnumLODs(void) const;\r\n\r\n\t\t/** Read back the value of a given pixel to the CPU.\r\n\t\t\\param i layer\r\n\t\t\\param x x coordinate in [0,w-1]\r\n\t\t\\param y y coordinate in [0,h-1]\r\n\t\t\\param lod the mip level\r\n\t\t\\return a converted RGBA float color\r\n\t\t\\warning Use only for debugging, can cause a GPU flush/sync.\r\n\t\t*/\r\n\t\tVector4f\treadBackPixel(int i, int x, int y, uint lod = 0) const;\r\n\r\n\tprivate:\r\n\r\n\t\t/** Create the texture array. */\r\n\t\tvoid createArray(uint compression = 0);\r\n\r\n\t\t/** Upload the images data to the GPU.\r\n\t\t\\param images the data to upload\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid sendArray(const std::vector<ImageType>& images);\r\n\r\n\t\t/** Copy the rendertargets data to the texture.\r\n\t\t\\param RTs the rendertargets to copy\r\n\t\t*/\r\n\t\tvoid sendRTarray(const std::vector<typename PixelRT::Ptr>& RTs);\r\n\r\n\t\t/** Upload the images data to the GPU.\r\n\t\t\\param images the data to upload\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tvoid sendMipArray(const std::vector<std::vector<ImageType>>& images);\r\n\r\n\t\t/** Flip and rescale a subset of images from a list.\r\n\t\t\\param images the images to resize\r\n\t\t\\param tmp a temporary buffer\r\n\t\t\\param tw the target width\r\n\t\t\\param th the target height\r\n\t\t\\param slices the indices of the images to process in the list\r\n\t\t\\return a list of pointers to the transformed images\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tstd::vector<const ImageType*> applyFlipAndResize(\r\n\t\t\tconst std::vector<ImageType>& images,\r\n\t\t\tstd::vector<ImageType>& tmp, uint tw, uint th,\r\n\t\t\tconst std::vector<int>& slices\r\n\t\t);\r\n\r\n\t\t/** Flip and rescale a set of images.\r\n\t\t\\param images the images to resize\r\n\t\t\\param tmp a temporary buffer\r\n\t\t\\param tw the target width\r\n\t\t\\param th the target height\r\n\t\t\\return a list of pointers to the transformed images\r\n\t\t*/\r\n\t\ttemplate<typename ImageType>\r\n\t\tstd::vector<const ImageType*> applyFlipAndResize(\r\n\t\t\tconst std::vector<ImageType>& images,\r\n\t\t\tstd::vector<ImageType>& tmp, uint tw, uint th\r\n\t\t);\r\n\r\n\t\tGLuint  m_Handle = 0; ///< Texture handle.\r\n\t\tuint    m_W = 0; ///< Texture width.\r\n\t\tuint    m_H = 0; ///< Texture height.\r\n\t\tuint    m_Flags = 0; ///< Options.\r\n\t\tuint\tm_Depth = 0; ///< Layers count.\r\n\t\tuint\tm_numLODs = 1; ///< Mipmap level count.\r\n\t};\r\n\r\n\r\n\t/** Interface for a generic GPU cubemap texture.\r\n\t* \\sa TextureCubeMap\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tclass ITextureCubeMap\r\n\t{\r\n\tpublic:\r\n\t\ttypedef std::shared_ptr<ITextureCubeMap>\tPtr;\r\n\t\ttypedef std::unique_ptr<ITextureCubeMap>\tUPtr;\r\n\tpublic:\r\n\t\t/// Destructor.\r\n\t\tvirtual ~ITextureCubeMap(void) { }\r\n\r\n\t\t/** \\return the texture handle. */\r\n\t\tvirtual GLuint\thandle(void) const = 0;\r\n\r\n\t\t/** \\return the texture width. */\r\n\t\tvirtual uint\tw(void) const = 0;\r\n\r\n\t\t/** \\return the texture height. */\r\n\t\tvirtual uint\th(void) const = 0;\r\n\t};\r\n\r\n\t/**\r\n\t* Represent a cubemap composed of 6 2D faces on the GPU, with custom format and type.\r\n\t* \\sa ITextureCubeMap\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tclass TextureCubeMap : public ITextureCubeMap {\r\n\t\tSIBR_DISALLOW_COPY(TextureCubeMap);\r\n\r\n\tpublic:\r\n\t\ttypedef\t\tImage<T_Type, T_NumComp>\t\t\tPixelImage;\r\n\t\ttypedef\t\ttypename PixelImage::Pixel\t\t\tPixelFormat;\r\n\t\ttypedef\t\tRenderTarget<T_Type, T_NumComp>\t\t\tPixelRT;\r\n\t\ttypedef\t\tstd::shared_ptr<TextureCubeMap<T_Type, T_NumComp>>\tPtr;\r\n\t\ttypedef\t\tstd::unique_ptr<TextureCubeMap<T_Type, T_NumComp>>\tUPtr;\r\n\r\n\tpublic:\r\n\r\n\t\t/// Constructor.\r\n\t\tTextureCubeMap(void);\r\n\r\n\t\t/** Constructor.\r\n\t\t\\param w width\r\n\t\t\\param h height\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tTextureCubeMap(const uint w, const uint h, uint flags = 0);\r\n\r\n\t\t/** Create a cubemap from 6 images.\r\n\t\t\\param xpos positive X face\r\n\t\t\\param xneg negative X face\r\n\t\t\\param ypos positive Y face\r\n\t\t\\param yneg negative Y face\r\n\t\t\\param zpos positive Z face\r\n\t\t\\param zneg negative Z face\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tTextureCubeMap(const PixelImage& xpos, const PixelImage& xneg,\r\n\t\t\tconst PixelImage& ypos, const PixelImage& yneg,\r\n\t\t\tconst PixelImage& zpos, const PixelImage& zneg, uint flags = 0);\r\n\r\n\t\t/** Create the texture from 6 images.\r\n\t\t\\param xpos positive X face\r\n\t\t\\param xneg negative X face\r\n\t\t\\param ypos positive Y face\r\n\t\t\\param yneg negative Y face\r\n\t\t\\param zpos positive Z face\r\n\t\t\\param zneg negative Z face\r\n\t\t\\param flags options\r\n\t\t*/\r\n\t\tvoid createFromImages(const PixelImage& xpos, const PixelImage& xneg,\r\n\t\t\tconst PixelImage& ypos, const PixelImage& yneg,\r\n\t\t\tconst PixelImage& zpos, const PixelImage& zneg, uint flags = 0);\r\n\r\n\t\t/// Destructor.\r\n\t\t~TextureCubeMap(void);\r\n\r\n\t\t/** \\return the texture handle. */\r\n\t\tGLuint\thandle(void) const;\r\n\r\n\t\t/** \\return the texture width. */\r\n\t\tuint\tw(void) const;\r\n\r\n\t\t/** \\return the texture height. */\r\n\t\tuint\th(void) const;\r\n\r\n\tprivate:\r\n\r\n\t\t/** Create the cubemap texture object. */\r\n\t\tvoid createCubeMap();\r\n\r\n\t\t/** Upload cubemap data.\r\n\t\t\\param xpos positive X face\r\n\t\t\\param xneg negative X face\r\n\t\t\\param ypos positive Y face\r\n\t\t\\param yneg negative Y face\r\n\t\t\\param zpos positive Z face\r\n\t\t\\param zneg negative Z face\r\n\t\t*/\r\n\t\tvoid sendCubeMap(const PixelImage& xpos, const PixelImage& xneg,\r\n\t\t\tconst PixelImage& ypos, const PixelImage& yneg,\r\n\t\t\tconst PixelImage& zpos, const PixelImage& zneg);\r\n\r\n\t\tGLuint  m_Handle = 0; ///< Texture handle.\r\n\t\tuint    m_W = 0; ///< Texture width.\r\n\t\tuint    m_H = 0; ///< Texture height.\r\n\t\tuint    m_Flags = 0; ///< Options.\r\n\r\n\t};\r\n\r\n\r\n\t/**\r\n\tCopy the content of a texture to another texture, resizing if needed.\r\n\t\\param src source texture\r\n\t\\param dst destination texture\r\n\t\\param mask which part of the buffer to copy (color, depth, stencil).\r\n\t\\param filter filtering mode if the two buffers have different dimensions (linear or nearest)\r\n\t\\warning If the mask contains the depth or stencil, filter must be GL_NEAREST\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblit(const ITexture2D& src, const ITexture2D& dst, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_LINEAR);\r\n\r\n\r\n\t/**\r\n\tCopy the content of a texture to a render target, resizing if needed.\r\n\t\\param src source texture\r\n\t\\param dst destination rendertarget\r\n\t\\param mask which part of the buffer to copy (color, depth, stencil).\r\n\t\\param filter filtering mode if the two buffers have different dimensions (linear or nearest)\r\n\t\\param flip flip the texture vertically when copying it\r\n\t\\note The blit can only happen for color attachment 0 in dst.\r\n\t\\warning If the mask contains the depth or stencil, filter must be GL_NEAREST\r\n\t \\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblit(const ITexture2D& src, const IRenderTarget& dst, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_LINEAR, bool flip = false);\r\n\r\n\t/**\r\n\tCopy the content of a texture to a render target, resizing if needed and flipping the result.\r\n\t\\param src source texture\r\n\t\\param dst destination rendertarget\r\n\t\\param mask which part of the buffer to copy (color, depth, stencil).\r\n\t\\param filter filtering mode if the two buffers have different dimensions (linear or nearest)\r\n\t\\note The blit can only happen for color attachment 0 in dst.\r\n\t\\warning If the mask contains the depth or stencil, filter must be GL_NEAREST\r\n\t \\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblit_and_flip(const ITexture2D& src, const IRenderTarget& dst, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_LINEAR);\r\n\r\n\t/**\r\n\tCopy the content of a texture to a specific color attachment of the destination render target, resizing if needed.\r\n\t\\param src source texture\r\n\t\\param dst destination rendertarget\r\n\t\\param location the color attachment to blit to\r\n\t\\param filter filtering mode if the two buffers have different dimensions (linear or nearest)\r\n\t\\param flip flip the texture vertically when copying it\r\n\t\\note No mask to specify, as this is assumed to be COLOR.\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblitToColorAttachment(const ITexture2D& src, IRenderTarget& dst, int location, GLenum filter = GL_LINEAR, bool flip = false);\r\n\t\r\n\t/**\r\n\tCopy the content of a rendertarget first color attachment to a texture, resizing if needed.\r\n\t\\param src source rendertarget\r\n\t\\param dst destination texture\r\n\t\\param mask which part of the buffer to copy (color, depth, stencil).\r\n\t\\param filter filtering mode if the two buffers have different dimensions (linear or nearest)\r\n\t\\note The blit can only happen for color attachment 0 in dst.\r\n\t\\warning If the mask contains the depth or stencil, filter must be GL_NEAREST\r\n\t \\ingroup sibr_graphics\r\n\t*/\r\n\tSIBR_GRAPHICS_EXPORT void\t\t\tblit(const IRenderTarget& src, const ITexture2D& dst, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_LINEAR);\r\n\r\n\t/** Display a RenderTarget into a popup OpenCV window.\r\n\t\\param rt the rendertarget to display\r\n\t\\param winTitle the window title\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\ttemplate <typename T_Type, unsigned T_NumComp>\r\n\tstatic void\t\tshow(const RenderTarget<T_Type, T_NumComp>& rt, const std::string& winTitle = \"sibr::show()\") {\r\n\t\tImage<T_Type, T_NumComp> img;\r\n\t\trt.readBack(img);\r\n\t\tshow(img, winTitle);\r\n\t}\r\n\r\n\t/** Display a texture into a popup OpenCV window.\r\n\t\\param texture the texture to display\r\n\t\\param winTitle the window title\r\n\t\\ingroup sibr_graphics\r\n\t*/\r\n\ttemplate <typename T_Type, unsigned T_NumComp>\r\n\tstatic void\t\tshow(const Texture2D<T_Type, T_NumComp>& texture, const std::string& winTitle = \"sibr::show()\") {\r\n\t\tImage<T_Type, T_NumComp> img(texture.w(), texture.h());\r\n\r\n\t\tglActiveTexture(GL_TEXTURE0);\r\n\t\tglBindTexture(GL_TEXTURE_2D, texture.handle());\r\n\r\n\t\tglGetTexImage(GL_TEXTURE_2D, 0, sibr::GLFormat<T_Type, T_NumComp>::format, sibr::GLType<T_Type>::type, img.data());\r\n\t\tshow(img, winTitle);\r\n\t}\r\n\r\n\t// --- TYPEDEFS --------------------------------------------------\r\n\r\n\ttypedef Texture2D<unsigned char, 3>     Texture2DRGB;\r\n\ttypedef Texture2D<unsigned char, 4>     Texture2DRGBA;\r\n\ttypedef Texture2D<unsigned char, 1>     Texture2DLum;\r\n\r\n\ttypedef Texture2D<unsigned short, 4>    Texture2DRGBA16;\r\n\ttypedef Texture2D<unsigned short, 1>    Texture2DLum16;\r\n\ttypedef Texture2D<unsigned short, 2>    Texture2DUV16;\r\n\r\n\ttypedef Texture2D<short, 2>             Texture2DUV16s;\r\n\r\n\ttypedef Texture2D<float, 3>             Texture2DRGB32F;\r\n\ttypedef Texture2D<float, 4>             Texture2DRGBA32F;\r\n\ttypedef Texture2D<float, 2>             Texture2DUV32F;\r\n\ttypedef Texture2D<float, 1>             Texture2DLum32F;\r\n\r\n\r\n\ttypedef Texture2DArray<unsigned char, 1>     Texture2DArrayLum;\r\n\ttypedef Texture2DArray<unsigned char, 2>     Texture2DArrayUV;\r\n\ttypedef Texture2DArray<unsigned char, 3>     Texture2DArrayRGB;\r\n\ttypedef Texture2DArray<unsigned char, 4>     Texture2DArrayRGBA;\r\n\r\n\ttypedef Texture2DArray<unsigned short, 1>    Texture2DArrayLum16;\r\n\ttypedef Texture2DArray<unsigned short, 2>    Texture2DArrayUV16;\r\n\ttypedef Texture2DArray<unsigned short, 3>    Texture2DArrayRGB16;\r\n\ttypedef Texture2DArray<unsigned short, 4>    Texture2DArrayRGBA16;\r\n\r\n\ttypedef Texture2DArray<short, 1>             Texture2DArrayLum16s;\r\n\ttypedef Texture2DArray<short, 2>             Texture2DArrayUV16s;\r\n\ttypedef Texture2DArray<short, 3>             Texture2DArrayRGB16s;\r\n\ttypedef Texture2DArray<short, 4>             Texture2DArrayRGBA16s;\r\n\r\n\ttypedef Texture2DArray<int, 1>\t\t\t\t Texture2DArrayInt1;\r\n\ttypedef Texture2DArray<int, 2>\t\t\t\t Texture2DArrayInt2;\r\n\ttypedef Texture2DArray<int, 3>\t\t\t\t Texture2DArrayInt3;\r\n\ttypedef Texture2DArray<int, 4>\t\t\t\t Texture2DArrayInt4;\r\n\r\n\ttypedef Texture2DArray<float, 1>             Texture2DArrayLum32F;\r\n\ttypedef Texture2DArray<float, 2>             Texture2DArrayUV32F;\r\n\ttypedef Texture2DArray<float, 3>             Texture2DArrayRGB32F;\r\n\ttypedef Texture2DArray<float, 4>             Texture2DArrayRGBA32F;\r\n\r\n\r\n\ttypedef TextureCubeMap<unsigned char, 1>    TextureCubeMapLum;\r\n\ttypedef TextureCubeMap<unsigned char, 3>    TextureCubeMapRGB;\r\n\ttypedef TextureCubeMap<unsigned char, 4>    TextureCubeMapRGBA;\r\n\r\n\ttypedef TextureCubeMap<unsigned short, 1>   TextureCubeMapLum16;\r\n\ttypedef TextureCubeMap<unsigned short, 2>   TextureCubeMapUV16;\r\n\ttypedef TextureCubeMap<unsigned short, 4>   TextureCubeMapRGBA16;\r\n\r\n\ttypedef TextureCubeMap<short, 2>            TextureCubeMapUV16s;\r\n\r\n\ttypedef TextureCubeMap<float, 1>            TextureCubeMapLum32F;\r\n\ttypedef TextureCubeMap<float, 3>            TextureCubeMapRGB32F;\r\n\ttypedef TextureCubeMap<float, 4>            TextureCubeMapRGBA32F;\r\n\r\n\t/* Note concerning depth buffers :\r\n\t* We don't support depth only rendertargets.\r\n\t* Other kinds of RenderTarget (e.g. RenderTargetRGB) creates\r\n\t* also a new depth buffer that is bound with the color buffer, so no need to explicitely create one.\r\n\t* typedef RenderTarget<depth24,1>        RenderTargetDepth24;\r\n\t*/\r\n\r\n\r\n\t// ----DEFINITIONS Texture2D --------------------------------------------------\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tGLuint Texture2D<T_Type, T_NumComp>::create2D(const ImageType& img, uint flags) {\r\n\t\tGLuint id = 0;\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglGenTextures(1, &id);\r\n\t\tglBindTexture(GL_TEXTURE_2D, id);\r\n\t\tif (flags & SIBR_CLAMP_UVS) {\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\r\n\t\t}\r\n\t\telse if (flags & SIBR_CLAMP_TO_BORDER) {\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);\r\n\t\t}\r\n\t\tif (flags & SIBR_GPU_AUTOGEN_MIPMAP) {\r\n\t\t\tif (flags & SIBR_GPU_INTEGER) {\r\n\t\t\t\tthrow std::runtime_error(\"Mipmapping on integer texture not supported, probably not even by OpenGL\");\r\n\t\t\t}\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\t}\r\n\t\telse {\r\n#if SIBR_COMPILE_FORCE_SAMPLING_LINEAR\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n#else\r\n\t\t\tif (flags & SIBR_GPU_LINEAR_SAMPLING) {\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\r\n\t\t\t}\r\n#endif\r\n\t\t}\r\n\t\tsend2D(id, img, flags);\r\n\t\tCHECK_GL_ERROR;\r\n\t\treturn id;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\t/*static*/ GLuint Texture2D<T_Type, T_NumComp>::create2D(const std::vector<PixelImage>& miparray, uint flags) {\r\n\t\tGLuint id = 0;\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglGenTextures(1, &id);\r\n\t\tglBindTexture(GL_TEXTURE_2D, id);\r\n\t\tif (flags & SIBR_CLAMP_UVS) {\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\r\n\t\t}\r\n\t\telse if (flags & SIBR_CLAMP_TO_BORDER) {\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);\r\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);\r\n\t\t}\r\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, int(miparray.size()) - 1);\r\n\t\tsend2Dmipmap(id, miparray, flags);\r\n\t\tCHECK_GL_ERROR;\r\n\t\treturn id;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2D<T_Type, T_NumComp>::send2D(GLuint id, const ImageType& img, uint flags) {\r\n\t\tusing FormatInfos = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tif (flags & SIBR_GPU_INTEGER) {\r\n\t\t\tif (FormatInfos::int_internal_format < 0) {\r\n\t\t\t\tthrow std::runtime_error(\"Texture format does not support integer mapping\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tbool flip = flags & SIBR_FLIP_TEXTURE;\r\n\t\tImageType flippedImg;\r\n\t\tif (flip) {\r\n\t\t\tflippedImg = FormatInfos::flip(img);\r\n\t\t}\r\n\t\tconst ImageType& sendedImg = flip ? flippedImg : img;\r\n\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\t\tglBindTexture(GL_TEXTURE_2D, id);\r\n\t\tglTexImage2D(GL_TEXTURE_2D,\r\n\t\t\t0,\r\n\t\t\t(flags & SIBR_GPU_INTEGER) ? FormatInfos::int_internal_format : FormatInfos::internal_format,\r\n\t\t\tFormatInfos::width(sendedImg), FormatInfos::height(sendedImg),\r\n\t\t\t0,\r\n\t\t\t(flags & SIBR_GPU_INTEGER) ? FormatInfos::int_format : FormatInfos::format,\r\n\t\t\tFormatInfos::type,\r\n\t\t\tFormatInfos::data(sendedImg)\r\n\t\t);\r\n\r\n\t\tbool autoMIPMAP = ((flags & SIBR_GPU_AUTOGEN_MIPMAP) != 0);\r\n\t\tif (autoMIPMAP)\r\n\t\t\tglGenerateMipmap(GL_TEXTURE_2D);\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\t// Send 2D texture to GPU memory, each mipmap is specified\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\t/*static*/ void Texture2D<T_Type, T_NumComp>::send2Dmipmap(GLuint id, const std::vector<PixelImage>& miparray, uint flags) {\r\n\t\tCHECK_GL_ERROR;\r\n\t\tif (flags & SIBR_GPU_INTEGER) {\r\n\t\t\tthrow std::runtime_error(\"Mipmapping on integer texture not supported, probably not even by OpenGL\");\r\n\t\t}\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\t\tglBindTexture(GL_TEXTURE_2D, id);\r\n\r\n\t\tstd::vector<PixelImage> flippedMipArray;\r\n\t\tbool flip = flags & SIBR_FLIP_TEXTURE;\r\n\t\tif (flip) {\r\n\t\t\tflippedMipArray.resize(miparray.size());\r\n#pragma omp parallel for\r\n\t\t\tfor (uint l = 0; l < miparray.size(); l++) {\r\n\t\t\t\tflippedMipArray[l] = miparray[l].clone();\r\n\t\t\t\tflippedMipArray[l].flipH();\r\n\t\t\t}\r\n\t\t}\r\n\t\tconst std::vector<PixelImage>& sendedMipArray = flip ? flippedMipArray : miparray;\r\n\r\n\t\tfor (uint l = 0; l < miparray.size(); l++) {\r\n\t\t\tglTexImage2D(GL_TEXTURE_2D,\r\n\t\t\t\tl,\r\n\t\t\t\tGLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::internal_format,\r\n\t\t\t\tmiparray[l].w(), miparray[l].h(),\r\n\t\t\t\t0,\r\n\t\t\t\tGLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::format,\r\n\t\t\t\tGLType<typename PixelFormat::Type>::type,\r\n\t\t\t\tsendedMipArray[l].data()\r\n\t\t\t);\r\n\t\t}\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2D<T_Type, T_NumComp>::Texture2D(void) {\r\n\t\tm_Flags = 0;\r\n\t\tm_W = 0;\r\n\t\tm_H = 0;\r\n\t\tm_Handle = 0;\r\n\t\tm_autoMIPMAP = false;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tTexture2D<T_Type, T_NumComp>::Texture2D(const ImageType& img, uint flags) {\r\n\t\tusing TexFormat = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\t\tm_Flags = flags;\r\n\t\tm_W = TexFormat::width(img);\r\n\t\tm_H = TexFormat::height(img);\r\n\t\tm_Handle = create2D(img, m_Flags);\r\n\t\tm_autoMIPMAP = ((flags & SIBR_GPU_AUTOGEN_MIPMAP) != 0);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2D<T_Type, T_NumComp>::Texture2D(const std::vector<PixelImage>& miparray, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tm_W = miparray[0].w();\r\n\t\tm_H = miparray[0].h();\r\n\t\tm_Handle = create2D(miparray, m_Flags);\r\n\t\tm_autoMIPMAP = false;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2D<T_Type, T_NumComp>::~Texture2D(void) {\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglDeleteTextures(1, &m_Handle);\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tGLuint Texture2D<T_Type, T_NumComp>::handle(void) const { return m_Handle; }\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint   Texture2D<T_Type, T_NumComp>::w(void) const { return m_W; }\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint   Texture2D<T_Type, T_NumComp>::h(void) const { return m_H; }\r\n\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tsibr::Image<T_Type, T_NumComp>\t\tTexture2D<T_Type, T_NumComp>::readBack(void) const {\r\n\r\n\t\t// makes sure Vertex have the correct size (read back relies on pointers)\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\t\tglBindTexture(GL_TEXTURE_2D, handle());\r\n\r\n\t\tint w, h;\r\n\t\tglGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);\r\n\t\tglGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);\r\n\r\n\t\tsibr::Image<T_Type, T_NumComp> img(w, h);\r\n\r\n\t\tglGetTexImage(GL_TEXTURE_2D,\r\n\t\t\t0,\r\n\t\t\tGLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::format,\r\n\t\t\tGLType<typename PixelFormat::Type>::type,\r\n\t\t\timg.data()\r\n\t\t);\r\n\r\n\t\t// flip data vertically to get origin on lower left corner\r\n\t\timg.flipH();\r\n\r\n\t\tCHECK_GL_ERROR;\r\n\r\n\t\treturn img;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2D<T_Type, T_NumComp>::update(const ImageType& img) {\r\n\t\tusing FormatInfos = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\t\tif (FormatInfos::width(img) == w() && FormatInfos::height(img) == h())\r\n\t\t{\r\n\t\t\tbool flip = m_Flags & SIBR_FLIP_TEXTURE;\r\n\t\t\tImageType flippedImg;\r\n\t\t\tif (flip) {\r\n\t\t\t\tflippedImg = FormatInfos::flip(img);\r\n\t\t\t}\r\n\t\t\tconst ImageType& sendedImg = flip ? flippedImg : img;\r\n\r\n\t\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\t\t\tglBindTexture(GL_TEXTURE_2D, handle());\r\n\t\t\tglTexSubImage2D(GL_TEXTURE_2D, 0,\r\n\t\t\t\t0, 0, FormatInfos::width(sendedImg), FormatInfos::height(sendedImg),\r\n\t\t\t\tFormatInfos::format,\r\n\t\t\t\tFormatInfos::type,\r\n\t\t\t\tFormatInfos::data(sendedImg)\r\n\t\t\t);\r\n\t\t\tif (m_autoMIPMAP)\r\n\t\t\t\tglGenerateMipmap(GL_TEXTURE_2D);\r\n\t\t}\r\n\t\telse {\r\n\t\t\tm_W = FormatInfos::width(img);\r\n\t\t\tm_H = FormatInfos::height(img);\r\n\t\t\tsend2D(m_Handle, img, m_Flags);\r\n\t\t}\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid Texture2D<T_Type, T_NumComp>::mipmap(int maxLOD) {\r\n\t\tglBindTexture(GL_TEXTURE_2D, handle());\r\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLOD >= 0 ? maxLOD : 1000);\r\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\tm_autoMIPMAP = true;\r\n\t\tglGenerateMipmap(GL_TEXTURE_2D);\r\n\t}\r\n\r\n\r\n\r\n\t// ----DEFINITIONS Texture2DArray --------------------------------------------------\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const uint d, uint flags) {\r\n\t\tm_Depth = d;\r\n\t\tm_Flags = flags;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const uint w, const uint h, const uint d, uint flags) {\r\n\t\tm_W = w;\r\n\t\tm_H = h;\r\n\t\tm_Depth = d;\r\n\t\tm_Flags = flags;\r\n\t\tcreateArray();\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const std::vector<ImageType>& images, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tcreateFromImages(images, flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const std::vector<ImageType>& images, uint w, uint h, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tcreateFromImages(images, w, h, flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const std::vector<std::vector<ImageType>>& images, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tcreateFromImages(images, flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const std::vector<std::vector<ImageType>>& images, uint w, uint h, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tcreateFromImages(images, w, h, flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2DArray<T_Type, T_NumComp>::Texture2DArray(const std::vector<typename PixelRT::Ptr>& RTs, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tcreateFromRTs(RTs, flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createArray(uint compression) {\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglGenTextures(1, &m_Handle);\r\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, m_Handle);\r\n\r\n\t\tconst bool autoMIPMAP = ((m_Flags & SIBR_GPU_AUTOGEN_MIPMAP) != 0);\r\n\t\tconst int numMipMap = autoMIPMAP ? (int)std::floor(std::log2(std::max(m_W, m_H))) : m_numLODs;\r\n\r\n\t\tm_numLODs = numMipMap;\r\n\r\n\t\tif (m_numLODs == 1) {\r\n\t\t\tif (m_Flags & SIBR_GPU_LINEAR_SAMPLING) {\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\telse {\r\n\t\t\tif (m_Flags & SIBR_GPU_LINEAR_SAMPLING) {\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);\r\n\t\t\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\r\n\t\tglTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\r\n\r\n\t\tuint internal_format = GLFormat<T_Type, T_NumComp>::internal_format;\r\n\t\tif (compression)\r\n\t\t\tinternal_format = compression;\r\n\r\n\t\tglTexStorage3D(GL_TEXTURE_2D_ARRAY, numMipMap,\r\n\t\t\tinternal_format,\r\n\t\t\tm_W,\r\n\t\t\tm_H,\r\n\t\t\tm_Depth\r\n\t\t);\r\n\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::sendArray(const std::vector<ImageType>& images) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, m_Handle);\r\n\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\r\n\t\t// Make sure all images have the same size.\r\n\t\tstd::vector<ImageType> tmp;\r\n\t\tstd::vector<const ImageType*> imagesPtrToSend = applyFlipAndResize(images, tmp, m_W, m_H);\r\n\r\n\t\tfor (int im = 0; im < (int)m_Depth; ++im) {\r\n\t\t\tglTexSubImage3D(GL_TEXTURE_2D_ARRAY,\r\n\t\t\t\t0,\r\n\t\t\t\t0, 0, im,\r\n\t\t\t\tm_W,\r\n\t\t\t\tm_H,\r\n\t\t\t\t1, // one slice at a time\r\n\t\t\t\tImgTypeInfo::format,\r\n\t\t\t\tImgTypeInfo::type,\r\n\t\t\t\tImgTypeInfo::data(*imagesPtrToSend[im])\r\n\t\t\t);\r\n\t\t\t//CHECK_GL_ERROR;\r\n\t\t}\r\n\t\tbool autoMIPMAP = ((m_Flags & SIBR_GPU_AUTOGEN_MIPMAP) != 0);\r\n\t\tif (autoMIPMAP) {\r\n\t\t\tglGenerateMipmap(GL_TEXTURE_2D_ARRAY);\r\n\t\t}\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::sendMipArray(const std::vector<std::vector<ImageType>>& images) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, m_Handle);\r\n\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\r\n\t\tassert(m_numLODs == images.size());\r\n\t\tfor (int lid = 0; lid < int(images.size()); ++lid) {\r\n\r\n\t\t\tassert(m_Depth == images[lid].size());\r\n\r\n\t\t\t// Make sure all images have the same size.\r\n\t\t\tconst uint dW = m_W / (1 << lid);\r\n\t\t\tconst uint dH = m_H / (1 << lid);\r\n\t\t\tstd::vector<ImageType> tmp;\r\n\t\t\tstd::vector<const ImageType*> imagesPtrToSend = applyFlipAndResize(images[lid], tmp, dW, dH);\r\n\r\n\t\t\tfor (int im = 0; im < (int)m_Depth; ++im) {\r\n\t\t\t\tglTexSubImage3D(GL_TEXTURE_2D_ARRAY,\r\n\t\t\t\t\tlid,\r\n\t\t\t\t\t0, 0, im,\r\n\t\t\t\t\tdW,\r\n\t\t\t\t\tdH,\r\n\t\t\t\t\t1, // one slice at a time\r\n\t\t\t\t\tImgTypeInfo::format,\r\n\t\t\t\t\tImgTypeInfo::type,\r\n\t\t\t\t\tImgTypeInfo::data(*imagesPtrToSend[im])\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t}\r\n\t\t// No auto mipmap when specifying the mips.\r\n\t\tm_Flags &= ~SIBR_GPU_AUTOGEN_MIPMAP;\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tstd::vector<const ImageType*> Texture2DArray<T_Type, T_NumComp>::applyFlipAndResize(\r\n\t\tconst std::vector<ImageType>& images,\r\n\t\tstd::vector<ImageType>& tmp, uint tw, uint th,\r\n\t\tconst std::vector<int>& slices)\r\n\t{\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tstd::vector<const ImageType*> imagesPtrToSend(images.size());\r\n\t\ttmp.resize(images.size());\r\n\r\n\t\tbool flip = m_Flags & SIBR_FLIP_TEXTURE;\r\n\t\t//#pragma omp parallel for // Disabled due to performance reasons when live-updating slices.\r\n\t\tfor (int slice_id = 0; slice_id < (int)slices.size(); ++slice_id) {\r\n\t\t\tint im = slices[slice_id];\r\n\r\n\t\t\tbool resize = !(tw == ImgTypeInfo::width(images[im]) && th == ImgTypeInfo::height(images[im]));\r\n\t\t\tif (!flip && !resize) {\r\n\t\t\t\timagesPtrToSend[im] = &images[im];\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tif (resize) {\r\n\t\t\t\t\ttmp[im] = ImgTypeInfo::resize(images[im], tw, th);\r\n\t\t\t\t}\r\n\t\t\t\tif (flip) {\r\n\t\t\t\t\ttmp[im] = ImgTypeInfo::flip(resize ? tmp[im] : images[im]);\r\n\t\t\t\t}\r\n\t\t\t\timagesPtrToSend[im] = &tmp[im];\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn imagesPtrToSend;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\ttemplate<typename ImageType>\r\n\tstd::vector<const ImageType*> Texture2DArray<T_Type, T_NumComp>::applyFlipAndResize(\r\n\t\tconst std::vector<ImageType>& images,\r\n\t\tstd::vector<ImageType>& tmp, uint tw, uint th\r\n\t) {\r\n\t\tstd::vector<int> slices(m_Depth);\r\n\t\tfor (int i = 0; i < (int)m_Depth; ++i) {\r\n\t\t\tslices[i] = i;\r\n\t\t}\r\n\t\treturn applyFlipAndResize(images, tmp, tw, th, slices);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::sendRTarray(const std::vector<typename PixelRT::Ptr>& RTs) {\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, m_Handle);\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\t\tfor (int im = 0; im < (int)m_Depth; ++im) {\r\n\t\t\t// Set correct RT as read-framebuffer.\r\n\r\n\t\t\tRTs[im]->bind();\r\n\t\t\tglCopyTexSubImage3D(GL_TEXTURE_2D_ARRAY,\r\n\t\t\t\t0,\r\n\t\t\t\t0, 0, im,\r\n\t\t\t\t0, 0,\r\n\t\t\t\tm_W,\r\n\t\t\t\tm_H\r\n\t\t\t);\r\n\t\t\tRTs[im]->unbind();\r\n\t\t}\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createFromImages(const std::vector<ImageType>& images, uint flags) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tsibr::Vector2u maxSize(0, 0);\r\n\t\tfor (const auto& img : images) {\r\n\t\t\tmaxSize = maxSize.cwiseMax(sibr::Vector2u(ImgTypeInfo::width(img), ImgTypeInfo::height(img)));\r\n\t\t}\r\n\t\tcreateFromImages(images, maxSize[0], maxSize[1], flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createFromImages(const std::vector<ImageType>& images, uint w, uint h, uint flags) {\r\n\t\tm_W = w;\r\n\t\tm_H = h;\r\n\t\tm_Depth = (uint)images.size();\r\n\t\tm_Flags = flags;\r\n\t\tcreateArray();\r\n\t\tsendArray(images);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createCompressedFromImages(const std::vector<ImageType>& images, uint compression, uint flags) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tsibr::Vector2u maxSize(0, 0);\r\n\t\tfor (const auto& img : images) {\r\n\t\t\tmaxSize = maxSize.cwiseMax(sibr::Vector2u(ImgTypeInfo::width(img), ImgTypeInfo::height(img)));\r\n\t\t}\r\n\t\tcreateCompressedFromImages(images, maxSize[0], maxSize[1], compression, flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createCompressedFromImages(const std::vector<ImageType>& images, uint w, uint h, uint compression, uint flags) {\r\n\t\tm_W = w;\r\n\t\tm_H = h;\r\n\t\tm_Depth = (uint)images.size();\r\n\t\tm_Flags = flags;\r\n\t\tcreateArray(compression);\r\n\t\tsendArray(images);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createFromImages(const std::vector<std::vector<ImageType>>& images, uint flags) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tsibr::Vector2u maxSize(0, 0);\r\n\t\tfor (const auto& img : images[0]) {\r\n\t\t\tmaxSize = maxSize.cwiseMax(sibr::Vector2u(ImgTypeInfo::width(img), ImgTypeInfo::height(img)));\r\n\t\t}\r\n\t\tcreateFromImages(images, maxSize[0], maxSize[1], flags);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createFromImages(const std::vector<std::vector<ImageType>>& images, uint w, uint h, uint flags) {\r\n\t\tm_W = w;\r\n\t\tm_H = h;\r\n\t\tm_Depth = uint(images[0].size());\r\n\t\tm_Flags = flags & ~SIBR_GPU_AUTOGEN_MIPMAP;\r\n\t\tm_numLODs = uint(images.size());\r\n\t\tcreateArray();\r\n\r\n\t\tsendMipArray(images);\r\n\t}\r\n\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp> template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::updateFromImages(const std::vector<ImageType>& images) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tsibr::Vector2u maxSize(0, 0);\r\n\t\tfor (const auto& img : images) {\r\n\t\t\tmaxSize = maxSize.cwiseMax(sibr::Vector2u(ImgTypeInfo::width(img), ImgTypeInfo::height(img)));\r\n\t\t}\r\n\t\tif (images.size() == m_Depth && m_W == maxSize[0] && m_H == maxSize[1]) {\r\n\t\t\tsendArray(images);\r\n\t\t}\r\n\t\telse {\r\n\t\t\tcreateFromImages(images, m_Flags);\r\n\t\t}\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>  template<typename ImageType>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::updateSlices(const std::vector<ImageType>& images, const std::vector<int>& slices) {\r\n\t\tusing ImgTypeInfo = GLTexFormat<ImageType, T_Type, T_NumComp>;\r\n\r\n\t\tint numSlices = (int)slices.size();\r\n\t\tif (numSlices == 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tsibr::Vector2u maxSize(0, 0);\r\n\t\tfor (int i = 0; i < numSlices; ++i) {\r\n\t\t\tmaxSize = maxSize.cwiseMax(sibr::Vector2u(ImgTypeInfo::width(images[slices[i]]), ImgTypeInfo::height(images[slices[i]])));\r\n\t\t}\r\n\t\tif (m_W != maxSize[0] || m_H != maxSize[1]) {\r\n\t\t\tm_W = maxSize[0];\r\n\t\t\tm_H = maxSize[1];\r\n\t\t}\r\n\r\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, m_Handle);\r\n\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\r\n\t\tstd::vector<ImageType> tmp;\r\n\t\tstd::vector<const ImageType*> imagesPtrToSend = applyFlipAndResize(images, tmp, m_W, m_H, slices);\r\n\r\n\t\tfor (int i = 0; i < numSlices; ++i) {\r\n\t\t\tglTexSubImage3D(GL_TEXTURE_2D_ARRAY,\r\n\t\t\t\t0,\r\n\t\t\t\t0, 0, slices[i],\r\n\t\t\t\tm_W,\r\n\t\t\t\tm_H,\r\n\t\t\t\t1, // one slice at a time\r\n\t\t\t\tImgTypeInfo::format,\r\n\t\t\t\tImgTypeInfo::type,\r\n\t\t\t\tImgTypeInfo::data(*imagesPtrToSend[slices[i]])\r\n\t\t\t);\r\n\t\t}\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid Texture2DArray<T_Type, T_NumComp>::createFromRTs(const std::vector<typename PixelRT::Ptr>& RTs, uint flags) {\r\n\t\tm_W = 0;\r\n\t\tm_H = 0;\r\n\t\tfor (const auto& RT : RTs) {\r\n\t\t\tm_W = (std::max)(m_W, RT->w());\r\n\t\t\tm_H = (std::max)(m_H, RT->h());\r\n\t\t}\r\n\t\tm_Depth = (uint)RTs.size();\r\n\t\tm_Flags = flags;\r\n\t\tcreateArray();\r\n\t\tsendRTarray(RTs);\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTexture2DArray<T_Type, T_NumComp>::~Texture2DArray(void) {\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglDeleteTextures(1, &m_Handle);\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tGLuint Texture2DArray<T_Type, T_NumComp>::handle(void) const { return m_Handle; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint Texture2DArray<T_Type, T_NumComp>::w(void) const { return m_W; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint Texture2DArray<T_Type, T_NumComp>::h(void) const { return m_H; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint Texture2DArray<T_Type, T_NumComp>::depth(void) const { return m_Depth; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint Texture2DArray<T_Type, T_NumComp>::numLODs(void) const { return m_numLODs; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tVector4f\tTexture2DArray<T_Type, T_NumComp>::readBackPixel(int i, int x, int y, uint lod) const {\r\n\t\tVector4f out;\r\n//#define HEADLESS\r\n#ifdef HEADLESS\r\n\t\tSIBR_ERR << \"HEADLESS -- No support for readBackPixel\" << std::endl;\r\n#else\r\n\t\tglGetTextureSubImage(handle(),\r\n\t\t\tlod, x, y, i, 1, 1, 1,\r\n\t\t\tGL_RGBA, GL_FLOAT, 4 * sizeof(float), out.data()\r\n\t\t);\r\n#endif\r\n\t\tCHECK_GL_ERROR;\r\n\t\tfor (uint c = T_NumComp; c < 4; ++c) {\r\n\t\t\tout[c] = 0;\r\n\t\t}\r\n\t\treturn out;\r\n\t}\r\n\r\n\r\n\t// ----DEFINITIONS TextureCubeMap --------------------------------------------------\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTextureCubeMap<T_Type, T_NumComp>::TextureCubeMap(void) {}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTextureCubeMap<T_Type, T_NumComp>::TextureCubeMap(const uint w, const uint h, uint flags) {\r\n\t\tm_W = w;\r\n\t\tm_H = h;\r\n\t\tm_Flags = flags;\r\n\t\tcreateCubeMap();\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTextureCubeMap<T_Type, T_NumComp>::TextureCubeMap(const PixelImage& xpos, const PixelImage& xneg,\r\n\t\tconst PixelImage& ypos, const PixelImage& yneg,\r\n\t\tconst PixelImage& zpos, const PixelImage& zneg, uint flags) {\r\n\t\tm_Flags = flags;\r\n\t\tcreateFromImages(xpos, xneg, ypos, yneg, zpos, zneg, flags);\r\n\t}\r\n\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid TextureCubeMap<T_Type, T_NumComp>::createCubeMap() {\r\n\r\n\t\t// We enable seamless junctions between cubemap faces.\r\n\t\tstatic bool enableStates = false;\r\n\t\tif (enableStates == false)\r\n\t\t{\r\n\t\t\tglEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);\r\n\t\t\tenableStates = true;\r\n\t\t}\r\n\t\tCHECK_GL_ERROR;\r\n\r\n\t\tglGenTextures(1, &m_Handle);\r\n\t\tglBindTexture(GL_TEXTURE_CUBE_MAP, m_Handle);\r\n\r\n\t\tif (m_Flags & SIBR_GPU_LINEAR_SAMPLING) {\r\n\t\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\r\n\t\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r\n\t\t}\r\n\t\telse {\r\n\t\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\r\n\t\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\r\n\t\t}\r\n\r\n\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\r\n\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\r\n\t\tglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);\r\n\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid TextureCubeMap<T_Type, T_NumComp>::sendCubeMap(const PixelImage& xpos, const PixelImage& xneg,\r\n\t\tconst PixelImage& ypos, const PixelImage& yneg,\r\n\t\tconst PixelImage& zpos, const PixelImage& zneg) {\r\n\t\tCHECK_GL_ERROR;\r\n\r\n\t\tif (m_Flags & SIBR_GPU_INTEGER) {\r\n\t\t\tif (GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_internal_format < 0) {\r\n\t\t\t\tthrow std::runtime_error(\"Texture format does not support integer mapping\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Handle flipping.\r\n\t\tconst PixelImage* sendedXpos = &xpos;\r\n\t\tconst PixelImage* sendedYpos = &ypos;\r\n\t\tconst PixelImage* sendedZpos = &zpos;\r\n\t\tconst PixelImage* sendedXneg = &xneg;\r\n\t\tconst PixelImage* sendedYneg = &yneg;\r\n\t\tconst PixelImage* sendedZneg = &zneg;\r\n\r\n\t\tPixelImage flippedXpos, flippedYpos, flippedZpos;\r\n\t\tPixelImage flippedXneg, flippedYneg, flippedZneg;\r\n\r\n\t\t// ...\r\n\t\tif (m_Flags & SIBR_FLIP_TEXTURE) {\r\n\t\t\tflippedXpos = xpos.clone();\r\n\t\t\tflippedXpos.flipH();\r\n\t\t\tsendedXpos = &flippedXpos;\r\n\r\n\t\t\tflippedYpos = ypos.clone();\r\n\t\t\tflippedYpos.flipH();\r\n\t\t\tsendedYpos = &flippedYpos;\r\n\r\n\t\t\tflippedZpos = zpos.clone();\r\n\t\t\tflippedZpos.flipH();\r\n\t\t\tsendedZpos = &flippedZpos;\r\n\r\n\t\t\tflippedXneg = xneg.clone();\r\n\t\t\tflippedXneg.flipH();\r\n\t\t\tsendedXneg = &flippedXneg;\r\n\r\n\t\t\tflippedYneg = yneg.clone();\r\n\t\t\tflippedYneg.flipH();\r\n\t\t\tsendedYneg = &flippedYneg;\r\n\r\n\t\t\tflippedZneg = zneg.clone();\r\n\t\t\tflippedZneg.flipH();\r\n\t\t\tsendedZneg = &flippedZneg;\r\n\t\t}\r\n\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\r\n\t\tglBindTexture(GL_TEXTURE_CUBE_MAP, m_Handle);\r\n\r\n\t\tconst auto tinternal_format = (m_Flags & SIBR_GPU_INTEGER)\r\n\t\t\t? GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_internal_format\r\n\t\t\t: GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::internal_format;\r\n\t\tconst auto tformat = (m_Flags & SIBR_GPU_INTEGER)\r\n\t\t\t? GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::int_format\r\n\t\t\t: GLFormat<typename PixelFormat::Type, PixelFormat::NumComp>::format;\r\n\t\tconst auto ttype = GLType<typename PixelFormat::Type>::type;\r\n\r\n\t\tglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, tinternal_format, xpos.w(), xpos.h(), 0, tformat, ttype, xpos.data());\r\n\t\tglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, tinternal_format, xneg.w(), xneg.h(), 0, tformat, ttype, xneg.data());\r\n\t\tglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, tinternal_format, ypos.w(), ypos.h(), 0, tformat, ttype, ypos.data());\r\n\t\tglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, tinternal_format, yneg.w(), yneg.h(), 0, tformat, ttype, yneg.data());\r\n\t\tglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, tinternal_format, zpos.w(), zpos.h(), 0, tformat, ttype, zpos.data());\r\n\t\tglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, tinternal_format, zneg.w(), zneg.h(), 0, tformat, ttype, zneg.data());\r\n\r\n\r\n\t\tbool autoMIPMAP = ((m_Flags & SIBR_GPU_AUTOGEN_MIPMAP) != 0);\r\n\t\tif (autoMIPMAP) {\r\n\t\t\tglGenerateMipmap(GL_TEXTURE_CUBE_MAP);\r\n\t\t}\r\n\r\n\r\n\t}\r\n\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tvoid TextureCubeMap<T_Type, T_NumComp>::createFromImages(const PixelImage& xpos, const PixelImage& xneg,\r\n\t\tconst PixelImage& ypos, const PixelImage& yneg,\r\n\t\tconst PixelImage& zpos, const PixelImage& zneg, uint flags) {\r\n\t\tconst int numMipMap = 1;\r\n\t\tsibr::Vector2u maxSize(0, 0);\r\n\t\t/// \\todo TODO: check if the six images have the same size.\r\n\t\tm_W = xpos.w();\r\n\t\tm_H = xpos.h();\r\n\t\tm_Flags = flags;\r\n\t\tcreateCubeMap();\r\n\t\tsendCubeMap(xpos, xneg, ypos, yneg, zpos, zneg);\r\n\t}\r\n\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tTextureCubeMap<T_Type, T_NumComp>::~TextureCubeMap(void) {\r\n\t\tCHECK_GL_ERROR;\r\n\t\tglDeleteTextures(1, &m_Handle);\r\n\t\tCHECK_GL_ERROR;\r\n\t}\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tGLuint TextureCubeMap<T_Type, T_NumComp>::handle(void) const { return m_Handle; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint TextureCubeMap<T_Type, T_NumComp>::w(void) const { return m_H; }\r\n\r\n\ttemplate<typename T_Type, unsigned int T_NumComp>\r\n\tuint TextureCubeMap<T_Type, T_NumComp>::h(void) const { return m_W; }\r\n\r\n} // namespace sibr\r\n\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Types.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include <type_traits>\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n# include \"core/graphics/Image.hpp\"\r\n\r\n\r\n# define SIBR_GPU_AUTOGEN_MIPMAP\t\t(1<<0)\r\n# define SIBR_GPU_MULSTISAMPLE\t\t\t(1<<1)\r\n# define SIBR_GPU_LINEAR_SAMPLING\t\t(1<<2)\r\n# define SIBR_GPU_INTEGER\t\t\t\t(1<<4)\r\n# define SIBR_MSAA4X\t\t\t\t\t(1<<5)\r\n# define SIBR_MSAA8X\t\t\t\t\t(1<<6)\r\n# define SIBR_MSAA16X\t\t\t\t\t(1<<7)\r\n# define SIBR_MSAA32X\t\t\t\t\t(1<<8)\r\n# define SIBR_STENCIL_BUFFER\t\t\t(1<<9)\r\n# define SIBR_CLAMP_UVS\t\t\t\t\t(1<<10)\r\n# define SIBR_CLAMP_TO_BORDER\t\t\t(1<<11)\r\n# define SIBR_FLIP_TEXTURE\t\t\t\t(1<<12)\r\n\r\n# define SIBR_COMPILE_FORCE_SAMPLING_LINEAR\t0\r\n\r\nnamespace sibr{\r\n\r\n\r\n\t/**\r\n\t* Contain type utilities to match C, cv and sibr types to OpenGL formats.\r\n\t* \\addtogroup sibr_graphics\r\n\t* @{\r\n\t*/\r\n\r\n\t// --- TYPE HELPERS ---------------------------------------------------\r\n\r\n\t/** Helper building the correspondence between a GL type and a C type. */\r\n\ttemplate <typename T> class GLType;\r\n\r\n\t/** Helper building the correspondence between a GL type and a C type. */\r\n\ttemplate <> class GLType<unsigned char> {\r\n\tpublic:\r\n\t\tenum { type = GL_UNSIGNED_BYTE };\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL type and a C type. */\r\n\ttemplate <> class GLType<unsigned short> {\r\n\tpublic:\r\n\t\tenum { type = GL_UNSIGNED_SHORT };\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL type and a C type. */\r\n\ttemplate <> class GLType<short> {\r\n\tpublic:\r\n\t\tenum { type = GL_SHORT };\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL type and a C type. */\r\n\ttemplate <> class GLType<float> {\r\n\tpublic:\r\n\t\tenum { type = GL_FLOAT };\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL type and a C type. */\r\n\ttemplate <> class GLType<int> {\r\n\tpublic:\r\n\t\tenum { type = GL_INT };\r\n\t};\r\n\r\n\t// --- FORMAT HELPERS -------------------------------------------------------\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <typename T_Type,int T_Num> class GLFormat;\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned char,1> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_R8,\r\n\t\t\tformat = GL_RED,\r\n\t\t\tint_internal_format = GL_R8UI,\r\n\t\t\tint_format = GL_RED_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned char, 2> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RG8,\r\n\t\t\tformat = GL_RG,\r\n\t\t\tint_internal_format = GL_RG8UI,\r\n\t\t\tint_format = GL_RG_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned char,3> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGB8,\r\n\t\t\tformat = GL_RGB,\r\n\t\t\tint_internal_format = GL_RGB8UI,\r\n\t\t\tint_format = GL_RGB_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned char,4> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGBA8,\r\n\t\t\tformat = GL_RGBA,\r\n\t\t\tint_internal_format = GL_RGBA8UI,\r\n\t\t\tint_format = GL_RGBA_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned short,1> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_R16,\r\n\t\t\tformat = GL_R,\r\n\t\t\tint_internal_format = GL_R16UI,\r\n\t\t\tint_format = GL_RED_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned short, 2> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RG16,\r\n\t\t\tformat = GL_RG,\r\n\t\t\tint_internal_format = GL_RG16UI,\r\n\t\t\tint_format = GL_RG_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned short,3> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGB16,\r\n\t\t\tformat = GL_RGB,\r\n\t\t\tint_internal_format = GL_RGB16UI,\r\n\t\t\tint_format = GL_RGB_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<unsigned short,4> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGBA16,\r\n\t\t\tformat = GL_RGBA,\r\n\t\t\tint_internal_format = GL_RGBA16UI,\r\n\t\t\tint_format = GL_RGBA_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<int,1> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_R32I,\r\n\t\t\tformat = GL_RED_INTEGER,\r\n\t\t\tint_internal_format = GL_R32I,\r\n\t\t\tint_format = GL_RED_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<int, 2> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RG32I,\r\n\t\t\tformat = GL_RG_INTEGER,\r\n\t\t\tint_internal_format = GL_RG32I,\r\n\t\t\tint_format = GL_RG_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<int, 3> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGB32I,\r\n\t\t\tformat = GL_RGB_INTEGER,\r\n\t\t\tint_internal_format = GL_RGB32I,\r\n\t\t\tint_format = GL_RGB_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<int, 4> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGBA32I,\r\n\t\t\tformat = GL_RGBA_INTEGER,\r\n\t\t\tint_internal_format = GL_RGBA32I,\r\n\t\t\tint_format = GL_RGBA_INTEGER,\r\n\t\t\tisdepth = 0\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<float,1> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_R32F,\r\n\t\t\tformat = GL_RED,\r\n\t\t\tint_internal_format = -1,\r\n\t\t\tint_format = -1,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<float, 2> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RG32F,\r\n\t\t\tformat = GL_RG,\r\n\t\t\tint_internal_format = -1,\r\n\t\t\tint_format = -1,\r\n\t\t\tisdepth = 0\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<float,3> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGB32F,\r\n\t\t\tformat = GL_RGB,\r\n\t\t\tint_internal_format = -1,\r\n\t\t\tint_format = -1,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a C type and number of components. */\r\n\ttemplate <> class GLFormat<float,4> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GL_RGBA32F,\r\n\t\t\tformat = GL_RGBA,\r\n\t\t\tint_internal_format = -1,\r\n\t\t\tint_format = -1,\r\n\t\t\tisdepth = 0\r\n\t};\r\n\t};\r\n\r\n\t// Depth texture format (unsupported)\r\n\r\n\t//template <> class GLFormat<depth32,1> {\r\n\t//public:\r\n\t//\tenum {\r\n\t//\t\tinternal_format     = GL_DEPTH_COMPONENT32F,\r\n\t//\t\tformat              = GL_DEPTH_COMPONENT,\r\n\t//\t\tint_internal_format = -1,\r\n\t//\t\tint_format          = -1,\r\n\t//\t\tisdepth             =  1};\r\n\t//};\r\n\r\n\t// --- MAT HELPERS -----------------------\r\n\r\n\t/** Helper building the correspondence between a GL format and a cv::Mat */\r\n\ttemplate <typename T_Type, int T_Num> class GLFormatCVmat;\r\n\r\n\t/** Helper building the correspondence between a GL format and a cv::Mat */\r\n\ttemplate <> class GLFormatCVmat<unsigned char, 1> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GLFormat<uchar, 1>::internal_format,\r\n\t\t\tformat = GLFormat<uchar, 1>::format,\r\n\t\t\tint_internal_format = GLFormat<uchar, 1>::int_internal_format,\r\n\t\t\tint_format = GLFormat<uchar, 1>::int_format,\r\n\t\t\tisdepth = GLFormat<uchar, 1>::isdepth\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a cv::Mat */\r\n\ttemplate <> class GLFormatCVmat<unsigned char, 3> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GLFormat<uchar,3>::internal_format,\r\n\t\t\tformat = GL_BGR,\r\n\t\t\tint_internal_format = GLFormat<uchar, 3>::int_internal_format,\r\n\t\t\tint_format = GLFormat<uchar, 3>::int_format,\r\n\t\t\tisdepth = GLFormat<uchar, 3>::isdepth\r\n\t\t};\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL format and a cv::Mat */\r\n\ttemplate <> class GLFormatCVmat<unsigned char, 4> {\r\n\tpublic:\r\n\t\tenum {\r\n\t\t\tinternal_format = GLFormat<uchar, 4>::internal_format,\r\n\t\t\tformat = GL_BGRA,\r\n\t\t\tint_internal_format = GLFormat<uchar, 4>::int_internal_format,\r\n\t\t\tint_format = GLFormat<uchar, 4>::int_format,\r\n\t\t\tisdepth = GLFormat<uchar, 4>::isdepth\r\n\t\t};\r\n\t};\r\n\t\r\n\t/** Helper building the correspondence between a GL type and a cv::Mat depth. */\r\n\ttemplate<typename T> struct OpenCVdepth;\r\n\r\n\t/** Helper building the correspondence between a GL type and a cv::Mat depth. */\r\n\ttemplate<> struct OpenCVdepth<uchar> {\r\n\t\tstatic const uint value = CV_8U;\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL type and a cv::Mat depth. */\r\n\ttemplate<> struct OpenCVdepth<float> {\r\n\t\tstatic const uint value = CV_32F;\r\n\t};\r\n\r\n\t/** Helper building the correspondence between a GL type and a cv::Mat depth. */\r\n\ttemplate<> struct OpenCVdepth<double> {\r\n\t\tstatic const uint value = CV_64F;\r\n\t};\r\n\r\n\t/** Helper to create a cv::Mat type from its depth and number of components. */\r\n\ttemplate<typename T, uint N> constexpr uint getOpenCVtype = CV_MAKE_TYPE(OpenCVdepth<T>::value, N);\r\n\t\r\n\t/** Helper to create a one-channel cv::Mat from its depth. */\r\n\ttemplate<typename T> constexpr uint getOpenCVtypeSingleChannel = getOpenCVtype<T, 1>;\r\n\t\r\n\t/** Helper class to specify for which image type we can find a valid texture format. */\r\n\ttemplate<typename ImageType> struct ValidGLTexFormat {\r\n\t\tstatic const bool value = false;\r\n\t};\r\n\r\n\t/** Helper class to specify for which image type we can find a valid texture format. */\r\n\ttemplate<typename ScalarType, uint N> struct ValidGLTexFormat<sibr::Image<ScalarType, N>> {\r\n\t\tstatic const bool value = true;\r\n\t};\r\n\r\n\t/** Helper class to specify for which image type we can find a valid texture format. */\r\n\ttemplate<> struct ValidGLTexFormat<cv::Mat> {\r\n\t\tstatic const bool value = true;\r\n\t};\r\n\r\n\t/** Helper class to provide, from a generic image type, all the information needed for OpenGL textures\r\n\t\tRight now it can work with all sibr::Image and with cv::Mat (3U8 only)\r\n\t\tYou can add more using explicit template instanciation to specify both\r\n\t\tValidGLTexFormat and the following GLTexFormat properties.\r\n\t\t*/\r\n\ttemplate<typename ImageType, typename ScalarType = typename ImageType::Type, uint N = ImageType::e_NumComp> struct GLTexFormat {\r\n\t\tstatic_assert(ValidGLTexFormat<ImageType>::value, \"ImageWrapper currently only specialized for sibr::Image and cv::Mat \");\r\n\r\n\t\t/** Flip an image.\r\n\t\t\\param img image to flip\r\n\t\t\\return the fliped image.\r\n\t\t*/\r\n\t\tstatic ImageType flip(const ImageType& img);\r\n\r\n\t\t/** Resize an image.\r\n\t\t\\param img image to resize\r\n\t\t\\param w new width\r\n\t\t\\param h new height\r\n\t\t\\return the resize image.\r\n\t\t*/\r\n\t\tstatic ImageType resize(const ImageType& img, uint w, uint h);\r\n\r\n\t\t/** Get an image width.\r\n\t\t\\param img the image\r\n\t\t\\return the width\r\n\t\t*/\r\n\t\tstatic uint width(const ImageType& img);\r\n\r\n\t\t/** Get an image height.\r\n\t\t\\param img the image\r\n\t\t\\return the height\r\n\t\t*/\r\n\t\tstatic uint height(const ImageType& img);\r\n\r\n\t\t/** Get an image data.\r\n\t\t\\param img the image\r\n\t\t\\return pointer to the beginning of the data.\r\n\t\t*/\r\n\t\tstatic const void* data(const ImageType& img);\r\n\r\n\t\tstatic const uint internal_format;\t\t///< Internal GL format.\r\n\t\tstatic const uint format;\t\t\t\t///< Generic GL format.\r\n\t\tstatic const uint int_internal_format;\t///< Internal GL format for integer textures.\r\n\t\tstatic const uint int_format;\t\t\t///< Generic GL format for integer textures.\r\n\t\tstatic const uint isdepth;\t\t\t\t///< Is it a depth format.\r\n\t\tstatic const uint type;\t\t\t\t\t ///< The component GL type.\r\n\t};\r\n\r\n\t/** Helper class to provide, for an sibr::Image, all the information needed for OpenGL textures. */\r\n\ttemplate<typename ScalarType, uint N > struct GLTexFormat<sibr::Image<ScalarType, N>, ScalarType, N > {\r\n\t\tusing ImageType = sibr::Image<ScalarType, N>;\r\n\r\n\t\t/** \\copydoc GLTexFormat::flip */\r\n\t\tstatic ImageType flip(const ImageType& img) {\r\n\t\t\tImageType temp = img.clone();\r\n\t\t\ttemp.flipH();\r\n\t\t\treturn temp;\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::resize */\r\n\t\tstatic ImageType resize(const ImageType& img, uint w, uint h) {\r\n\t\t\treturn img.resized(w, h);\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::width */\r\n\t\tstatic uint width(const ImageType& img) {\r\n\t\t\treturn img.w();\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::height */\r\n\t\tstatic uint height(const ImageType& img) {\r\n\t\t\treturn img.h();\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::data */\r\n\t\tstatic const void* data(const ImageType& img) {\r\n\t\t\treturn img.data();\r\n\t\t}\r\n\r\n\t\tstatic const uint internal_format = GLFormat<ScalarType, N>::internal_format; ///< Internal GL format.\r\n\t\tstatic const uint format = GLFormat<ScalarType, N>::format;  ///< Generic GL format.\r\n\t\tstatic const uint int_internal_format = GLFormat<ScalarType, N>::int_internal_format; ///< Internal GL format for integer textures.\r\n\t\tstatic const uint int_format = GLFormat<ScalarType, N>::int_format;  ///< Generic GL format for integer textures.\r\n\t\tstatic const uint isdepth = GLFormat<ScalarType, N>::isdepth; ///< Is it a depth format.\r\n\t\tstatic const uint type = GLType<ScalarType>::type;  ///< The component GL type.\r\n\t};\r\n\r\n\t/** Helper class to provide, for an sibr::Image::Ptr, all the information needed for OpenGL textures. */\r\n\ttemplate<typename ScalarType, uint N > struct GLTexFormat<ImagePtr<ScalarType, N>, ScalarType, N > {\r\n\t\tusing ImageType = ImagePtr<ScalarType, N>;\r\n\r\n\t\t/** \\copydoc GLTexFormat::flip */\r\n\t\tstatic ImageType flip(const ImageType& img) {\r\n\t\t\tImageType temp = ImageType::fromImg(*img);\r\n\t\t\ttemp->flipH();\r\n\t\t\treturn temp;\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::resize */\r\n\t\tstatic ImageType resize(const ImageType& img, uint w, uint h) {\r\n\t\t\treturn ImageType::fromImg(img->resized(w, h));\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::width */\r\n\t\tstatic uint width(const ImageType& img) {\r\n\t\t\treturn img->w();\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::height */\r\n\t\tstatic uint height(const ImageType& img) {\r\n\t\t\treturn img->h();\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::data */\r\n\t\tstatic const void* data(const ImageType& img) {\r\n\t\t\treturn img->data();\r\n\t\t}\r\n\r\n\t\tstatic const uint internal_format = GLFormat<ScalarType, N>::internal_format; ///< Internal GL format.\r\n\t\tstatic const uint format = GLFormat<ScalarType, N>::format;  ///< Generic GL format.\r\n\t\tstatic const uint int_internal_format = GLFormat<ScalarType, N>::int_internal_format; ///< Internal GL format for integer textures.\r\n\t\tstatic const uint int_format = GLFormat<ScalarType, N>::int_format;  ///< Generic GL format for integer textures.\r\n\t\tstatic const uint isdepth = GLFormat<ScalarType, N>::isdepth; ///< Is it a depth format.\r\n\t\tstatic const uint type = GLType<ScalarType>::type;  ///< The component GL type.\r\n\r\n\t};\r\n\r\n\t/** Helper class to provide, for a cv::Mat, all the information needed for OpenGL textures. */\r\n\ttemplate<typename ScalarType, uint N > struct GLTexFormat<cv::Mat, ScalarType, N> {\r\n\t\tstatic_assert(std::is_same_v<ScalarType, uchar> && (N == 3 || N == 4 || N == 1) , \"GLTexFormat with cv::Mat currently only defined for 3U8 or 4U8\");\r\n\r\n\t\t/** \\copydoc GLTexFormat::flip */\r\n\t\tstatic cv::Mat flip(const cv::Mat& img) {\r\n\t\t\tcv::Mat temp;\r\n\t\t\tcv::flip(img, temp, 0); //0 for flipH\r\n\t\t\treturn temp;\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::resize */\r\n\t\tstatic cv::Mat resize(const cv::Mat& img, uint w, uint h) {\r\n\t\t\tcv::Mat temp;\r\n\t\t\tcv::resize(img, temp, cv::Size(w, h));\r\n\t\t\treturn temp;\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::width */\r\n\t\tstatic uint width(const cv::Mat& img) {\r\n\t\t\treturn img.cols;\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::height */\r\n\t\tstatic uint height(const cv::Mat& img) {\r\n\t\t\treturn img.rows;\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::data */\r\n\t\tstatic const void* data(const cv::Mat& img) {\r\n\t\t\treturn img.ptr();\r\n\t\t}\r\n\r\n\t\t/** \\copydoc GLTexFormat::data */\r\n\t\tstatic void* data(cv::Mat& img) {\r\n\t\t\treturn img.ptr();\r\n\t\t}\r\n\r\n\t\t/** \\return the matrix OpenCV type. */\r\n\t\tstatic uint cv_type() {\r\n\t\t\treturn CV_MAKE_TYPE(cv::DataDepth<ScalarType>::value, N);\r\n\t\t}\r\n\r\n\t\tstatic const uint internal_format = GLFormatCVmat<ScalarType, N>::internal_format; ///< Internal GL format.\r\n\t\tstatic const uint format = GLFormatCVmat<ScalarType, N>::format;  ///< Generic GL format.\r\n\t\tstatic const uint int_internal_format = GLFormatCVmat<ScalarType, N>::int_internal_format; ///< Internal GL format for integer textures.\r\n\t\tstatic const uint int_format = GLFormatCVmat<ScalarType, N>::int_format;  ///< Generic GL format for integer textures.\r\n\t\tstatic const uint isdepth = GLFormatCVmat<ScalarType, N>::isdepth; ///< Is it a depth format.\r\n\t\tstatic const uint type = GLType<ScalarType>::type;  ///< The component GL type.\r\n\t};\r\n\r\n\t/** Helper class to provide, for a cv::Mat, all the information needed for OpenGL textures. */\r\n\ttemplate<typename ScalarType, uint N >\r\n\tstruct GLTexFormat<cv::Mat_<cv::Vec<ScalarType, N> >, ScalarType, N>\r\n\t\t: GLTexFormat<cv::Mat, ScalarType, N>\r\n\t{\r\n\t};\r\n\r\n\t/*** @} */\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Utils.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/graphics/Utils.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\r\n\r\n\tcv::Scalar jetColor(float gray) {\r\n\t\tconst sibr::Vector3ub col = jetColor<uchar>(gray);\r\n\t\treturn toOpenCV<uchar, uchar, 3>(col);\r\n\t}\r\n\r\n\tsibr::Vector3ub getLinearColorFromProbaV(double proba) {\r\n\t\tconst double scProba = 3.0 * proba;\r\n\t\tconst unsigned char red = double(sibr::clamp(scProba, 0.0, 1.0)) * 255;\r\n\t\tconst unsigned char green = double(sibr::clamp(scProba - 1, 0.0, 1.0)) * 255;\r\n\t\tconst unsigned char blue = double(sibr::clamp(scProba - 2, 0.0, 1.0)) * 255;\r\n\r\n\t\treturn sibr::Vector3ub(red, green, blue);\r\n\t}\r\n\r\n\tdouble getProbaFromLinearColor(const sibr::Vector3ub & color) {\r\n\t\tconst double red = double(color[0]) / 255.0;\r\n\t\tconst double green = double(color[1]) / 255.0;\r\n\t\tconst double blue = double(color[2]) / 255.0;\r\n\t\treturn (red + green + blue) / 3.0;\r\n\t}\r\n\r\n\tsibr::Vector2d cartesianToSpherical(const sibr::Vector3d & dir)\r\n\t{\r\n\t\tdouble theta = std::acos(dir.z());\r\n\r\n\t\tdouble phi = 0;\r\n\t\tif (dir.x() != 0 && dir.y() != 0) {\r\n\t\t\tphi = std::atan2(dir.y(), dir.x());\r\n\t\t}\r\n\r\n\t\treturn sibr::Vector2d(phi, theta);\r\n\t}\r\n\r\n\tsibr::Vector2d cartesianToSphericalUVs(const sibr::Vector3d & dir)\r\n\t{\r\n\t\tconst sibr::Vector2d angles = cartesianToSpherical(dir);\r\n\t\tconst double & phi = angles[0];\r\n\t\tconst double & theta = angles[1];\r\n\r\n\t\treturn sibr::Vector2d(0.5*(phi / M_PI + 1.0), theta / M_PI);\r\n\t}\r\n\r\n\tfloat sRGB2LinF(float inF) {\r\n\t\tif (inF < 0.04045f) {\r\n\t\t\treturn inF / 12.92f;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn std::pow((inF + 0.055f) / (1.055f), 2.4f);\r\n\t\t}\r\n\t}\r\n\r\n\tfloat lin2sRGBF(float inF) {\r\n\r\n\t\tif (inF < 0.0031308f) {\r\n\t\t\treturn std::max(0.0f, std::min(1.0f, 12.92f*inF));\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn std::max(0.0f, std::min(1.0f, 1.055f*std::pow(inF, 1.0f / 2.4f) - 0.055f));\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tvoid sRGB2Lin(sibr::ImageRGB32F& img) {\r\n#pragma omp parallel for\r\n\t\tfor (int j = 0; j < int(img.h()); j++) {\r\n\t\t\tfor (int i = 0; i < int(img.w()); i++) {\r\n\t\t\t\tfor (int c = 0; c < 3; c++) {\r\n\t\t\t\t\timg(i, j)[c] = sRGB2LinF(img(i, j)[c]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid lin2sRGB(sibr::ImageRGB32F& img) {\r\n#pragma omp parallel for\r\n\t\tfor (int j = 0; j < int(img.h()); j++) {\r\n\t\t\tfor (int i = 0; i < int(img.w()); i++) {\r\n\t\t\t\tfor (int c = 0; c < 3; c++) {\r\n\t\t\t\t\timg(i, j)[c] = lin2sRGBF(img(i, j)[c]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Utils.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/graphics/Image.hpp\"\n\n\nnamespace sibr\n{\n\t/**\n\t* \\addtogroup sibr_graphics\n\t* @{\n\t*/\n\n\t/** Generate a random color.\n\t\\return a random RGB triplet\n\t*/\n\ttemplate<typename T_Type>\n\tstatic Eigen::Matrix<T_Type, 3, 1, Eigen::DontAlign> randomColor(){\n\t\t// We just use rand here, we don't need 'proper' PRNG.\n\t\tconst uint8_t r = uint8((std::rand() % 255 + 192) * 0.5f);\n\t\tconst uint8_t g = uint8((std::rand() % 255 + 192) * 0.5f);\n\t\tconst uint8_t b = uint8((std::rand() % 255 + 192) * 0.5f);\n\t\tconst sibr::Vector3ub output(r, g,b);\n\t\treturn output.unaryExpr([](float f) { return f * sibr::opencv::imageTypeRange<T_Type>(); }).template cast<T_Type>();\n\t}\n\n\t/** Generate a color for a given scalar score, using the jet color map.\n\t\\param gray the probability value\n\t\\return the associated jet color.\n\t*/\n\ttemplate<typename T_Type>\n\tstatic Eigen::Matrix<T_Type, 3, 1, Eigen::DontAlign> jetColor(float gray)\n\t{\n\t\tsibr::Vector3f output(1, 1, 1);\n\t\tfloat g = std::min(1.0f, std::max(0.0f, gray));\n\t\tfloat dg = 0.25f;\n\t\tfloat d = 4.0f;\n\t\tif (g < dg) {\n\t\t\toutput.x() = 0.0f; \n\t\t\toutput.y() = d*g;\n\t\t} else if (g < 2.0f*dg) {\n\t\t\toutput.x() = 0.0f; \n\t\t\toutput.z() = 1.0f + d*(dg - g);\n\t\t} else if (g < 3.0f*dg) {\n\t\t\toutput.x() = d*(g - 0.5f); \n\t\t\toutput.z() = 0.0f;\n\t\t} else {\n\t\t\toutput.y() = 1.0f + d*(0.75f - g);  \n\t\t\toutput.z() = 0.0f;\n\t\t}\n\n\t\treturn output.unaryExpr([](float f) { return f * sibr::opencv::imageTypeRange<T_Type>(); }).template cast<T_Type>();\n\t}\n\n\t/** Generate a jet color associated to the input probability, as a 3-channels cv::Scalar.\n\t\\param gray the probability value\n\t\\return the associated jet color.\n\t*/\n\tSIBR_GRAPHICS_EXPORT cv::Scalar jetColor(float gray);\n\n\t/** Generate a color for a given scalar score, using a reversible mapping.\n\t\\param proba the probability value\n\t\\return the associated color\n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::Vector3ub getLinearColorFromProbaV(double proba);\n\n\t/** Convert a color to the associated scalar score, using a reversible mapping.\n\t\\param color the color\n\t\\return the probability value\n\t*/\n\tSIBR_GRAPHICS_EXPORT double getProbaFromLinearColor(const sibr::Vector3ub & color);\n\n\t/** Convert a direction from cartesian to spherical coordinates.\n\t\\param dir a direction in cartesian 3D space\n\t\\return the spherical coordinates [phi,theta] in [-pi,pi]x[0,pi]\n\t\\warning dir is assumed to be normalized\n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::Vector2d cartesianToSpherical(const sibr::Vector3d & dir); \n\n\t/** Convert a direction from cartesian to spherical UVs.\n\t\\param dir a direction in cartesian 3D space\n\t\\return the spherical UVs [u,v] in [0,1]^2\n\t\\warning dir is assumed to be normalized\n\t*/\n\tSIBR_GRAPHICS_EXPORT sibr::Vector2d cartesianToSphericalUVs(const sibr::Vector3d & dir);\n\n\t/**Inplace conversion of float image from sRGB space to linear.\n\t\\param img the image to convert\n\t*/\n\tSIBR_GRAPHICS_EXPORT void sRGB2Lin(sibr::ImageRGB32F& img);\n\n\t/** Inplace conversion of a float image from linear space to sRGB.\n\t\\param img the image to convert\n\t*/\n\tSIBR_GRAPHICS_EXPORT void lin2sRGB(sibr::ImageRGB32F& img);\n\n\t/** Debug helper: wrap a rendering task in an openGL debug group (visible in Renderdoc).\n\t\\param s debug group name\n\t\\param f the task to wrap\n\t\\param args the task arguments\n\t*/\n\ttemplate<typename FunType, typename ...ArgsType>\n\tvoid renderTask(const std::string & s, FunType && f, ArgsType && ... args) {\n\t\tglPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, s.c_str());\n\t\tf(args...);\n\t\tglPopDebugGroup();\n\t};\n\n\t/** Interpolate between two values.\n\t\\param A first value\n\t\\param B second value\n\t\\param fac interpolation factor\n\t\\return A+fac*(B-A)\n\t*/\n\tinline float lerp( float A, float B, float fac ) {\n\t\treturn A*(1.f-fac)+B*fac;\n\t}\n\n\t/** Express a value as the linear combination of two other values.\n\t\\param from first value\n\t\\param to second value\n\t\\param current value to express as a combination\n\t\\return the interpolation factor\n\t*/\n\tinline float inverseLerp( float from, float to, float current ) {\n\t\treturn (current - from)/(to - from);\n\t}\n\n\t/*** @} */\n\n} // namespace sibr\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Viewport.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/graphics/Window.hpp\"\r\n#include \"core/graphics/Viewport.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\tvoid\t\t\tViewport::bind( uint screenWidth, uint screenHeight ) const\r\n\t{\r\n\t\tglViewport(\r\n\t\t\t(GLint)(left()*screenWidth), (GLint)(top()*screenHeight),\r\n\t\t\t(GLsizei)(width()*screenWidth), (GLsizei)(height()*screenHeight));\r\n\t}\r\n\r\n\tvoid\t\t\tViewport::clear( const Vector3f& bgColor ) const\r\n\t{\r\n\t\t//if (width() < 1.f)\r\n\t\t//\treturn;\r\n\r\n\t\tGLint l = (GLint)finalLeft();\r\n\t\tGLint t = (GLint)finalTop();\r\n\t\tGLsizei w = (GLsizei)finalWidth();\r\n\t\tGLsizei h = (GLsizei)finalHeight();\r\n\r\n\t\tglViewport(l, t, w, h);\r\n\t\tglScissor(l, t, w, h);\r\n\t\tglEnable(GL_SCISSOR_TEST);\r\n\t\tglClearColor(bgColor[0], bgColor[1], bgColor[2], 0.f);\r\n\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r\n\t\tglDisable(GL_SCISSOR_TEST);\r\n\t\t\r\n\t\tglViewport(l, t, w, h);\r\n\t}\r\n\r\n\tvoid\t\t\tViewport::bind( void ) const\r\n\t{\r\n\t\t//assert((_parent != nullptr || width() > 1.f) \r\n\t\t//\t&& \"Too small viewport detected (Set a parent viewport from a window using Viewport::parent(...) and Window::viewport()\");\r\n\r\n\t\tglViewport(\r\n\t\t\t(GLint)(finalLeft()), (GLint)(finalTop()),\r\n\t\t\t(GLsizei)(finalWidth()), (GLsizei)(finalHeight()));\r\n\t}\r\n\t\r\n\tbool\tViewport::contains( float x, float y ) const\r\n\t{\r\n\t\treturn (x > finalLeft() && x < finalRight() && y > finalTop() && y < finalBottom());\r\n\t}\r\n\t\r\n\tbool\tViewport::contains( int x, int y ) const\r\n\t{\r\n\t\treturn (x > (int)finalLeft() && x < (int)finalRight() && y > (int)finalTop() && y < (int)finalBottom());\r\n\t}\r\n\r\n\tbool Viewport::contains(const Vector2f & xy) const\r\n\t{\r\n\t\treturn contains(xy.x(), xy.y());\r\n\t}\r\n\r\n\tbool Viewport::isEmpty() const {\r\n\t\treturn width() == 0.0 && height() == 0.0;\r\n\t}\r\n\r\n\tVector2f Viewport::pixAt(const Vector2f & uv) const {\r\n\t\treturn uv.cwiseProduct(finalSize()) + finalTopLeft();\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Viewport.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/graphics/Config.hpp\"\n# include \"core/system/Vector.hpp\"\n\nnamespace sibr\n{\n\n\t/** Represent an on-screen viewport using normalized coordinates, which can be nested into another viewport.\n\t* \\ingroup sibr_graphics\n\t*/\n\tclass SIBR_GRAPHICS_EXPORT Viewport\n\t{\n\tpublic:\n\n\t\t/** Default constructor: unit viewport. */\n\t\tViewport( void ):\n\t\t\t_parent(nullptr),\n\t\t\t_left(0.f), _top(0.f), _right(1.f), _bottom(1.f) { }\n\n\t\t/** Constructor from extents.\n\t\t *\\param left left extent\n\t\t *\\param top top extent\n\t\t *\\param right right extent\n\t\t *\\param bottom bottom extent\n\t\t */\n\t\tViewport( float left, float top, float right, float bottom ) :\n\t\t\t_parent(nullptr),\n\t\t\t_left(left), _top(top), _right(right), _bottom(bottom) { }\n\n\t\t/** Constructor from a parent and relative extents.\n\t\t *\\param parent_ the parent viewport\n\t\t *\\param left left relative extent\n\t\t *\\param top top relative extent\n\t\t *\\param right right relative extent\n\t\t *\\param bottom bottom relative extent\n\t\t */\n\t\tViewport( const Viewport* parent_, float left, float top, float right, float bottom ) :\n\t\t\t_left(left), _top(top), _right(right), _bottom(bottom) { parent(parent_); }\n\n\t\t/** Constructor from a parent and relative extents.\n\t\t *\\param parent_ the parent viewport\n\t\t *\\param left left relative extent\n\t\t *\\param top top relative extent\n\t\t *\\param right right relative extent\n\t\t *\\param bottom bottom relative extent\n\t\t */\n\t\tViewport(const Viewport & parent_, float left, float top, float right, float bottom) :\n\t\t\tViewport(&parent_, left, top, right, bottom) {\n\t\t\t*this = Viewport(finalLeft(), finalTop(), finalRight(), finalBottom());\n\t\t}\n\n\t\t/** \\return the relative left extent. */\n\t\tinline float\tleft( void ) const { return _left; }\n\t\t/** \\return the relative top extent. */\n\t\tinline float\ttop( void ) const { return _top; }\n\t\t/** \\return the relative right extent. */\n\t\tinline float\tright( void ) const { return _right; }\n\t\t/** \\return the relative bottom extent. */\n\t\tinline float\tbottom( void ) const { return _bottom; }\n\n\t\t/** \\return the relative viewport width */\n\t\tinline float\twidth( void ) const { return _right-_left; }\n\t\t/** \\return the relative viewport height */\n\t\tinline float\theight( void ) const { return _bottom-_top; }\n\n\t\t/** \\return the absolute left extent. */\n\t\tfloat\tfinalLeft( void ) const;\n\t\t/** \\return the absolute top extent. */\n\t\tfloat\tfinalTop( void ) const;\n\t\t/** \\return the absolute right extent. */\n\t\tfloat\tfinalRight( void ) const;\n\t\t/** \\return the absolute bottom extent. */\n\t\tfloat\tfinalBottom( void ) const;\n\n\t\t/** \\return the absolute viewport width. */\n\t\tfloat\tfinalWidth( void ) const;\n\t\t/** \\return the absolute viewport height. */\n\t\tfloat\tfinalHeight( void ) const;\n\n\t\t/** \\return the absolute viewport size. */\n\t\tsibr::Vector2f finalSize() const;\n\t\t/** \\return the absolute cooridnates of the top left corner. */\n\t\tVector2f finalTopLeft() const;\n\n\n\t\t/** Compute the absolute pixel coordinates based on relative normalized coordinates.\n\t\t *\\param uv the normalized UVs \n\t\t *\\return the pixel coordinates \n\t\t */\n\t\tVector2f pixAt(const Vector2f & uv) const;\n\n\t\t/** Check if a point is inside the viewport.\n\t\t *\\param x horizontal coordinate\n\t\t *\\param y vertical coordinate\n\t\t *\\return true if the point is inside\n\t\t */\n\t\tbool\tcontains( float x, float y ) const;\n\n\t\t/** Check if a point is inside the viewport.\n\t\t *\\param x horizontal coordinate\n\t\t *\\param y vertical coordinate\n\t\t *\\return true if the point is inside\n\t\t */\n\t\tbool\tcontains( int x, int y ) const;\n\n\t\t/** Check if a point is inside the viewport.\n\t\t *\\param xy coordinates\n\t\t *\\return true if the point is inside\n\t\t */\n\t\tbool\tcontains(const Vector2f & xy) const;\n\n\t\t/** Bind an OpenGL viewport whose values are determined based on the viewport final dimensions and the target size.\n\t\t *\\param screenWidth the width of the rendertarget\n\t\t *\\param screenHeight the height of the rendertarget\n\t\t */\n\t\tvoid\t\t\tbind( uint screenWidth, uint screenHeight ) const;\n\n\t\t/** Bind an OpenGL viewport  whose values are determined based on the viewport final dimensions. */\n\t\tvoid\t\t\tbind( void ) const;\n\n\t\t/** Perform a full OpenGL clear of the region defined by the viewport in the currently bound target.\n\t\t *\\param bgColor clear color\n\t\t */\n\t\tvoid\t\t\tclear( const Vector3f& bgColor=Vector3f(0.f, 0.f, 0.f) ) const;\n\n\t\t/** Set the viewport parent\n\t\t *\\param view the new parent\n\t\t */\n\t\tvoid\t\t\t\tparent( const Viewport* view );\n\n\t\t/** \\return the parent viewport if it exists or nullptr. */\n\t\tconst Viewport*\t\tparent( void ) const;\n\n\t\t/** \\return true if the viewport is empty (0x0). */\n\t\tbool isEmpty() const;\n\n\tprivate:\n\t\tconst Viewport*\t_parent; ///< (optional)\n\n\t\tfloat\t_left; ///< Left extent.\n\t\tfloat\t_top; ///< Top extent.\n\t\tfloat\t_right; ///< Right extent.\n\t\tfloat\t_bottom; ///< Bottom extent.\n\n\t};\n\n\t///// DEFINITIONS /////\n\n\tinline void\t\t\t\tViewport::parent( const Viewport* view ) { \n\t\t_parent = view; \n\n\t\t//if (_parent == this) // means 'is the root'\n\t\t//\t_parent = nullptr;\n\t}\n\tinline const Viewport*\t\tViewport::parent( void ) const { \n\t\treturn _parent; \n\t}\n\n\tinline float\tViewport::finalLeft( void ) const {\n\t\treturn (_parent)? (_parent->finalLeft() + _parent->finalWidth()*left()) : left();\n\t}\n\n\tinline float\tViewport::finalTop( void ) const {\n\t\treturn (_parent)? ( _parent->finalTop() + _parent->finalHeight()*top() ) : top();\n\t}\n\n\tinline float\tViewport::finalRight( void ) const {\n\t\treturn (_parent)? (_parent->finalLeft() + _parent->finalWidth()*right()) : right();\n\t}\n\n\tinline float\tViewport::finalBottom( void ) const {\n\t\treturn (_parent)? (_parent->finalTop() + _parent->finalHeight()*bottom()) : bottom();\n\t}\n\n\tinline float\tViewport::finalWidth( void ) const {\n\t\treturn (_parent)? _parent->finalWidth()*width() : width();\n\t}\n\n\tinline float\tViewport::finalHeight( void ) const {\n\t\treturn (_parent)? _parent->finalHeight()*height() : height();\n\t}\n\n\tinline sibr::Vector2f\tViewport::finalSize(void) const {\n\t\treturn sibr::Vector2f(finalWidth(),finalHeight());\n\t}\n\n\tinline Vector2f Viewport::finalTopLeft() const {\n\t\treturn { finalLeft(), finalTop() };\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Window.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/graphics/Input.hpp\"\r\n#include \"core/graphics/Window.hpp\"\r\n#include \"core/graphics/RenderUtility.hpp\"\r\n\r\n#include \"imgui/imgui.cpp\" // needed for loading ini settings\r\n#include \"imgui/imgui.h\"\r\n#include \"imgui_impl_glfw_gl3.h\"\r\n\r\n#include <regex>\r\n\r\nnamespace sibr\r\n{\r\n\tint Window::contextId = -1;\r\n\r\n\tstatic void glfwErrorCallback(int error, const char* description)\r\n\t{\r\n\t\tSIBR_ERR << description << std::endl;\r\n\t}\r\n\r\n\tstatic void glErrorCallback(GLenum src, GLenum type, GLuint id, GLenum severity, GLsizei size, const GLchar* str, const void* user) {\r\n\t\t// For now we only log errors, and we ignore severity.\r\n\t\tif(type != GL_DEBUG_TYPE_ERROR) {\r\n\t\t\t//SIBR_LOG << \"[API]\" << \"(\" << src << \",\" << type << \",\" << id << \",\" << severity << \"): \" << std::string(str, size) << std::endl;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tstd::string errStr;\r\n\t\tswitch(src) {\r\n\t\tcase GL_DEBUG_SOURCE_API:\r\n\t\t\terrStr = \"[API] \";\r\n\t\t\tbreak;\r\n\t\tcase GL_DEBUG_SOURCE_SHADER_COMPILER:\r\n\t\t\terrStr = \"[Shader] \";\r\n\t\t\tbreak;\r\n\t\tcase GL_DEBUG_SOURCE_THIRD_PARTY:\r\n\t\t\terrStr = \"[3rd party] \";\r\n\t\t\tbreak;\r\n\t\tcase GL_DEBUG_SOURCE_APPLICATION:\r\n\t\t\terrStr = \"[Application] \";\r\n\t\t\tbreak;\r\n\t\tcase GL_DEBUG_SOURCE_OTHER:\r\n\t\t\terrStr = \"[Other] \";\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tconst std::string errStr2(str, size);\r\n\r\n\t\tSIBR_ERR << \"OpenGL: \" << errStr << errStr2 << std::endl;\r\n\t\r\n\t}\r\n\r\n\tstatic void glfwKeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods)\r\n\t{\r\n\t\tkey = std::max(0, key);\r\n\r\n\t\t// We only pass the key input to our code if the interface isn't currently using it.\r\n\t\tif (!ImGui::GetIO().WantCaptureKeyboard) {\r\n\t\t\tif (action == GLFW_PRESS) {\r\n\t\t\t\tsibr::Input::global().key().press((sibr::Key::Code)key);\r\n\t\t\t} else if (action == GLFW_RELEASE) {\r\n\t\t\t\tsibr::Input::global().key().release((sibr::Key::Code)key);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tsibr::Input::global() = sibr::Input();\r\n\t\t}\r\n\t\tImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);\r\n\t}\r\n\r\n\tstatic void glfwResizeCallback(GLFWwindow* window, int w, int h)\r\n\t{\r\n\t\tvoid* userptr = glfwGetWindowUserPointer(window);\r\n\t\tWindow* win = reinterpret_cast<Window*>(userptr);\r\n\t\t\r\n\t\t// TT : should be the right thing to do, but might break some old stuff\r\n\t\twin->viewport(Viewport(0.f, 0.f, (float)(w), (float)(h)));\r\n\t}\r\n\r\n\tstatic void glfwCursorPosCallback(GLFWwindow* /*window*/, double x, double y)\r\n\t{\r\n\t\t// We  pass the mouse position to our code iff the interface doesn't need it.\r\n\t\tif (!ImGui::GetIO().WantCaptureMouse) {\r\n\t\t\tsibr::Input::global().mousePosition(Vector2i((int)x, (int)y));\r\n\t\t} else {\r\n\t\t\tsibr::Input::global() = sibr::Input();\r\n\t\t}\r\n\t\t\r\n\t}\r\n\r\n\tstatic void glfwMouseButtonCallback(GLFWwindow* window, int button, int action, int mods)\r\n\t{\r\n\t\t// We only pass the mouse input to our code if the interface isn't currently using it.\r\n\t\tif (!ImGui::GetIO().WantCaptureMouse) {\r\n\t\t\tif (action == GLFW_PRESS) {\r\n\t\t\t\tsibr::Input::global().mouseButton().press((sibr::Mouse::Code)button);\r\n\t\t\t} else if (action == GLFW_RELEASE) {\r\n\t\t\t\tsibr::Input::global().mouseButton().release((sibr::Mouse::Code)button);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// We have to pass release events in the case where we pressed while inside our views, and released outside.\r\n\t\t\tif(sibr::Input::global().mouseButton().isActivated((sibr::Mouse::Code)button)) {\r\n\t\t\t\tsibr::Input::global().mouseButton().release((sibr::Mouse::Code)button);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);\r\n\t}\r\n\r\n\tstatic void glfwMouseScrollCallback(GLFWwindow* window, double x, double y)\r\n\t{\r\n\t\tsibr::Input::global().mouseScroll(y);\r\n\t\tImGui_ImplGlfw_ScrollCallback(window, x, y);\r\n\t}\r\n\t///////////////////////////////////////////////////////////////////////////\r\n\r\n\tstatic int windowCounter = 0;\r\n\r\n\t/*static*/ bool\t\t\tWindow::contextIsRunning( void )\r\n\t{\r\n\t\treturn windowCounter > 0;\r\n\t}\r\n\r\n\tWindow::AutoInitializer::AutoInitializer( const WindowArgs & args ) : _useGUI(!args.no_gui && !args.offscreen)\r\n\t{\r\n\t\tif (windowCounter == 0)\r\n\t\t{\r\n\t\t\tSIBR_LOG << \"Initialization of GLFW\" << std::endl;\r\n\t\t\tglfwSetErrorCallback(glfwErrorCallback);\r\n\r\n\t\t\tif (!glfwInit())\r\n\t\t\t\tSIBR_ERR << \"cannot init glfw\" << std::endl;\r\n\t\t\tif (!args.offscreen)\r\n\t\t\t\tsibr::Input::global().key().clearStates();\r\n\r\n\t\t}\r\n\t\t++windowCounter;\r\n\t}\r\n\r\n\tWindow::AutoInitializer::~AutoInitializer( void )\r\n\t{\r\n\t\t--windowCounter;\r\n\t\tif (windowCounter == 0)\r\n\t\t{\r\n\t\t\tif(_useGUI) {\r\n\t\t\t\tImGui_ImplGlfwGL3_Shutdown();\t/// \\todo TODO: not sure if safe with multi-context\r\n\t\t\t\tImGui::DestroyContext();\r\n\t\t\t}\r\n\t\t\tglfwSetErrorCallback(nullptr);\r\n\t\t\tSIBR_LOG << \"Deinitialization of GLFW\" << std::endl;\r\n\t\t\tglfwTerminate();\r\n\t\t}\r\n\t}\r\n\r\n\tWindow::Window(uint w, uint h, const std::string& title, const WindowArgs & args, const std::string& defaultSettingsFilename) \r\n\t\t: _hiddenInit(args), _useGUI(!args.no_gui && !args.offscreen), _shouldClose(false) \r\n\t{\r\n\t\t\r\n\t\tsetup(w, h, title, args, defaultSettingsFilename);\r\n\r\n\t\tif (!(args.fullscreen)) {\r\n\t\t\tglfwSetWindowPos(_glfwWin.get(), 200, 200);\r\n\t\t}\r\n\t}\r\n\r\n\tWindow::Window(const std::string& title, const WindowArgs & args, const std::string& defaultSettingsFilename)\r\n\t\t: Window(args.win_width, args.win_height, title, args, defaultSettingsFilename)\r\n\t{\r\n\t}\r\n\r\n\tWindow::Window(const std::string& title, const sibr::Vector2i & margins, const WindowArgs & args, const std::string& defaultSettingsFilename)\r\n\t\t: _hiddenInit(args), _useGUI(!args.no_gui && !args.offscreen), _shouldClose(false)\r\n\t{\r\n\t\tsibr::Vector2i winSize;\r\n\t\tif (args.offscreen) {\r\n\t\t\twinSize = sibr::Vector2i(args.win_width, args.win_height);\r\n\t\t}\r\n\t\telse {\r\n\t\t\twinSize = desktopSize();\r\n\t\t}\r\n\t\t\r\n\t\t// Here autoInitializer is already initialized, thus glfwInit() has been called\r\n\t\tsetup(winSize.x() - 2*margins.x(), winSize.y() - 2*margins.y(), title, args, defaultSettingsFilename);\r\n\r\n\t\tif (!(args.fullscreen)) {\r\n\t\t\tglfwSetWindowPos(_glfwWin.get(), margins.x(), margins.y());\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tvoid Window::swapBuffer(void) {\r\n\t\tif (_useGUI) {\r\n\t\t\tglPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, \"ImGui interface\");\r\n\t\t\tImGui::Render();\r\n\t\t\tImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());\r\n\t\t\tglPopDebugGroup();\r\n\t\t}\r\n\t\tglfwSwapBuffers(_glfwWin.get());\r\n\t\t// Keep the call below in all cases to avoid accumulating all interfaces in one frame.\r\n\t\tif (_useGUI)\r\n\t\t\tImGui_ImplGlfwGL3_NewFrame();\r\n\t}\r\n\t\r\n\tvoid Window::resetSettingsToDefault() {\r\n\t\tstd::string iniFilename = ImGui::GetIO().IniFilename;\r\n\t\tif(iniFilename != \"\" && fileExists(iniFilename)) {\r\n\t\t\tif(remove(iniFilename.c_str()))\r\n\t\t\t\tSIBR_WRG << \"Settings file \" << iniFilename << \" was not removed due to an error.\" << std::endl;\r\n\t\t\telse\r\n\t\t\t\tSIBR_LOG << \"Settings file \" << iniFilename << \" was removed successfully.\" << std::endl;\r\n\t\t}\r\n\t\telse {\r\n\t\t\tSIBR_WRG << \"Settings file \" << iniFilename << \" not found.\" << std::endl;\r\n\t\t}\r\n\r\n\t\tImGuiContext& g = *ImGui::GetCurrentContext();\r\n\r\n\t\tfor (int i = 0; i < g.SettingsWindows.Size; i++)\r\n\t\t\t\tIM_DELETE(g.SettingsWindows[i].Name);\r\n\r\n\t\tg.SettingsWindows.clear();\r\n\r\n\t\tloadSettings();\r\n\r\n\t\tfor (ImGuiWindow * window: g.Windows) {\r\n\t\t\tif (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) {\r\n\t\t\t\tSetWindowPos(window, settings->Pos, ImGuiCond_Always);\r\n\t\t\t\tSetWindowSize(window, settings->Size, ImGuiCond_Always);\r\n\t\t\t\tSetWindowCollapsed(window, settings->Collapsed, ImGuiCond_Always);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid Window::loadSettings() {\r\n\t\t// Load defaults from core\r\n\t\tLoadIniSettingsFromDisk(std::string(getResourcesDirectory() + \"/core/\" + _defaultImguiSettingsFilename).c_str());\r\n\t\t\r\n\t\t// Load defaults from Window constructor\r\n\t\tif(fileExists(_windowImguiSettingsFilename))\r\n\t\t\tLoadIniSettingsFromDisk(_windowImguiSettingsFilename.c_str());\r\n\r\n\t\t// Load user specific settings for this particular window\r\n\t\tLoadIniSettingsFromDisk(ImGui::GetIO().IniFilename);\r\n\t}\r\n\r\n\tvoid Window::setup(int width, int height, const std::string& title, const WindowArgs & args, const std::string& defaultSettingsFilename) {\r\n\t\t// IMPORTANT NOTE: if you got compatibility problem with old opengl function,\r\n\t\t// try to load compat 3.2 instead of core 4.2\r\n\r\n\t\tglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);\r\n\t\tglfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);\r\n\t\tglfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);\r\n\r\n#ifdef GLEW_EGL\r\n\t\tglfwWindowHint(GLFW_CONTEXT_CREATION_API, (args.offscreen) ?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tGLFW_EGL_CONTEXT_API :\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tGLFW_NATIVE_CONTEXT_API);\r\n#else\r\n\t\tif(args.offscreen) SIBR_WRG << \"Offscreen enabled without EGL implementation. Using native context (Offscreen might run into issues if no real display is available).\" << std::endl;\r\n#endif\r\n\r\n\t\tglfwWindowHint(GLFW_RED_BITS, 8);\r\n\t\tglfwWindowHint(GLFW_GREEN_BITS, 8);\r\n\t\tglfwWindowHint(GLFW_BLUE_BITS, 8);\r\n\t\tglfwWindowHint(GLFW_ALPHA_BITS, 8);\r\n\t\tglfwWindowHint(GLFW_DEPTH_BITS, 24);\r\n\t\tglfwWindowHint(GLFW_STENCIL_BITS, 8);\r\n\r\n\t\tif (args.offscreen) {\r\n\t\t\tglfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);\r\n\t\t}\r\n\r\n\t\t_glfwWin = GLFWwindowptr(\r\n\t\t\tglfwCreateWindow(\r\n\t\t\t\twidth, height, title.c_str(),\r\n\t\t\t\t(args.fullscreen && !args.offscreen) ? glfwGetPrimaryMonitor() : NULL\r\n\t\t\t\t, NULL ), \r\n\t\t\tglfwDestroyWindow\r\n\t\t);\r\n\r\n\t\tif (_glfwWin == nullptr)\r\n\t\t\tSIBR_ERR << \"failed to create a glfw window (is your graphics driver updated ?)\" << std::endl;\r\n\r\n\t\tmakeContextCurrent();\r\n\r\n\t\t\r\n\r\n\t\t//SR, TT fix for image size non divisible by 4\r\n\t\tglPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r\n\t\tglPixelStorei(GL_PACK_ALIGNMENT, 1);\r\n\r\n\t\t//// Print available OpenGL version\r\n\t\tGLint major, minor;\r\n\t\tglGetIntegerv(GL_MAJOR_VERSION, &major);\r\n\t\tglGetIntegerv(GL_MINOR_VERSION, &minor);\r\n\t\tSIBR_LOG << \"OpenGL Version: \" << glGetString(GL_VERSION)\r\n\t\t\t<< \"[major: \" << major << \", minor: \" << minor << \"]\" << std::endl;\r\n\r\n\t\tglewExperimental = GL_TRUE;\r\n\t\tGLenum err = glewInit();\r\n#ifdef GLEW_EGL\r\n//\t\tif (err != GLEW_OK && (!args.offscreen || err != GLEW_ERROR_NO_GLX_DISPLAY)) // Small hack for glew, this error occurs but does not concern offscreen\r\n\t\tif (err != GLEW_OK && (!args.offscreen )) // Small hack for glew, this error occurs but does not concern offscreen\r\n#else\r\n\t\tif (err != GLEW_OK)\r\n#endif\r\n\t\t\tSIBR_ERR << \"cannot initialize GLEW (used to load OpenGL function)\" << std::endl;\r\n\t\t(void)glGetError(); // I notice that glew might do wrong things during its init()\r\n\t\t\t\t\t\t\t// some drivers complain about it. So I reset OpenGL's errors to discard this.\r\n\r\n\t\tglfwSetWindowUserPointer(_glfwWin.get(), this);\r\n\t\t/// \\todo TODO: fix, width and height might be erroneous. SR\r\n\t\tviewport(Viewport(0.f, 0.f, (float)width, (float)height));\t/// \\todo TODO: bind both\r\n\r\n\t\t_useVSync = args.vsync;\r\n\t\tglfwSwapInterval(args.vsync);\r\n\t\tglfwSetKeyCallback(_glfwWin.get(), glfwKeyboardCallback);\r\n\t\tglfwSetScrollCallback(_glfwWin.get(), glfwMouseScrollCallback);\r\n\t\tglfwSetMouseButtonCallback(_glfwWin.get(), glfwMouseButtonCallback);\r\n\t\tglfwSetCursorPosCallback(_glfwWin.get(), glfwCursorPosCallback);\r\n\t\tglfwSetWindowSizeCallback(_glfwWin.get(), glfwResizeCallback);\r\n\r\n\t\t// SR: we don't use it by default because you won't get callstack/file/line info.\r\n\t\tif(args.gl_debug) {\r\n\t\t\tglEnable(GL_DEBUG_OUTPUT);\r\n\t\t\tglDebugMessageCallback(glErrorCallback, nullptr);\r\n\t\t}\r\n\r\n\t\tif(_useGUI) {\r\n\t\t\t//contextId\r\n\t\t\t++Window::contextId;\r\n\r\n\t\t\t// Setup ImGui binding\r\n\t\t\tImGui::CreateContext();\r\n\t\t\tImGui_ImplGlfwGL3_Init(_glfwWin.get(), false);\r\n\t\t\tglfwSetCharCallback(_glfwWin.get(), ImGui_ImplGlfw_CharCallback);\r\n\r\n\t\t\tImGui_ImplGlfwGL3_NewFrame();\r\n\r\n\t\t\t_windowImguiSettingsFilename = defaultSettingsFilename;\r\n\r\n\t\t\t// Set user specific file for this particular window as default ini file\r\n\t\t\tstd::string iniFilename = std::string(getAppDataDirectory() + \"/\" + std::regex_replace(title, std::regex(\"[^0-9A-Za-z\\\\-_]\"), \"_\") + \".ini\").c_str();\r\n\t\t\tchar* iniFilenameCStr = new char[iniFilename.length()+1];\r\n\t\t\tstrcpy(iniFilenameCStr, iniFilename.c_str());\r\n\t\t\tImGui::GetIO().IniFilename = iniFilenameCStr;\r\n\r\n\t\t\tloadSettings();\r\n\t\t}\r\n\r\n\t\tif(!args.offscreen) {\r\n\t\t\t_oldPosition = position();\r\n\t\t\t_oldSize = size();\r\n\r\n\t\t\t// Support for HiDPI on Windows. The default is 96.\r\n\t\t\t// Compute the pixel density at the current definition.\r\n\t\t\tint widthmm, heightmm;\r\n\t\t\tglfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), &widthmm, &heightmm);\r\n\t\t\tconst float defaultDPI = 96.0f;\r\n\t\t\tsibr::Vector2i dsize = desktopSize();\r\n\t\t\t\r\n\t\t\t_scaling = sibr::clamp(std::round(dsize.x() / (widthmm / 25.4f) / defaultDPI), 1.0f, 2.0f);\r\n\r\n\t\t\tif (_useGUI && args.hdpi) {\r\n\t\t\t\tImGui::GetStyle().ScaleAllSizes(scaling());\r\n\t\t\t\tImGui::GetIO().FontGlobalScale = scaling();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/** \\todo\r\n\t\tTODO: fix issue on some HiDPI screens + interaction with GUI labels generation.\r\n\t\t// If we have a screen in HiDPI mode, scale the interface accordingly.\r\n\t\tif (_scaling > 1.0f) {\r\n\t\t\tImGui::GetStyle().ScaleAllSizes(_scaling);\r\n\t\t\tImGui::GetIO().FontGlobalScale = _scaling;\r\n\t\t}\r\n\t\t*/\r\n\t}\r\n\r\n\tVector2i\t\tWindow::desktopSize( void )\r\n\t{\r\n\t\tconst GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());\r\n\t\treturn Vector2i(mode->width, mode->height);\r\n\t}\r\n\r\n\tVector2i\t\tWindow::size( void ) const\r\n\t{\r\n\t\tVector2i s;\r\n\t\tglfwGetWindowSize(_glfwWin.get(), &s[0], &s[1]);\r\n\t\treturn s;\r\n\t}\r\n\r\n\tvoid Window::position(const unsigned int x, const unsigned int y)\r\n\t{\r\n\t\tglfwSetWindowPos(_glfwWin.get(), x, y);\r\n\t}\r\n\r\n\tVector2i Window::position() const {\r\n\t\tVector2i s;\r\n\t\tglfwGetWindowPos(_glfwWin.get(), &s[0], &s[1]);\r\n\t\treturn s;\r\n\t}\r\n\r\n\tbool\t\t\tWindow::isOpened( void ) const\r\n\t{\r\n\t\treturn (!_shouldClose && !glfwWindowShouldClose(_glfwWin.get()));\r\n\t}\r\n\r\n\tvoid\t\t\tWindow::close( void )\r\n\t{\r\n\t\t_shouldClose = true;\r\n\t\tglfwSetWindowShouldClose(_glfwWin.get(), GL_TRUE);\r\n\t}\r\n\r\n\tbool Window::isFullscreen(void) const\r\n\t{\r\n\t\treturn glfwGetWindowMonitor(_glfwWin.get()) != NULL;\r\n\t}\r\n\r\n\tvoid Window::setFullscreen(const bool fullscreen) {\r\n\t\tconst bool currentState = isFullscreen();\r\n\t\tif((fullscreen && currentState) || (!fullscreen && !currentState)) {\r\n\t\t\t// Do nothing.\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (fullscreen) {\r\n\t\t\t_oldPosition = position();\r\n\t\t\t_oldSize = size();\r\n\t\t\tGLFWmonitor* monitor = glfwGetPrimaryMonitor();\r\n\t\t\tconst GLFWvidmode* mode = glfwGetVideoMode(monitor);\r\n\t\t\tglfwSetWindowMonitor(_glfwWin.get(), monitor, 0, 0, mode->width, mode->height, mode->refreshRate);\r\n\t\t\t// There is a bug in glfw (see https://github.com/glfw/glfw/issues/1072).\r\n\t\t\t// We have to manually re-set the swap interval.\r\n\t\t\tglfwSwapInterval(_useVSync ? 1 : 0);\r\n\t\t} else {\r\n\t\t\tglfwSetWindowMonitor(_glfwWin.get(), NULL, _oldPosition[0], _oldPosition[1], _oldSize[0], _oldSize[1], 0);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid\t\t\tWindow::size( int w, int h )\r\n\t{\r\n\t\tglfwSetWindowSize(_glfwWin.get(), w, h);\r\n\t\tVector2i s = size();\r\n\r\n\t\tif (s[0] != w || s[1] != h)\r\n\t\t\tSIBR_WRG << \"Attempting to resize the window to an unsuported resolution \"\r\n\t\t\t\"(w = \" << w << \", h = \" << h << \" ), using w = \" << s[0] << \", h = \" << s[1] << \" instead.\" << std::endl;\r\n\r\n\t\t// TT : should be the right thing to do, but might brake some old stuff\r\n\t\tviewport(Viewport(0.f, 0.f, (float)(s[0]), (float)(s[1])));\r\n\r\n\t\t//viewport(Viewport(0.f, 0.f, (float)(s[0]-1), (float)(s[1]-1))); // TODO: bind both\r\n\t}\r\n\r\n\tvoid Window::setFrameRate(int fps)\r\n\t{\r\n\t\tif (fps == 60) {\r\n\t\t\tglfwSwapInterval(1);\r\n\t\t} else if (fps == 30) {\r\n\t\t\tglfwSwapInterval(2);\r\n\t\t} else if (fps == 15) {\r\n\t\t\tglfwSwapInterval(3);\r\n\t\t}\r\n\t}\r\n\r\n\tbool Window::isVsynced(void) const\r\n\t{\r\n\t\treturn _useVSync;\r\n\t}\r\n\r\n\tbool Window::isGUIEnabled(void) const\r\n\t{\r\n\t\treturn _useGUI;\r\n\t}\r\n\r\n\tvoid Window::setVsynced(const bool vsync) {\r\n\t\t_useVSync = vsync;\r\n\t\tglfwSwapInterval(_useVSync ? 1 : 0);\r\n\t}\r\n\r\n\tvoid\t\t\t\tWindow::enableCursor( bool enable )\r\n\t{\r\n\t\tglfwSetInputMode(_glfwWin.get(), GLFW_CURSOR, enable? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);\r\n\t}\r\n\r\n\tGLFWwindow * Window::GLFW(void) {\r\n\t\treturn _glfwWin.get();\r\n\t}\r\n\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/Window.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include \"core/graphics/Config.hpp\"\r\n#include \"core/system/Vector.hpp\"\r\n#include \"core/graphics/Viewport.hpp\"\r\n#include \"core/graphics/Texture.hpp\"\r\n#include <core/system/CommandLineArgs.hpp>\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/** System window backed by an internal framebuffer.\r\n\t* \\ingroup sibr_graphics\r\n\t*/\r\n\tclass SIBR_GRAPHICS_EXPORT Window : public IRenderTarget\r\n\t{\r\n\tpublic:\r\n\t\ttypedef std::shared_ptr<Window>\t\tPtr;\r\n\r\n\tpublic:\r\n\t\t/** Constructor.\r\n\t\t *\\param title window title\r\n\t\t *\\param args window setup arguments\r\n\t\t *\\sa WindowArgs\r\n\t\t *\\param defaultSettingsFilename default settings file for this specific window\r\n\t\t **/\r\n\t\tWindow(const std::string& title, const WindowArgs & args = {}, const std::string& defaultSettingsFilename = \"\");\r\n\r\n\t\t/** Constructor, overriding the window size.\r\n\t\t *\\param w window width\r\n\t\t *\\param h window height\r\n\t\t *\\param title window title\r\n\t\t *\\param args window setup arguments\r\n\t\t *\\sa WindowArgs\r\n\t\t *\\param defaultSettingsFilename default settings file for this specific window\r\n\t\t **/\r\n\t\tWindow(uint w, uint h, const std::string& title, const WindowArgs & args = {}, const std::string& defaultSettingsFilename = \"\");\r\n\r\n\t\t/** Constructor, adjust the size to fill the screen except for the margins.\r\n\t\t *\\param title window title\r\n\t\t *\\param margins horizontal and vertical margins to preserve on each side of the window\r\n\t\t *\\param args window setup arguments\r\n\t\t *\\sa WindowArgs\r\n\t\t *\\param defaultSettingsFilename default settings file for this specific window\r\n\t\t **/\r\n\t\tWindow(const std::string & title, const sibr::Vector2i & margins, const WindowArgs & args = {}, const std::string& defaultSettingsFilename = \"\");\r\n\r\n\t\t/** \\return a pointer to the underlying GLFW window */\r\n\t\tGLFWwindow *\t\tGLFW(void);\r\n\r\n\t\t/** Activate the associated graphics context. */\r\n\t\tvoid\t\t\t\tmakeContextCurrent(void);\r\n\t\t/** \\return the context currently in use (represented by a GLFW window) */\r\n\t\tGLFWwindow *\t\tgetContextCurrent(void);\r\n\t\t/** Deactivate the associated graphics context. */\r\n\t\tvoid\t\t\t\tmakeContextNull(void);\r\n\r\n\t\t/** Flush the graphics pipeline and perform rendering, displaying the result in the abck buffer. */\r\n\t\tvoid\t\t\t\tswapBuffer(void);\r\n\r\n\t\t/** Reset window settings to default.\r\n\t\t */\r\n\t\tvoid\t\t\t\tresetSettingsToDefault();\r\n\r\n\t\t/** Load Imgui settings.\r\n\t\t */\r\n\t\tvoid\t\t\t\tloadSettings();\r\n\r\n\t\t/** Set the window size\r\n\t\t *\\param w width\r\n\t\t *\\param h height\r\n\t\t **/\r\n\t\tvoid\t\t\t\tsize(int w, int h);\r\n\t\t\r\n\t\t/** \\return the window size */\r\n\t\tVector2i\t\t\tsize(void) const;\r\n\r\n\t\t/** Set the window position\r\n\t\t *\\param x horizontal location\r\n\t\t *\\param y vertical location\r\n\t\t **/\r\n\t\tvoid\t\t\t\tposition(const unsigned int x, const unsigned int y);\r\n\r\n\t\t/** \\return the window position on screen */\r\n\t\tVector2i\t\t\tposition() const;\r\n\r\n\t\t/** \\return the screen size. */\r\n\t\tstatic Vector2i\t\tdesktopSize(void);\r\n\r\n\t\t/** \\return true if an openGL context is active. */\r\n\t\tstatic bool\t\t\tcontextIsRunning(void);\r\n\r\n\t\t/** Set the framerate.\r\n\t\t *\\param fps one of 60, 30, 15 \r\n\t\t */\r\n\t\tvoid\t\t\t\tsetFrameRate(int fps);\r\n\r\n\t\t/** Display the cursor in the window. \r\n\t\t * \\param enable boolean flag\r\n\t\t */\r\n\t\tvoid\t\t\t\tenableCursor(bool enable);\r\n\r\n\t\t/** \\return if the window is currently opened */\r\n\t\tbool\t\t\t\tisOpened(void) const;\r\n\t\t/** Mark the window as closed. */\r\n\t\tvoid\t\t\t\tclose(void);\r\n\r\n\t\t/** \\return true if the window is fullscreen. */\r\n\t\tbool\t\t\t\tisFullscreen(void) const;\r\n\t\t/** Toggle fullscreen.\r\n\t\t *\\param fullscreen if true the window will be resized to occupy the whole screen, without visible borders.\r\n\t\t */\r\n\t\tvoid\t\t\t\tsetFullscreen(const bool fullscreen);\r\n\r\n\t\t/** \\return true if the window is using V-sync. */\r\n\t\tbool\t\t\t\tisVsynced(void) const;\r\n\r\n\t\t/** \\return true if the window is enabling GUI. */\r\n\t\tbool\t\t\t\tisGUIEnabled(void) const;\r\n\t\t\r\n\t\t/** Toggle V-sync.\r\n\t\t *\\param vsync if true, framerate will be limited to 60 FPS\r\n\t\t *\\note When set to false, tearing might be visible.\r\n\t\t */\r\n\t\tvoid\t\t\t\tsetVsynced(const bool vsync);\r\n\r\n\t\t/** \\return the window viewport */\r\n\t\tconst Viewport&\t\tviewport(void) const;\r\n\r\n\t\t/** Set the window viewport.\r\n\t\t *\\param view the new viewport\r\n\t\t */\r\n\t\tvoid\t\t\t\tviewport(const Viewport& view);\r\n\r\n\t\t// From IRenderTarget\r\n\t\t/** Get the backbuffer texture ID. unsuported. */\r\n\t\tGLuint\t\t\t\ttexture(uint t = 0) const;\r\n\r\n\t\t/** Get the backbuffer texture ID. unsuported. */\r\n\t\tGLuint\t\t\t\thandle(uint t = 0) const;\r\n\r\n\t\t/** \\return the window buffer ID (0) */\r\n\t\tGLuint\t\t\t\tfbo(void) const;\r\n\r\n\t\t/** Bind the window buffer. */\r\n\t\tvoid\t\t\t\tbind(void);\r\n\r\n\t\t/** Unind the window buffer. */\r\n\t\tvoid\t\t\t\tunbind(void);\r\n\r\n\t\t/** Clear the window buffer. */\r\n\t\tvoid\t\t\t\tclear(void);\r\n\r\n\t\t/** \\return the window buffer width */\r\n\t\tuint\t\t\t\tw(void) const;\r\n\r\n\t\t/** \\return the window buffer height */\r\n\t\tuint\t\t\t\th(void) const;\r\n\r\n\t\t/** \\return the screens caling factor. */\r\n\t\tfloat\t\t\t\tscaling() const;\r\n\t\t\r\n\t\tstatic int\t\t\tcontextId; ///< Last created window context ID (-1 initially).\r\n\r\n\tprivate:\r\n\r\n\t\t/** Setup the window.\r\n\t\t *\\param width window width on screen\r\n\t\t *\\param height window height on screen\r\n\t\t *\\param title window title\r\n\t\t *\\param args window setup arguments\r\n\t\t *\\param defaultSettingsFilename default settings file for this specific window\r\n\t\t *\\sa WindowArgs\r\n\t\t */\r\n\t\tvoid setup(int width, int height, const std::string & title, const WindowArgs & args, const std::string& defaultSettingsFilename = \"\");\r\n\r\n\t\t/// Window pointer for callbacks.\r\n\t\ttypedef std::unique_ptr<GLFWwindow, std::function<void(GLFWwindow*)>> GLFWwindowptr;\r\n\r\n\t\t/// Helper to handle window creation/destruction.\r\n\t\tstruct AutoInitializer\r\n\t\t{\r\n\t\t\tAutoInitializer(const WindowArgs & args = {});\r\n\t\t\t~AutoInitializer(void);\r\n\t\t\t\r\n\t\t\tconst bool\t\t\t_useGUI; ///< Should ImGui windows be displayed.\r\n\t\t};\r\n\r\n\t\tbool\t\t\t\t_shouldClose; ///< Is the window marked as closed.\r\n\t\tGLFWwindowptr\t\t_glfwWin; ///< Undelrying GLF window.\r\n\t\tVector2i\t\t\t_size; ///< Window size.\r\n\t\tconst bool\t\t\t_useGUI; ///< Should ImGui windows be displayed.\r\n\t\tbool\t\t\t\t_useVSync; ///< is the window using vsync.\r\n\t\tVector2i\t\t\t_oldPosition; ///< Backup for handling fullscreen/windowed mode restoration.\r\n\t\tVector2i\t\t\t_oldSize; ///< Backup for handling fullscreen/windowed mode restoration.\r\n\t\tViewport\t\t\t_viewport; ///< Current viewport.\r\n\t\tfloat\t\t\t\t_scaling = 1.0f; ///< Internal scaling for HiDPI screens.\r\n\t\t// Must be placed add the end of member data .\r\n\t\tAutoInitializer\t\t_hiddenInit; ///< nifty counter used to auto-init window system\r\n\t\tstd::string\t\t\t_defaultImguiSettingsFilename = \"imgui_default.ini\"; ///< string of default Imgui settings filename\r\n\t\tstd::string\t\t\t_windowImguiSettingsFilename; ///< string of default Window specific Imgui settings filename\r\n\t};\r\n\r\n\t///// INLINES /////\r\n\tinline void\t\tWindow::makeContextCurrent(void) {\r\n\t\tglfwMakeContextCurrent(_glfwWin.get());\r\n\t}\r\n\r\n\tinline void\t\tWindow::makeContextNull(void) {\r\n\t\tglfwMakeContextCurrent(0);\r\n\t}\r\n\r\n\tinline GLFWwindow *\t\tWindow::getContextCurrent(void) {\r\n\t\treturn glfwGetCurrentContext();\r\n\t}\r\n\r\n\tinline GLuint\tWindow::texture(uint /*t*/) const {\r\n\t\tSIBR_ERR << \"You are trying to read the Window's backbuffer (use sibr::blit instead).\" << std::endl;\r\n\t\treturn 0;\r\n\t}\r\n\tinline GLuint\tWindow::handle(uint /*t*/) const {\r\n\t\tSIBR_ERR << \"You are trying to read the Window's backbuffer (use sibr::blit instead).\" << std::endl;\r\n\t\treturn 0;\r\n\t}\r\n\tinline GLuint\tWindow::fbo(void) const {\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tinline void\t\tWindow::bind(void) {\r\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, 0);\r\n\r\n\t\t\r\n\t}\r\n\r\n\tinline void\t\tWindow::unbind(void) {\r\n\t\t/*nothing*/\r\n\t}\r\n\r\n\tinline void\t\tWindow::clear(void) {\r\n\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r\n\t}\r\n\r\n\tinline uint\t\tWindow::w(void) const {\r\n\t\treturn (uint)size().x();\r\n\t}\r\n\r\n\tinline uint\t\tWindow::h(void) const {\r\n\t\treturn (uint)size().y();\r\n\t}\r\n\r\n\tinline float\tWindow::scaling() const\r\n\t{\r\n\t\treturn _scaling;\r\n\t}\r\n\r\n\tinline const Viewport&\tWindow::viewport(void) const {\r\n\t\treturn _viewport;\r\n\t}\r\n\r\n\tinline void\t\t\t\tWindow::viewport(const Viewport& view) {\r\n\t\t_viewport = view;\r\n\t}\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/resources/imgui_default.ini",
    "content": "[Window]\nPos=60,60\nSize=400,400\nCollapsed=0"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/graphics/sibr_graphics.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_graphics sibr_graphics\n\n\t\\brief OpenGL and graphics utilities.\n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nproject(sibr_imgproc)\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\r\nsource_group(\"Source Files\" FILES ${SOURCES})\r\n\r\n## Specify target rules\r\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\r\n\r\ninclude_directories(\r\n\t${Boost_INCLUDE_DIRS}\r\n\t${mrf_INCLUDE_DIRS}\r\n)\r\nif(WIN32)\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\t${Boost_LIBRARIES}\r\n\t${OpenCV_LIBRARIES}\r\n\tOpenMP::OpenMP_CXX\r\n\tglfw3\r\n\tmrf\r\n\tsibr_system\r\n\tsibr_graphics\r\n\tsibr_assets\r\n\tsibr_raycaster\r\n)\r\nelse()\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\t${Boost_LIBRARIES}\r\n\t${OpenCV_LIBRARIES}\r\n\tOpenMP::OpenMP_CXX\r\n\t${GLFW_LIBRARY}\r\n\tmrf\r\n\tsibr_system\r\n\tsibr_graphics\r\n\tsibr_assets\r\n\tsibr_raycaster\r\n)\r\nendif()\r\n\r\nif (NOT WIN32)\r\n\ttarget_link_libraries(${PROJECT_NAME}\r\n \t\t# GLEW\r\n \t\trt m dl X11 pthread Xrandr Xinerama Xxf86vm Xcursor\r\n\t\t# X11 Xi Xrandr Xxf86vm Xinerama Xcursor dl rt m pthread\r\n\t)\r\nendif()\r\n\r\nadd_definitions( -DSIBR_IMGPROC_EXPORTS -DBOOST_ALL_DYN_LINK  )\r\n\r\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\r\n\r\n\r\n## High level macro to install in an homogen way all our ibr targets\r\ninclude(install_runtime)\r\nibr_install_target(${PROJECT_NAME}\r\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\r\n)\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/system/Config.hpp\"\n# include \"core/system/Utils.hpp\"\n\n# ifdef SIBR_OS_WINDOWS\n//// Export Macro (used for creating DLLs) ////\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_IMGPROC_EXPORT\n#      ifdef SIBR_IMGPROC_EXPORTS\n          /* We are building this library */\n#        define SIBR_IMGPROC_EXPORT __declspec(dllexport)\n#      else\n          /* We are using this library */\n#        define SIBR_IMGPROC_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT \n#    endif\n#  endif\n# else\n# define SIBR_IMGPROC_EXPORT\n# endif\n\n\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/CropScaleImageUtility.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"CropScaleImageUtility.hpp\"\n\nnamespace sibr {\n\n\tstd::vector<std::string> CropScaleImageUtility::getPathToImgs(const std::string & inputFileName)\n\t{\n\t\tstd::ifstream inputFile(inputFileName);\n\t\tstd::string line;\n\t\tstd::vector<std::string> pathToImgs;\n\t\twhile (getline(inputFile, line)) {\n\t\t\tstd::stringstream ss(line);\n\t\t\tstd::string path;\n\t\t\tunsigned width, height;\n\t\t\tss >> path >> width >> height;\n\t\t\tpathToImgs.push_back(path);\n\t\t}\n\t\tinputFile.close();\n\t\treturn pathToImgs;\n\t}\n\n\tvoid CropScaleImageUtility::logExecution(const sibr::Vector2i & originalResolution, unsigned nrImages, long long elapsedTime, bool wasTransformed, const char* log_file_name)\n\t{\n\t\t// check if file exists\n\t\tconst bool isEmptyFile = !sibr::fileExists(log_file_name);\n\t\tstd::ofstream outputFile(log_file_name, std::ios::app);\n\n\t\tif (isEmptyFile) {\n\t\t\toutputFile << \"date\\t\\t\\tresolution\\tnrImgs\\telapsedTime\\twas transformed?\\n\";\n\t\t}\n\n\t\ttime_t now = std::time(nullptr);\n\t\t\n#ifdef SIBR_OS_WINDOWS\n\t\ttm ltm = {0,0,0,0,0,0,0,0,0};\n\t\tlocaltime_s(&ltm, &now);\n#else\n\t\ttm ltm = *(std::localtime(&now));\n#endif\n\t\t\n\t\tstd::stringstream dateSS;\n\t\tdateSS << \"[\" << 1900 + ltm.tm_year << \"/\" << 1 + ltm.tm_mon << \"/\" << ltm.tm_mday << \"] \"\n\t\t\t<< ltm.tm_hour << \":\" << ltm.tm_min << \":\" << ltm.tm_sec;\n\n\t\toutputFile << dateSS.str() << \"\\t\" << originalResolution[0] << \"x\" << originalResolution[1] << \"\\t\\t\" << nrImages << \"\\t\" << elapsedTime << \"\\t\" << wasTransformed << \"\\n\";\n\n\t\toutputFile.close();\n\t}\n\n\tvoid CropScaleImageUtility::writeListImages(const std::string path_to_file, const std::vector<CropScaleImageUtility::Image> & listOfImages)\n\t{\n\t\tstd::ofstream outputFile(path_to_file);\n\n\t\tfor (unsigned i = 0; i < listOfImages.size(); i++) {\n\t\t\toutputFile << listOfImages[i].filename << \" \" << listOfImages[i].width << \" \" << listOfImages[i].height << \"\\n\";\n\t\t}\n\n\t\toutputFile.close();\n\t}\n\n\tsibr::Vector2i CropScaleImageUtility::parseResolution(const std::string & param)\n\t{\n\t\tsize_t delimiterPos = param.find('x');\n\t\tstd::string widthStr = param.substr(0, delimiterPos);\n\t\tstd::string heightStr = param.substr(delimiterPos + 1);\n\t\treturn sibr::Vector2i(std::stoi(widthStr), std::stoi(heightStr));\n\t}\n\n\tvoid CropScaleImageUtility::writeScaleFactor(const std::string path_to_file, float scaleFactor)\n\t{\n\t\tstd::ofstream outputFile(path_to_file);\n\n\t\toutputFile << scaleFactor << \"\\n\";\n\n\t\toutputFile.close();\n\t}\n\n\tvoid CropScaleImageUtility::writeTargetResolution(const std::string path_to_file, const sibr::Vector2i & targetResolution)\n\t{\n\t\tstd::ofstream outputFile(path_to_file);\n\n\t\toutputFile << targetResolution[0] << \" \" << targetResolution[1] << \"\\n\";\n\n\t\toutputFile.close();\n\t}\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/CropScaleImageUtility.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n#include <core/graphics/Image.hpp>\n\n#include <boost/filesystem.hpp>\n\n#include <fstream>\n#include <iostream>\n#include <iomanip>\n\n\nnamespace sibr {\n\n\t/** \\brief Utility class to crop and rescale images, especially for uniformizing IBR datasets.\n\t* \\ingroup sibr_imgproc\n\t*/\n\tclass SIBR_IMGPROC_EXPORT CropScaleImageUtility\n\t{\n\tpublic:\n\n\t\t/** Image infos. */\n\t\tstruct Image {\n\t\t\tstd::string\tfilename; ///< Image file name.\n\t\t\tunsigned\twidth; ///< Image width.\n\t\t\tunsigned\theight; ///< Image height.\n\t\t};\n\n\t\t/** Load a list_images.txt file and extract the image paths.\n\t\t * \\param inputFileName path to the listing\n\t\t * \\return a list of image paths\n\t\t */\n\t\tstd::vector<std::string> getPathToImgs(const std::string & inputFileName);\n\t\t\n\t\t/**\n\t\t * Log processing informations to a file.\n\t\t * \\param resolution the estimated resolution\n\t\t * \\param nrImages the number of images\n\t\t * \\param elapsedTime the time taken by the processing\n\t\t * \\param wasTransformed was transforamtion applied\n\t\t * \\param log_file_name the destination file path\n\t\t */\n\t\tvoid logExecution(const sibr::Vector2i & resolution, unsigned nrImages, long long elapsedTime, bool wasTransformed, const char* log_file_name);\n\t\t\n\t\t/**\n\t\t * Save a list of images to a list_images.txt file, where each image has a line \"name w h\".\n\t\t * \\param path_to_file the destination file path\n\t\t * \\param listOfImages the images to save to the list\n\t\t */\n\t\tvoid writeListImages(const std::string path_to_file, const std::vector<Image> & listOfImages);\n\n\t\t/**\n\t\t * Write a scale float factor to a text file.\n\t\t * \\param path_to_file the destination file path\n\t\t * \\param scaleFactor the value to write\n\t\t */\n\t\tvoid writeScaleFactor(const std::string path_to_file, float scaleFactor);\n\n\t\t/**\n\t\t * Write a resolution to a text file, as \"w h\".\n\t\t * \\param path_to_file the destination file path\n\t\t * \\param targetResolution the resolution to write\n\t\t */\n\t\tvoid writeTargetResolution(const std::string path_to_file, const sibr::Vector2i & targetResolution);\n\n\t\t/**\n\t\t * Extract an image resolution from a \"wxh\" string.\n\t\t * \\param param the string to parse\n\t\t * \\return the resolution\n\t\t */\n\t\tsibr::Vector2i parseResolution(const std::string & param);\n\n\t};\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/DistordCropUtility.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"DistordCropUtility.hpp\"\n\nnamespace sibr {\n\t\n\n\tbool DistordCropUtility::isBlack(const sibr::Vector3ub & pixelColor, Vector3i backgroundColor, int threshold_black_color) {\n\t\tsibr::Vector3i c = pixelColor.cast<int>() - backgroundColor;\n\t\treturn c.squaredNorm() < threshold_black_color;\n\t}\n\n\n\tbool DistordCropUtility::is_number(const std::string& s)\n\t{\n\t\treturn !s.empty() && std::find_if(s.begin(),\n\t\t\ts.end(), [](char c) { return !std::isdigit(c); }) == s.end();\n\t}\n\n\tvoid DistordCropUtility::addPixelToQueue(const sibr::Vector2i & pixel, const sibr::ImageRGB & img, std::priority_queue<sibr::Vector2i> & queue, sibr::Array2d<bool> & arrayVisited, Vector3i backgroundColor, int threshold_black_color) {\n\t\tif (!arrayVisited(pixel.x(), pixel.y()) && isBlack(img(pixel.x(), pixel.y()), backgroundColor, threshold_black_color)) {\n\t\t\tqueue.push(pixel);\n\t\t\tarrayVisited(pixel.x(), pixel.y()) = true;\n\t\t}\n\t}\n\n\tvoid DistordCropUtility::findBounds(sibr::Array2d<bool> & isBlack, DistordCropUtility::Bounds & bounds, int thinest_bounding_box_size)\n\t{\n\t\tbool wasUpdated = true;\n\n\t\twhile (wasUpdated) {\n\n\t\t\twasUpdated = false;\n\n\t\t\tfor (int x = bounds.xMin; x <= bounds.xMax; ++x) {\n\t\t\t\twasUpdated = wasUpdated || isBlack(x, bounds.yMax) || isBlack(x, bounds.yMin);\n\t\t\t}\n\t\t\tfor (int y = bounds.yMin; y <= bounds.yMax; ++y) {\n\t\t\t\twasUpdated = wasUpdated || isBlack(bounds.xMax, y) || isBlack(bounds.xMin, y);\n\t\t\t}\n\n\t\t\tif (wasUpdated) {\n\t\t\t\t--bounds.xMax;\n\t\t\t\t++bounds.xMin;\n\t\t\t\t--bounds.yMax;\n\t\t\t\t++bounds.yMin;\n\t\t\t}\n\n\t\t\tif (bounds.xMax - bounds.xMin < thinest_bounding_box_size || bounds.yMax - bounds.yMin < thinest_bounding_box_size) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tDistordCropUtility::Bounds DistordCropUtility::getBounds(const sibr::ImageRGB & img, Vector3i backgroundColor, int threshold_black_color, int thinest_bounding_box_size, float toleranceFactor) {\n\t\tint w = img.w() - 1;\n\t\tint h = img.h() - 1;\n\n\t\tsibr::Array2d<bool> wasVisited(img.w(), img.h(), false);\n\t\tsibr::Array2d<bool> isBlack(img.w(), img.h(), false);\n\t\tstd::priority_queue<sibr::Vector2i> pixelsQueue;\n\n\t\t//init with boundary pixel (set initial pixelQueue)\n\t\t// add first row and last row of pixels to the pixelsQueue (if they are black) and marked them as visited\n\t\tfor (int x = 0; x<w; ++x) {\n\t\t\taddPixelToQueue(sibr::Vector2i(x, 0), img, pixelsQueue, wasVisited, backgroundColor, threshold_black_color);\n\t\t\taddPixelToQueue(sibr::Vector2i(x, h - 1), img, pixelsQueue, wasVisited, backgroundColor, threshold_black_color);\n\t\t}\n\n\t\t// add left col and right col of pixels to the pixelsQueue (if they are black) and marked them as visited\n\t\tfor (int y = 0; y<h; ++y) {\n\t\t\taddPixelToQueue(sibr::Vector2i(0, y), img, pixelsQueue, wasVisited, backgroundColor, threshold_black_color);\n\t\t\taddPixelToQueue(sibr::Vector2i(w - 1, y), img, pixelsQueue, wasVisited, backgroundColor, threshold_black_color);\n\t\t}\n\n\t\t//neighbors shifts\n\t\tsibr::Vector2i shiftsArray[4] = { sibr::Vector2i(1,0), sibr::Vector2i(-1,0), sibr::Vector2i(0,-1), sibr::Vector2i(0,1) };\n\t\tstd::vector<sibr::Vector2i> shifts(shiftsArray, shiftsArray + sizeof(shiftsArray) / sizeof(sibr::Vector2i));\n\n\t\t//find all black pixels linked to the boundaries\n\t\twhile (pixelsQueue.size() > 0) {\n\t\t\tsibr::Vector2i currentPix = pixelsQueue.top();\n\t\t\tpixelsQueue.pop();\n\t\t\t// if it was in the queue, then it was black\n\t\t\tisBlack(currentPix.x(), currentPix.y()) = true;\n\n\t\t\tfor (auto & shift : shifts) {\n\t\t\t\tsibr::Vector2i newPos = currentPix + shift;\n\t\t\t\tif (img.isInRange(newPos.x(), newPos.y())) {\n\t\t\t\t\taddPixelToQueue(newPos, img, pixelsQueue, wasVisited, backgroundColor, threshold_black_color);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\t//find maximal bounding box not containing black pixels\n\t\tDistordCropUtility::Bounds bounds(img);\n\t\tfindBounds(isBlack, bounds, thinest_bounding_box_size);\n\n\t\tbounds.xRatio = bounds.xMax / (float)img.w() - 0.5f;\n\t\tbounds.yRatio = bounds.yMax / (float)img.h() - 0.5f;\n\n\t\tint proposedWidth = bounds.xMax - bounds.xMin;\n\t\tint proposedHeight = bounds.yMax - bounds.yMin;\n\n\t\tbounds.width = int(float(int(img.w()) - proposedWidth) * toleranceFactor + float(proposedWidth));\n\t\tbounds.height = int(float(int(img.h()) - proposedHeight) * toleranceFactor + float(proposedHeight));\n\n\t\treturn bounds;\n\t}\n\n\n\tsibr::Vector2i DistordCropUtility::calculateAvgResolution(const std::vector<Path>& imagePaths, std::vector<sibr::Vector2i> & resolutions, const int batch_size)\n\t{\n\t\tconst int nrBatches = static_cast<int>(ceil((float)(imagePaths.size()) / batch_size));\n\t\tresolutions.resize(imagePaths.size());\n\t\tstd::vector<std::pair<std::pair<long, long>, unsigned>> sumAndNrItems(nrBatches);\n\n\t\tfor (int batchId = 0; batchId < nrBatches; batchId++) {\n\n\t\t\tconst int nrItems = (batchId != nrBatches - 1) ? batch_size : ((nrBatches * batch_size != int(imagePaths.size())) ? (int(imagePaths.size()) - (batch_size * batchId)) : batch_size);\n\t\t\tlong sumOfWidths = 0;\n\t\t\tlong sumOfHeights = 0;\n\n\t\t\tstd::vector<sibr::ImageRGB> chunkOfInputImages(nrItems);\n\n#pragma omp parallel for\n\t\t\tfor (int localImgIndex = 0; localImgIndex < nrItems; localImgIndex++) {\n\t\t\t\tunsigned globalImgIndex = (batchId * batch_size) + localImgIndex;\n\t\t\t\tchunkOfInputImages.at(localImgIndex).load(imagePaths.at(globalImgIndex).string(), false);\n\n#pragma omp critical\n\t\t\t\t{\n\t\t\t\t\tsumOfWidths += long(chunkOfInputImages[localImgIndex].w());\n\t\t\t\t\tsumOfHeights += long(chunkOfInputImages[localImgIndex].h());\n\t\t\t\t\tresolutions[localImgIndex].x() = chunkOfInputImages[localImgIndex].w();\n\t\t\t\t\tresolutions[localImgIndex].y() = chunkOfInputImages[localImgIndex].h();\n\t\t\t\t}\n\t\t\t}\n\t\t\tstd::pair<long, long> sums(sumOfWidths, sumOfHeights);\n\t\t\tstd::pair<std::pair<long, long>, unsigned> batch(sums, nrItems);\n\t\t\tsumAndNrItems[batchId] = batch;\n\t\t}\n\n\t\tlong sumOfWidth = 0;\n\t\tlong sumOfHeight = 0;\n\t\tfor (unsigned i = 0; i < sumAndNrItems.size(); i++) {\n\t\t\tsumOfWidth += sumAndNrItems[i].first.first;\n\t\t\tsumOfHeight += sumAndNrItems[i].first.second;\n\t\t}\n\n\t\tconst long globalAvgWidth = sumOfWidth / long(imagePaths.size());\n\t\tconst long globalAvgHeight = sumOfHeight / long(imagePaths.size());\n\n\t\treturn sibr::Vector2i(int(globalAvgWidth), int(globalAvgHeight));\n\t}\n\n\tsibr::Vector2i DistordCropUtility::findBiggestImageCenteredBox(const Path & root,\n\t\tconst std::vector<Path>& imagePaths, \n\t\tstd::vector<sibr::Vector2i>& resolutions, \n\t\tint avgWidth, int avgHeight, \n\t\tconst int batch_size, \n\t\tfloat resolutionThreshold, \n\t\tfloat threshold_ratio_bounding_box_size, \n\t\tVector3i backgroundColor, \n\t\tint threshold_black_color, \n\t\tint thinest_bounding_box_size, \n\t\tfloat toleranceFactor)\n\t{\n\t\t// check if avg resolution needs to be calculated\n\t\tif (avgWidth == 0 || avgHeight == 0) {\n\t\t\tstd::cout << \"about to calculate avg resolution. use python get_image_size script if dataset has too many images\\n\";\n\t\t\tsibr::Vector2i avgResolution = calculateAvgResolution(imagePaths, resolutions, batch_size);\n\t\t\tavgWidth = avgResolution.x();\n\t\t\tavgHeight = avgResolution.y();\n\t\t}\n\n\t\tstd::cout << \"[distordCrop] average resolution \" << avgWidth << \"x\" << avgHeight << \" and nr resolutions given: \" << resolutions.size() << \"\\n\";\n\n\t\t// discard images with different resolution\n\t\tstd::vector<uint> preExcludedCams;\n\t\tfor (unsigned i = 0; i < resolutions.size(); i++) {\n\t\t\tbool shrinkHorizontally = ((resolutions[i].x() < avgWidth) && ((avgWidth - resolutions[i].x()) > avgWidth * resolutionThreshold)) ? true : false;\n\t\t\tbool shrinkVertically = ((resolutions[i].y() < avgHeight) && ((avgHeight - resolutions[i].y()) > avgHeight * resolutionThreshold)) ? true : false;\n\t\t\tif (shrinkHorizontally || shrinkVertically) {\n\t\t\t\tpreExcludedCams.push_back(i);\n\t\t\t\tstd::cout << \"[distordCrop] excluding input image \" << i << \" resolution=\" << resolutions[i].x() << \"x\" << resolutions[i].y() << \"\\n\";\n\t\t\t}\n\t\t}\n\n\t\tstd::cout << \"[distordCrop] nr pre excluded images \" << preExcludedCams.size() << \"\\n\";\n\n\t\t// compute bounding boxes for all non-discarded images\n\t\tstd::vector<Bounds> allBounds(imagePaths.size());\n\n\t\tconst int nrBatches = static_cast<int>(ceil((float)(imagePaths.size()) / batch_size));\n\n\t\t// processs batches sequentially (we don't want to run out of memory)\n\t\tfor (int batchId = 0; batchId < nrBatches; batchId++) {\n\n\t\t\tconst int nrItems = (batchId != nrBatches - 1) ? batch_size : ((nrBatches * batch_size != int(imagePaths.size())) ? (int(imagePaths.size()) - (batch_size * batchId)) : batch_size);\n\n\t\t\tstd::vector<sibr::ImageRGB> chunkOfInputImages(nrItems);\n\n\t\t\t// load images in parallel (OpenMP 2.0 doesn't allow unsigned int as index. must be signed integral type)\n#pragma omp parallel for\n\t\t\tfor (int localImgIndex = 0; localImgIndex < nrItems; localImgIndex++) {\n\t\t\t\tconst uint globalImgIndex = uint((batchId * batch_size) + localImgIndex);\n\t\t\t\t// if cam was discarded, do nothing\n\t\t\t\tif (std::find(preExcludedCams.begin(), preExcludedCams.end(), globalImgIndex) == preExcludedCams.end()) {\n\t\t\t\t\t// only now load the img\n\t\t\t\t\tchunkOfInputImages.at(localImgIndex).load(imagePaths.at(globalImgIndex).string(), false);\n\t\t\t\t\tallBounds.at(globalImgIndex) = getBounds(chunkOfInputImages.at(localImgIndex), backgroundColor, threshold_black_color, thinest_bounding_box_size, toleranceFactor);\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tBounds finalBounds(resolutions.at(0));\n\n\t\tint im_id = 0;\n\n\t\t// generate exclude file based on x and y ratios\n\t\tstd::string excludeFilePath = root.string() + \"/exclude_images.txt\";\n\t\tstd::ofstream excludeFile(excludeFilePath, std::ios::trunc);\n\n\t\tint minWidth = -1;\n\t\tint minHeight = -1;\n\n\t\tfor (auto & bounds : allBounds) {\n\t\t\tbool wasPreExcluded = std::find(preExcludedCams.begin(), preExcludedCams.end(), im_id) != preExcludedCams.end();\n\n\t\t\tif (!wasPreExcluded && bounds.xRatio > threshold_ratio_bounding_box_size && bounds.yRatio > threshold_ratio_bounding_box_size) {\n\t\t\t\t// get global x and y ratios\n\t\t\t\tbool check = false;\n\t\t\t\tif (bounds.xRatio < finalBounds.xRatio) {\n\t\t\t\t\tfinalBounds.xRatio = bounds.xRatio;\n\t\t\t\t\tcheck = true;\n\t\t\t\t}\n\t\t\t\tif (bounds.yRatio < finalBounds.yRatio) {\n\t\t\t\t\tfinalBounds.yRatio = bounds.yRatio;\n\t\t\t\t\tcheck = true;\n\t\t\t\t}\n\n\t\t\t\tminWidth = (minWidth < 0 || bounds.width < minWidth) ? bounds.width : minWidth;\n\t\t\t\tminHeight = (minHeight < 0 || bounds.height < minHeight) ? bounds.height : minHeight;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tstd::cerr << im_id << \" \";\n\t\t\t\texcludeFile << im_id << \" \";\n\n\t\t\t\tstd::cout << wasPreExcluded << \" \" << bounds.xRatio << \" \" << threshold_ratio_bounding_box_size << \" \" << bounds.yRatio << \" \" << threshold_ratio_bounding_box_size << std::endl;\n\t\t\t}\n\n\t\t\t++im_id;\n\n\t\t}\n\t\texcludeFile.close();\n\t\tstd::cout << std::endl;\n\n\t\treturn sibr::Vector2i(minWidth, minHeight);\n\n\t}\n\n\tsibr::Vector2i DistordCropUtility::findMinImageSize(const Path & root, const std::vector<Path>& imagePaths)\n\t{\n\t\tstd::vector<sibr::ImageRGB> inputImgs(imagePaths.size());\n\t\tstd::vector<sibr::Vector2i> imSizes(imagePaths.size());\n\n\t\tstd::cout << \"[distordCrop] loading input images : \" << std::flush;\n\n#pragma omp parallel for\n\t\tfor (int id = 0; id < (int)inputImgs.size(); ++id) {\n\t\t\tinputImgs.at(id).load(imagePaths.at(id).string(), false);\n\t\t\timSizes[id] = inputImgs[id].size().cast<int>();\n\t\t}\n\n\t\tsibr::Vector2i minSize = imSizes[0];\n\t\tfor (const auto & size : imSizes) {\n\t\t\tminSize = minSize.cwiseMin(size);\n\t\t}\n\n\t\t// generate exclude file based on x and y ratios\n\t\tstd::string excludeFilePath = root.string() + \"/excludeImages.txt\";\n\t\tstd::ofstream excludeFile(excludeFilePath, std::ios::trunc);\n\t\texcludeFile.close();\n\n\t\treturn minSize;\n\t}\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/DistordCropUtility.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n#include <core/graphics/Image.hpp>\n#include <core/system/Vector.hpp>\n#include <core/system/Array2d.hpp>\n\n#include <boost/filesystem.hpp>\n#include <boost/foreach.hpp> \n\n#include<fstream>\n#include <queue>\n\n\nnamespace sibr {\n\n\t/** \\brief Helpers for cropping undistorted dataset images so that margins are removed, while keeping the dataset consistent.\n\t* \\ingroup sibr_imgproc\n\t*/\n\tclass SIBR_IMGPROC_EXPORT DistordCropUtility\n\t{\n\tpublic:\n\n\t\t/** Image crop boundaries . */\n\t\tstruct Bounds\n\t\t{\n\t\t\t/// Default constructor.\n\t\t\tBounds() {}\n\n\t\t\t/** Initialize with an image boundaries.\n\t\t\t *\\param img the image to use\n\t\t\t **/\n\t\t\tBounds(const sibr::ImageRGB & img) {\n\t\t\t\txMax = (int)img.w() - 1;\n\t\t\t\txMin = 0;\n\t\t\t\tyMax = (int)img.h() - 1;\n\t\t\t\tyMin = 0;\n\t\t\t\txRatio = 1.0f;\n\t\t\t\tyRatio = 1.0f;\n\t\t\t}\n\n\t\t\t/** Initialize with a given resolution.\n\t\t\t *\\param res the resolution\n\t\t\t **/\n\t\t\tBounds(const sibr::Vector2i & res) {\n\t\t\t\txMax = res.x() - 1;\n\t\t\t\txMin = 0;\n\t\t\t\tyMax = res.y() - 1;\n\t\t\t\tyMin = 0;\n\t\t\t\txRatio = 1.0f;\n\t\t\t\tyRatio = 1.0f;\n\t\t\t}\n\n\t\t\t/** \\return a string representing the bounds, for logging. */\n\t\t\tstd::string display() const {\n\t\t\t\tstd::stringstream s;\n\t\t\t\ts << \"[\" << xMin << \", \" << xMax << \"]x[\" << yMin << \", \" << yMax << \"]\";\n\t\t\t\treturn s.str();\n\t\t\t}\n\n\t\t\tint xMax = 0; ///< Max x value.\n\t\t\tint xMin = 0; ///< Min x value.\n\t\t\tint yMax = 0; ///< Max y value.\n\t\t\tint yMin = 0; ///< Min y value.\n\t\t\tint width = 0; ///< Region width.\n\t\t\tint height = 0; ////< Region height.\n\n\t\t\tfloat xRatio = 1.0f; ///< Scaling ratio along X axis.\n\t\t\tfloat yRatio = 1.0f; ///< Scaling ratio along Y axis.\n\t\t};\n\n\t\t/** Check if a pixel color is close to a reference color.\n\t\t *\\param pixelColor the color to test\n\t\t *\\param backgroundColor the reference color\n\t\t *\\param threshold_black_color the tolerance threshold\n\t\t *\\return true if ||pixel - background||^2 < threshold\n\t\t */\n\t\tbool isBlack(const sibr::Vector3ub & pixelColor, Vector3i backgroundColor, int threshold_black_color);\n\n\t\t/*\n\t\t* Check if a file name is made out only of digits and not letters (like texture file names).\n\t\t* \\param s the filename to test\n\t\t* \\return true if the string only contains digits\n\t\t*/\n\t\tbool is_number(const std::string& s);\n\n\t\t/*\n\t\t* Add pixel(x,y) to the processing queue if it is close to backgroundColor.\n\t\t* Note that only the visited status of black pixels is updated (to avoid adding them multiple times) because we don't care about other pixels.\n\t\t* \\param pixel the coordinates of the pixel to test\n\t\t* \\param img the image the pixel is coming from\n\t\t* \\param queue the queue, pixel will be added to it if close to backgroundColor\n\t\t* \\param arrayVisited visited status of each pixel (to avoid adding a pixel to the queue multiple times)\n\t\t* \\param backgroundColor the reference color\n\t\t* \\param threshold_black_color the tolerance threshold\n\t\t* \\sa isBlack\n\t\t*/\n\t\tvoid addPixelToQueue(const sibr::Vector2i & pixel, const sibr::ImageRGB & img, std::priority_queue<sibr::Vector2i> & queue, sibr::Array2d<bool> & arrayVisited, Vector3i backgroundColor, int threshold_black_color);\n\n\t\t/**\n\t\t * Estimate a region that won't contain any black pixels.\n\t\t * \\param isBlack 2D array listing which pixels should be excluded\n\t\t * \\param bounds will contain the region boundaries\n\t\t * \\param thinest_bounding_box_size minimum size of the bounds along any dimension\n\t\t */\n\t\tvoid findBounds(sibr::Array2d<bool> & isBlack, Bounds & bounds, int thinest_bounding_box_size);\n\n\t\t/** Estimate a region of an image so that no pixels of a reference color are contained in it.\n\t\t *\\param img the image to crop\n\t\t *\\param backgroundColor the reference color\n\t\t *\\param threshold_black_color the color tolerance threshold\n\t\t *\\param thinest_bounding_box_size minimum size of the bounds along any dimension\n\t\t *\\param toleranceFactor Additional tolerance factor: if set to 0 the bounds will be tight, if set to 1 it will cover the full image.\n\t\t *\\return the estimated region boundaries\n\t\t */\n\t\tBounds getBounds(const sibr::ImageRGB & img, Vector3i backgroundColor, int threshold_black_color, int thinest_bounding_box_size, float toleranceFactor);\n\n\t\t/**\n\t\t * Estimate the average resolution of a set of images quickly using multithread to speed up the required loading.\n\t\t * \\param imagePaths list of paths to the images\n\t\t * \\param resolutions will contain each image resolution\n\t\t * \\param batch_size number of images loaded per thread internally\n\t\t * \\return the average resolution\n\t\t */\n\t\tsibr::Vector2i calculateAvgResolution(const std::vector< Path > & imagePaths, std::vector<sibr::Vector2i> & resolutions, const int batch_size = 150);\n\t\t\n\t\t/**\n\t\t * Find a common crop region for a set of images so that all pixels of a reference color are excluded from all images, while minimizing information loss.\n\t\t * \\param root the dataset root path (for writing list files)\n\t\t * \\param imagePaths list of image paths\n\t\t * \\param resolutions will contain the image resolutions\n\t\t * \\param avgWidth average image width, if 0 will be recomputed (slow for large datasets)\n\t\t * \\param avgHeight average image height, if 0 will be recomputed (slow for large datasets)\n\t\t * \\param batch_size batch size for multithreaded image loading\n\t\t * \\param resolutionThreshold ratio of the minimum allowed dimensions over the average image dimensions\n\t\t * \\param threshold_ratio_bounding_box_size maximum change in aspect ratio\n\t\t * \\param backgroundColor the reference background color\n\t\t * \\param threshold_black_color the color tolerance threshold\n\t\t * \\param thinest_bounding_box_size minimum size of the bounds along any dimension\n\t\t * \\param toleranceFactor Additional tolerance factor: if set to 0 the bounds will be tight, if set to 1 it will cover the full image.\n\t\t * \\return \n\t\t */\n\t\tsibr::Vector2i findBiggestImageCenteredBox(const Path & root, const std::vector< Path > & imagePaths, std::vector<sibr::Vector2i> & resolutions, int avgWidth = 0, int avgHeight = 0,\n\t\t\tconst int batch_size = 150,\n\t\t\tfloat resolutionThreshold = 0.15f,\n\t\t\tfloat threshold_ratio_bounding_box_size = 0.2f,\n\t\t\tVector3i backgroundColor = Vector3i(0, 0, 0),\n\t\t\tint threshold_black_color = 10,\n\t\t\tint thinest_bounding_box_size = 5,\n\t\t\tfloat toleranceFactor = 0.0f);\n\n\t\t/**\n\t\t * Find the resolution of the smallest image in a set.\n\t\t * \\note In the past, this function was also supposed to exclude images based on a certain criterion and write them to a file.\n\t\t * \\param root dataset root path (for writing an exclude list file, see note)\n\t\t * \\param imagePaths list of paths to the images\n\t\t * \\return the minimum resolution\n\t\t */\n\t\tsibr::Vector2i findMinImageSize(const Path & root, const std::vector< Path > & imagePaths);\n\n\n\t};\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/MRFSolver.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"MRFSolver.h\"\n\n\nnamespace sibr {\n\n\tMRFSolver::MRFSolver(void)\n\t{\n\t}\n\n\tMRFSolver::MRFSolver(std::vector<int> labels_list, std::vector<std::vector<int> >* neighborMap, int numIterations,\n\t\tUnaryLabelOnlyFuncPtr unaryLabelOnly,\n\t\tUnaryFuncPtr unaryFull,\n\t\tPairwiseLabelOnlyFuncPtr pairwiseLabelsOnly,\n\t\tPairwiseFuncPtr pairwiseFull)\n\t\t: ignoreIsolatedNode(false)\n\t{\n\t\tSIBR_LOG << \"[MRFSolver] Initialization ... \";\n\n\t\t_labList = labels_list;\n\t\t_neighborMap = neighborMap;\n\t\t_numIterations = numIterations;\n\t\t_unaryFull = unaryFull;\n\t\t_pairwiseFull = pairwiseFull;\n\n\t\tSIBR_LOG << \"[MRFSolver] Labels : \";\n\t\tfor (int l_id = 0; l_id < _labList.size(); l_id++) {\n\t\t\tstd::cout << _labList[l_id] << \",\";\n\t\t}\n\t\tstd::cout << std::endl;\n\n\t\t//storing values for the unary part only requiring label\n\t\tif (unaryLabelOnly.get()) {\n\t\t\tSIBR_LOG << \"[MRFSolver] unaryLabelOnly exists, precomputing.\" << std::endl;\n\t\t\t_UnaryLabelOnly.resize(_labList.size());\n\t\t\tfor (int l_id = 0; l_id < _labList.size(); l_id++) {\n\t\t\t\t_UnaryLabelOnly[l_id] = (*unaryLabelOnly)(_labList[l_id]);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tSIBR_LOG << \"[MRFSolver] unaryLabelOnly does not exist, skipping.\" << std::endl;\n\t\t}\n\n\t\t//storing values for the pairwise part only requiring labels\n\t\tif (pairwiseLabelsOnly.get()) {\n\t\t\tSIBR_LOG << \"[MRFSolver] pairwiseLabelsOnly exists, precomputing.\" << std::endl;\n\t\t\t_PairwiseLabelsOnly.resize(_labList.size());\n\n\t\t\tfor (int l_id1 = 0; l_id1 < _labList.size(); l_id1++) {\n\t\t\t\t_PairwiseLabelsOnly[l_id1].resize(_labList.size(), -1);\n\n\t\t\t\tfor (int l_id2 = 0; l_id2 < _labList.size(); l_id2++) {\n\t\t\t\t\t_PairwiseLabelsOnly[l_id1][l_id2] = (*pairwiseLabelsOnly)(_labList[l_id1], _labList[l_id2]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tSIBR_LOG << \"[MRFSolver] pairwiseLabelsOnly does not exist, skipping.\" << std::endl;\n\t\t}\n\n\t\tSIBR_LOG << \"[MRFSolver] Setup complete.\" << std::endl;\n\t}\n\n\tvoid MRFSolver::solveLabels(void)\n\t{\n\t\tSIBR_LOG << \"[MRFSolver] Running mincut... \" << std::endl;\n\n\t\tdouble infty = (double)1e20;\n\t\tdouble min_unary, temp_unary;\n\t\tint num_nodes = (int)_neighborMap->size();\n\t\tSIBR_LOG << \"[MRFSolver] Number of nodes = \" << num_nodes;\n\t\t_labels.resize(num_nodes);\n\n\t\tint numLinks = 0;\n\t\tfor (auto & links : (*_neighborMap)) {\n\t\t\tnumLinks += (int)links.size();\n\t\t}\n\t\tSIBR_LOG << \", number of links = \" << numLinks / 2 << std::endl;\n\t\t\n\t\tSIBR_LOG << \"[MRFSolver] Initialization : minimizing unaries...\" << std::flush;\n\t\tfor (int p = 0; p < num_nodes; p++) {\n\n\t\t\tint label_id;\n\t\t\tmin_unary = infty;\n\t\t\tint num_cand = 0;\n\t\t\tfor (int lp_id = 0; lp_id < (int)_labList.size(); lp_id++) {\n\t\t\t\ttemp_unary = unaryTotal(p, lp_id);\n\t\t\t\tif (temp_unary < (1 << 10)) { ++num_cand; }\n\t\t\t\tif (temp_unary < min_unary) {\n\t\t\t\t\tmin_unary = temp_unary;\n\t\t\t\t\tlabel_id = lp_id;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_labels[p] = label_id;\n\t\t}\n\t\tstd::cout << \" Done.\" << std::endl;\n\n\t\tSIBR_LOG << \"[MRFSolver] Energies: U: \" << computeEnergyU() << \", W: \" << computeEnergyW() << std::endl;\n\n\t\t// Alpha-expansion algorithm\n\t\tSIBR_LOG << \"[MRFSolver] Alpha-expansion [label,flow]...\" << std::endl;\n\t\tfor (int it = 0; it < _numIterations; it++) {\n\t\t\tSIBR_LOG << \"[MRFSolver] Iteration \" << (it+1)  << \"/\" << (_numIterations) << \": \" << std::endl;\n\t\t\t\n\t\t\tfor (int label_id = 0; label_id < (int)_labList.size(); label_id++) {\n\t\t\t\tint label = _labList.at(label_id);\n\t\t\t\t\n\t\t\t\tbuildGraphAlphaExp(label_id);\n\t\t\t\t// Solve mincut\n\t\t\t\t_energy = _graph->maxflow();\n\n\n\t\t\t\tint num_change = 0;\n\t\t\t\t//assign new labels\n\t\t\t\tfor (int p = 0; p < num_nodes; p++) {\n\t\t\t\t\tif (_graph->what_segment(p) == GraphType::SINK) {\n\t\t\t\t\t\tif (_labels[p] != label_id) { ++num_change; }\n\t\t\t\t\t\t_labels[p] = label_id;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tSIBR_LOG << \"[MRFSolver]\\t\\tLabel \" << label << \": modifications = \" <<  num_change << \", energy = \" << _energy << \" ]\" << std::endl;\n\n\t\t\t\tdelete _graph;\n\t\t\t}\n\t\t}\n\t\tSIBR_LOG << \"[MRFSolver] Done.\" << std::endl;\n\t}\n\n\tvoid MRFSolver::buildGraphAlphaExp(int label_iteration_id)\n\t{\n\t\tdouble infty = 1 << 25;\n\t\tint num_nodes = (int)_neighborMap->size();\n\t\tint n_nodes_estimation = num_nodes;\n\t\tint n_edges_estimation = num_nodes * 4;\n\n\t\t_graph = new GraphType(n_nodes_estimation, n_edges_estimation);\n\n\t\t//add nodes associated to pixels\n\t\tint node_id = 0;\n\t\tfor (int p = 0; p < num_nodes; p++) {\n\t\t\t_graph->add_node();\n\n\t\t\tif (_labels[p] == label_iteration_id) {\n\t\t\t\t_graph->add_tweights(node_id, unaryTotal(p, label_iteration_id), infty);\n\t\t\t} else {\n\t\t\t\t_graph->add_tweights(node_id, unaryTotal(p, label_iteration_id), unaryTotal(p, _labels[p]));\n\t\t\t}\n\t\t\t++node_id;\n\t\t}\n\t\t\n\t\t//add nodes associated to connexions between pixels\n\t\tfor (int p = 0; p < num_nodes; p++) {\n\n\t\t\tstd::vector<int> & neighors = (*_neighborMap)[p];\n\t\t\tfor (int q_id = 0; q_id < (int)neighors.size(); q_id++) {\n\n\t\t\t\tint q = neighors[q_id];\n\n\t\t\t\tif (p == q) { std::cerr << \"!\"; }\n\t\t\t\tif (q < p) { continue; }\n\n\t\t\t\tif (_labels[p] != _labels[q]) {\n\t\t\t\t\t//extra node associated to edge {p,q}\n\t\t\t\t\t_graph->add_node();\n\n\t\t\t\t\t_graph->add_tweights(node_id, 0, pairwiseTotal(q, p, _labels[q], _labels[p]));\n\n\t\t\t\t\tdouble pairwise_q_a = pairwiseTotal(q, p, _labels[q], label_iteration_id);\n\t\t\t\t\t_graph->add_edge(q, node_id, pairwise_q_a, pairwise_q_a);\n\n\t\t\t\t\tdouble pairwise_p_a = pairwiseTotal(q, p, label_iteration_id, _labels[p]);\n\t\t\t\t\t_graph->add_edge(p, node_id, pairwise_p_a, pairwise_p_a);\n\n\t\t\t\t\t++node_id;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdouble pairwise_p_q = pairwiseTotal(q, p, _labels[q], label_iteration_id);\n\t\t\t\t\t_graph->add_edge(q, p, pairwise_p_q, pairwise_p_q);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tvoid MRFSolver::solveBinaryLabels(void)\n\t{\n\t\tint numLabels = (int)_labList.size();\n\t\tif (numLabels < 2) {\n\t\t\tSIBR_ERR << \"[MRFSolver] solveBinaryLabels, expected 2 labels, only \" << numLabels << \" labels \" << std::endl;\n\t\t}\n\t\telse if (numLabels > 2) {\n\t\t\tSIBR_WRG << \"[MRFSolver] solveBinaryLabels, found \" << numLabels << \" labels, only the first two will be used.\" << std::endl;\n\t\t}\n\n\t\tbuildGraphBinaryLabels();\n\n\t\t_graph->maxflow();\n\n\t\tint num_nodes = (int)_neighborMap->size();\n\t\t_labels.resize(num_nodes);\n\n\t\t//assign new labels\n\t\tfor (int p = 0; p < num_nodes; p++) {\n\n\t\t\t//TODO check this is not the opposite\n\t\t\tif (_graph->what_segment(p) == GraphType::SINK) {\n\t\t\t\t_labels[p] = 0;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_labels[p] = 1;\n\t\t\t}\n\t\t}\n\n\t\tdelete _graph;\n\t}\n\n\tvoid MRFSolver::buildGraphBinaryLabels(void)\n\t{\n\t\tint num_nodes = (int)_neighborMap->size();\n\t\tint n_nodes_estimation = num_nodes;\n\t\tint n_edges_estimation = num_nodes * 4;\n\n\t\t_graph = new GraphType(n_nodes_estimation, n_edges_estimation);\n\n\t\tfor (int p = 0; p < num_nodes; p++) {\n\t\t\t_graph->add_node();\n\t\t\t_graph->add_tweights(p, unaryTotal(p, 0), unaryTotal(p, 1));\n\t\t}\n\n\t\tfor (int p = 0; p < num_nodes; p++) {\n\t\t\tstd::vector<int> & neighors = (*_neighborMap)[p];\n\t\t\tfor (int q_id = 0; q_id < (int)neighors.size(); q_id++) {\n\n\t\t\t\tint q = neighors[q_id];\n\n\t\t\t\tif (q < p) { continue; }\n\n\t\t\t\tdouble weight = pairwiseTotal(q, p, 0, 1);\n\n\t\t\t\t_graph->add_edge(q, p, weight, weight);\n\t\t\t}\n\t\t}\n\t}\n\n\tdouble MRFSolver::unaryTotal(int p, int lp_id)\n\t{\n\t\tdouble u = 0;\n\t\tif (!_UnaryLabelOnly.empty()) {\n\t\t\tu += _UnaryLabelOnly[lp_id];\n\t\t}\n\t\tif (_unaryFull) {\n\t\t\tu += (*_unaryFull)(p, _labList[lp_id]);\n\t\t}\n\t\tif (u < 0) { std::cerr << \"!\"; }\n\t\treturn u;\n\t}\n\n\tdouble MRFSolver::pairwiseTotal(int p, int q, int lp_id, int lq_id)\n\t{\n\t\tdouble w = 0;\n\t\tif (!_PairwiseLabelsOnly.empty()) {\n\t\t\tw += _PairwiseLabelsOnly[lp_id][lq_id];\n\t\t}\n\n\t\tif (_pairwiseFull) {\n\t\t\tw += (*_pairwiseFull)(p, q, _labList[lp_id], _labList[lq_id]);\n\t\t}\n\t\tif (w < 0) { std::cerr << \"?\" << w << \"?\"; }\n\t\treturn w;\n\t}\n\n\tstd::vector<int> MRFSolver::getLabels(void)\n\t{\n\t\tstd::vector<int> labels(_labels.size());\n\n\t\t//switch from labels id to actual labels \n\t\tfor (int p = 0; p < (int)labels.size(); p++) {\n\t\t\tlabels[p] = _labList[_labels[p]];\n\t\t}\n\t\treturn labels;\n\t}\n\n\tstd::vector<double> MRFSolver::getUnariesEnergies(void)\n\t{\n\t\tif (_labels.size() == 0) { std::cerr << \"[MRFSolver] warning getUnariesEnergies without nodes\" << std::endl; return std::vector<double>(); }\n\n\t\tstd::vector<double> energies(_labels.size());\n\t\tfor (int p = 0; p < (int)_labels.size(); p++) {\n\t\t\tenergies[p] = unaryTotal(p, _labels[p]);\n\t\t}\n\n\t\treturn energies;\n\t}\n\n\tdouble MRFSolver::computeEnergyU(void)\n\t{\n\t\tdouble e = 0;\n\t\tfor (int p = 0; p < (int)_labels.size(); p++) {\n\t\t\te += unaryTotal(p, _labels[p]);\n\t\t}\n\t\treturn e;\n\t}\n\n\tdouble MRFSolver::computeEnergyW(void)\n\t{\n\t\tdouble e = 0;\n\t\tfor (int p = 0; p < (int)_labels.size(); p++) {\n\t\t\tfor (int q_id = 0; q_id < (int)(*_neighborMap)[p].size(); q_id++) {\n\t\t\t\tint q = (*_neighborMap)[p][q_id];\n\t\t\t\tif (q < p) { continue; }\n\t\t\t\te += pairwiseTotal(q, p, _labels[q], _labels[p]);\n\t\t\t}\n\t\t}\n\t\treturn e;\n\t}\n\n\tdouble MRFSolver::getTotalEnergy(void)\n\t{\n\t\treturn _energy;\n\t}\n\n\tMRFSolver::~MRFSolver(void)\n\t{\n\t}\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/MRFSolver.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n#include \"mrf/graph.h\"\n#include <vector>\n#include <iostream>\n#include <functional>\n#include <memory>\n\nnamespace sibr {\n\n\t\n\t/** Object wrapper around Kolmogorov & Boykov MRF solver.\n\t *Solve labelling problems on regular grids using alpha expension.\n\t\\ingroup sibr_imgproc\n\t*/\n\tclass SIBR_IMGPROC_EXPORT MRFSolver\n\t{\n\n\tpublic:\n\n\t\ttypedef std::shared_ptr<std::function<double(int, int)> > UnaryFuncPtr; ///< Unary cost function that depend on node attributes and its label.\n\t\ttypedef std::shared_ptr<std::function<double(int, int, int, int)> > PairwiseFuncPtr; ///< Pairwise cost function that depend on node attributes and their labels.\n\t\t\n\t\ttypedef std::shared_ptr<std::function<double(int)> > UnaryLabelOnlyFuncPtr; ///< Unary cost function that only depend on the label.\n\t\ttypedef std::shared_ptr<std::function<double(int, int)> > PairwiseLabelOnlyFuncPtr; ///< Pairwise cost function that only depend on the labels.\n\t\t\n\t\t/// Default constructor.\n\t\tMRFSolver(void);\n\n\t\t/** Initialize from a set of labels, connections and node/edge weights.\n\t\t *\\param labels list of available labels\n\t\t *\\param neighborMap connectivity map, for each node, list of neighboring nodes linear indices\n\t\t *\\param numIterations number of expansion iterations to perform\n\t\t *\\param unaryLabelOnly optional unary cost that only depends on the label: f(lab0), else provide nullptr\n\t\t *\\param unaryFull unary (per node) cost function evaluator, receiving the node linear index and label: f(ind0, lab0)\n\t\t *\\param pairwiseLabelsOnly optional pairwise cost that only depends on the labels: f(lab0, lab1), else provide nullptr\n\t\t *\\param pairwiseFull pairwise (per pair of nodes) cost function evaluator, receiving the nodes linear indices and their labels: f(ind0, ind1, lab0, lab1)\n\t\t *\\note the \"*LabelsOnly\" functions are optional and are precomputed and cached for optimized resolution.\n\t\t */\n\t\tMRFSolver(std::vector<int> labels, std::vector<std::vector<int> >* neighborMap, int numIterations,\n\t\t\tUnaryLabelOnlyFuncPtr unaryLabelOnly,\n\t\t\tUnaryFuncPtr unaryFull,\n\t\t\tPairwiseLabelOnlyFuncPtr pairwiseLabelsOnly,\n\t\t\tPairwiseFuncPtr pairwiseFull\n\t\t);\n\n\t\t/// Solve using alpha expansion. When you have only two labels, use solveBinaryLabels instead\n\t\tvoid solveLabels(void);\n\n\t\t/// Solve for binary labels: if you only more than two labels, call solveLabels instead. \n\t\tvoid solveBinaryLabels(void);\n\n\t\t/** For each pixel, get the estimated label (call either solveLabels or solveBinaryLabels before).\n\t\t \\return a list of labels, one per pixel */\n\t\tstd::vector<int> getLabels(void);\n\n\t\t/** \\return the total energy of the current labeling. */\n\t\tdouble getTotalEnergy(void);\n\n\t\t/** \\return the unary energy of the current labeling. */\n\t\tdouble computeEnergyU(void);\n\n\t\t/** \\return the pairwise energy of the current labeling. */\n\t\tdouble computeEnergyW(void);\n\n\t\t/** \\return per label unary energy. */\n\t\tstd::vector<double> getUnariesEnergies(void);\n\n\t\t/// Destructor.\n\t\t~MRFSolver(void);\n\n\tprivate:\n\n\t\t/** Build graph for the general case.\n\t\t *\\param label_iteration_id\n\t\t **/\n\t\tvoid buildGraphAlphaExp(int label_iteration_id);\n\n\t\t/** Build graph for the binary labeling case. */\n\t\tvoid buildGraphBinaryLabels(void);\n\n\t\t/** Compute the unary cost of a node.\n\t\t *\\param p the node linear index\n\t\t *\\param lp_id the node label to consider\n\t\t *\\return the total unary cost\n\t\t **/\n\t\tdouble unaryTotal(int p, int lp_id);\n\n\t\t/** Compute the pairwise cost of a pair of nodes.\n\t\t *\\param p the first node linear index\n\t\t *\\param q the second node linear index\n\t\t *\\param lp_id the first node label to consider\n\t\t *\\param lq_id the scond node label to consider\n\t\t *\\return the total pairwise cost\n\t\t **/\n\t\tdouble pairwiseTotal(int p, int q, int lp_id, int lq_id);\n\n\t\tstd::vector<int> _labList; ///< Map the label_id to the actual labels.\n\t\tstd::vector<int> _labels; ///< Assign each node its current best label_id.\n\t\tstd::vector<std::vector<int> >* _neighborMap; ///< For each variable, gives the list of its neighbor variables\n\t\tint _numIterations; ///< Number of iterations in alpha expansion.\n\t\t\n\t\tstd::vector<double> _UnaryLabelOnly; ///< Unaries only requiring label.\n\t\tstd::shared_ptr<std::function<double(int, int)> > _unaryFull; ///< Unaries requiring label and variable.\n\t\tstd::vector<std::vector< double > >  _PairwiseLabelsOnly; ///< Pairwises only requiring labels.\n\t\tstd::shared_ptr<std::function<double(int, int, int, int)> > _pairwiseFull; ///< Pairwises requiring labels and variables.\n\n\t\ttypedef Graph<double, double, double> GraphType;\n\t\tdouble _energy; ///< Total energy.\n\t\tGraphType* _graph; ///< Graph.\n\t\tbool ignoreIsolatedNode; ///< Ignore nodes with no connections.\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/MeshTexturing.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"MeshTexturing.hpp\"\n#include \"PoissonReconstruction.hpp\"\n#include <core/system/LoadingProgress.hpp>\n\nnamespace sibr {\n\n\tMeshTexturing::MeshTexturing(unsigned int sideSize) :\n\t\t_accum(sideSize, sideSize, Vector3f(0.0f, 0.0f, 0.0f)),\n\t\t_mask(sideSize, sideSize, 0)\n\t{\n\n\t}\n\n\tvoid MeshTexturing::setMesh(const sibr::Mesh::Ptr mesh) {\n\t\t_mesh = mesh;\n\n\t\t// We need UVs else we can't do anything.\n\t\tif (!_mesh->hasTexCoords()) {\n\t\t\tSIBR_ERR << \"[Texturing] The mesh to texture needs to have UVs\" << std::endl;\n\t\t\treturn;\n\t\t}\n\t\t// We also need normals.\n\t\tif (!_mesh->hasNormals()) {\n\t\t\t_mesh->generateNormals();\n\t\t}\n\n\t\t// Create a mesh in UV space, using the UVs as 2D coordinates.\n\t\tSIBR_LOG << \"[Texturing] Generating UV mesh...\" << std::endl;\n\t\tconst int verticesCount = int(_mesh->vertices().size());\n\t\tstd::vector<sibr::Vector3f> uvVertices(verticesCount);\n\n#pragma omp parallel for\n\t\tfor (int vid = 0; vid < verticesCount; ++vid) {\n\t\t\tconst sibr::Vector2f & uvs = _mesh->texCoords()[vid];\n\t\t\tuvVertices[vid] = sibr::Vector3f(uvs[0], uvs[1], 0.0f);\n\t\t}\n\t\tMesh uvMesh(false);\n\t\tuvMesh.vertices(uvVertices);\n\t\tuvMesh.triangles(_mesh->triangles());\n\n\t\t// Init both raycasters.\n\t\t// For the world raycaster, we need to have no backface culling. \n\t\t// Our version of Embree being compiled with backface culling, we have to 'duplicate and flip' the mesh.\n\t\tMesh::Ptr doubleMesh = _mesh->clone();\n\t\tdoubleMesh->merge(_mesh->invertedFacesMesh());\n\t\t_worldRaycaster.addMesh(*doubleMesh);\n\t\t_uvsRaycaster.addMesh(uvMesh);\n\n\t}\n\n\tvoid MeshTexturing::interpolate(const sibr::RayHit & hit, sibr::Vector3f & vertex, sibr::Vector3f & normal) const {\n\t\tconst Mesh::Vertices & vertices = _mesh->vertices();\n\t\tconst Mesh::Normals & normals = _mesh->normals();\n\n\t\tconst sibr::Vector3u& tri = _mesh->triangles()[hit.primitive().triID];\n\n\t\tconst float uCoord = hit.barycentricCoord().u;\n\t\tconst float vCoord = hit.barycentricCoord().v;\n\t\tconst float wCoord = sibr::clamp(1.f - uCoord - vCoord, 0.0f, 1.0f);\n\n\t\tvertex = wCoord * vertices[tri[0]] + uCoord * vertices[tri[1]] + vCoord * vertices[tri[2]];\n\t\tnormal = (wCoord * normals[tri[0]] + uCoord * normals[tri[1]] + vCoord * normals[tri[2]]).normalized();\n\t}\n\n\tvoid MeshTexturing::reproject(const std::vector<InputCamera::Ptr> & cameras, const std::vector<sibr::ImageRGB::Ptr> & images, const float sampleRatio) {\n\t\t// We need a mesh for reprojection.\n\t\tif (!_mesh) {\n\t\t\tSIBR_WRG << \"[Texturing] No mesh available.\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\n\t\tstruct SampleInfos {\n\t\t\tsibr::Vector3f color;\n\t\t\tfloat weight;\n\t\t};\n\n\n\t\tconst int w = _accum.w();\n\t\tconst int h = _accum.h();\n\n\t\tsibr::LoadingProgress\t\t\tprogress(h, \"[Texturing] Gathering color samples from cameras\" );\n\t\tSIBR_LOG << \"[Texturing] Gathering color samples from \" << cameras.size() << \" cameras ...\" << std::endl;\n\n#pragma omp parallel for\n\t\tfor (int py = 0; py < h; ++py) {\n\t\t\tfor (int px = 0; px < w; ++px) {\n\t\t\t\t// Check if we fall inside a triangle in the UV map.\n\t\t\t\tRayHit hit;\n\t\t\t\tconst bool hasHit = sampleNeighborhood(px, py, hit);\n\n\t\t\t\t// We really have no triangle in the neighborhood to use, skip.\n\t\t\t\tif (!hasHit) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Need the smooth position and normal in the initial mesh.\n\t\t\t\tsibr::Vector3f vertex, normal;\n\t\t\t\tinterpolate(hit, vertex, normal);\n\n\t\t\t\tsibr::Vector3f avgColor(0.0f, 0.0f, 0.0f);\n\t\t\t\tfloat totalWeight = 0.0f;\n\n\t\t\t\tstd::vector<SampleInfos> samples;\n\n\t\t\t\tfor (int cid = 0; cid < cameras.size(); ++cid) {\n\t\t\t\t\tconst auto & cam = cameras[cid];\n\t\t\t\t\tif (!cam->frustumTest(vertex)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for occlusions.\n\t\t\t\t\tsibr::Vector3f occDir = (vertex - cam->position());\n\t\t\t\t\tconst float dist = occDir.norm();\n\t\t\t\t\tif (dist > 0.0f) {\n\t\t\t\t\t\toccDir /= dist;\n\t\t\t\t\t}\n\t\t\t\t\tconst RayHit hitOcc = _worldRaycaster.intersect(Ray(cam->position(), occDir));\n\t\t\t\t\tif (hitOcc.hitSomething() && (hitOcc.dist() + 0.0001f) < dist) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Reproject, read color.\n\t\t\t\t\tconst sibr::Vector2f pos = cam->projectImgSpaceInvertY(vertex).xy();\n\t\t\t\t\tconst sibr::Vector3f col = images[cid]->bilinear(pos).cast<float>().xyz();\n\t\t\t\t\t// Angle-based weight for now.\n\t\t\t\t\tconst float angleWeight = std::max(-occDir.dot(normal), 0.0f);\n\t\t\t\t\tconst float weight = angleWeight;\n\t\t\t\t\t//avgColor += weight * col;\n\t\t\t\t\t//totalWeight += weight;\n\t\t\t\t\tsamples.emplace_back();\n\t\t\t\t\tsamples.back().color = col;\n\t\t\t\t\tsamples.back().weight = weight;\n\t\t\t\t}\n\t\t\t\tif (samples.empty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstd::sort(samples.begin(), samples.end(), [](const SampleInfos & a, const SampleInfos & b)\n\t\t\t\t{\n\t\t\t\t\treturn a.weight > b.weight;\n\t\t\t\t});\n\n\t\t\t\t// Re-weight and accumulate the samples.\n\t\t\t\t// The code is written this way to support 'best sampleRatio of all samples' approaches.\n\t\t\t\tfor(int i = 0; i < sampleRatio * samples.size(); ++i) {\n\t\t\t\t\tfloat w = samples[i].weight;\n\t\t\t\t\tw = w * w;\n\t\t\t\t\ttotalWeight += w;\n\t\t\t\t\tavgColor += w * samples[i].color;\n\t\t\t\t}\n\n\t\t\t\tif (totalWeight > 0.0f) {\n\t\t\t\t\t_accum(px, py) = avgColor / totalWeight;\n\t\t\t\t\t_mask(px, py)[0] = 255;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif( (py % 1000) == 0 )\n\t\t\t\tprogress.walk(1000);\n\t\t}\n\t}\n\n\tsibr::ImageRGB::Ptr MeshTexturing::getTexture(uint options) const {\n\n\t\tImageRGB32F output;\n\t\tif (options & Options::FLOOD_FILL) {\n\t\t\toutput = floodFill(_accum, _mask)->clone();\n\t\t}\n\t\telse if (options & Options::POISSON_FILL) {\n\t\t\toutput = poissonFill(_accum, _mask)->clone();\n\t\t}\n\t\telse {\n\t\t\toutput = _accum.clone();\n\t\t}\n\n\t\t// Convert as-is to uchar.\n\t\tImageRGB::Ptr result(new ImageRGB());\n\t\tconst cv::Mat3f outputF = output.toOpenCV();\n\t\tconst cv::Mat3b outputB = cv::Mat3b(outputF);\n\t\tresult->fromOpenCV(outputB);\n\n\t\t/// \\todo For extra large images, this might crash because of internal openCV indexing limitations.\n\t\tif (options & Options::FLIP_VERTICAL) {\n\t\t\tresult->flipH();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tsibr::ImageRGB32F::Ptr MeshTexturing::poissonFill(const sibr::ImageRGB32F & image, const sibr::ImageL8 & mask) {\n\t\tSIBR_LOG << \"[Texturing] Poisson filling...\" << std::endl;\n\n\t\tconst cv::Mat3f guideF = cv::Mat3f(image.toOpenCV()) / 255.0f;\n\t\tcv::Mat1f maskF;\n\t\tmask.toOpenCV().convertTo(maskF, CV_32FC1, 1.0f / 255.0f);\n\n\t\tconst cv::Mat3f gradX = cv::Mat3f::zeros(guideF.rows, guideF.cols);\n\t\tconst cv::Mat3f gradY = gradX.clone();\n\n\t\tPoissonReconstruction poisson(gradX, gradY, maskF, guideF);\n\t\tpoisson.solve();\n\t\tconst cv::Mat3f resultF = 255.0f * poisson.result();\n\n\t\tImageRGB32F::Ptr filled(new ImageRGB32F());\n\t\tfilled->fromOpenCV(resultF);\n\t\treturn filled;\n\t}\n\n\n\tbool MeshTexturing::hitTest(int px, int py, RayHit & finalHit)\n\t{\n\t\t// From the UVs find the world space position.\n\t\tconst float u = (float(px) + 0.5f) / float(_accum.w());\n\t\tconst float v = (float(py) + 0.5f) / float(_accum.h());\n\t\t// Spawn a ray from (u,v,0) in the z direction.\n\t\tconst RayHit hit = _uvsRaycaster.intersect(Ray({ u, v, 1.0f }, { 0.0f,0.0f,-1.0f }));\n\t\tif (hit.hitSomething()) {\n\t\t\tfinalHit = hit;\n\t\t\treturn true;\n\t\t}\n\t\t// Just in case of backface culling, try the other side.\n\t\tconst RayHit hitBack = _uvsRaycaster.intersect(Ray({ u, v, -1.0f }, { 0.0f,0.0f,1.0f }));\n\t\tif (hitBack.hitSomething()) {\n\t\t\tfinalHit = hitBack;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tbool MeshTexturing::sampleNeighborhood(int px, int py, RayHit & hit)\n\t{\n\t\tbool hasHit = hitTest(px, py, hit);\n\t\t// Sample a 3x3 neighborhood to counter-act aliasing/interpolation later on, as long as we don't get a hit.\n\t\t// The order is important, to first fetch in line/column and then in diagonal. Sorry for the cache...\n\t\tstd::vector<int> dxs = { px, px - 1, px + 1 };\n\t\tstd::vector<int> dys = { py, py - 1, py + 1 };\n\t\tfor (const int dx : dxs) {\n\t\t\tif (hasHit) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfor (const int dy : dys) {\n\t\t\t\tif (hasHit) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// Ignore center pixel, already tested.\n\t\t\t\tif (dx == px && dy == py) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\thasHit = hitTest(dx, dy, hit);\n\t\t\t}\n\t\t}\n\t\treturn hasHit;\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/MeshTexturing.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n#include <core/graphics/Image.hpp>\n#include <core/graphics/Mesh.hpp>\n#include <core/assets/InputCamera.hpp>\n#include \"core/raycaster/Raycaster.hpp\"\n\n\nnamespace sibr {\n\n\t/** \\brief Reproject images onto a mesh using the associated camera poses, \n\t * and accumulate colors in UV-space to generate a texture map.\n\t * \\ingroup sibr_imgproc\n\t */\n\tclass SIBR_IMGPROC_EXPORT MeshTexturing\n\t{\n\tpublic:\n\n\t\t/** \\brief Export options\n\t\t */\n\t\tenum Options : uint {\n\t\t\tNONE = 0,\n\t\t\tFLIP_VERTICAL = 1, ///< Flip the final result.\n\t\t\tFLOOD_FILL = 2, ///< Perform flood filling.\n\t\t\tPOISSON_FILL = 4 ///< Perform poisson filling (slow).\n\t\t};\n\n\t\t/** Constructor.\n\t\t* \\param sideSize dimension of the texture\n\t\t*/\n\t\tMeshTexturing(unsigned int sideSize);\n\n\t\t/** Set the current mesh to texture.\n\t\t * \\param mesh the mesh to use.\n\t\t * \\warning The mesh MUST have texcoords.\n\t\t * \\note If the mesh has no normals, they will be computed.\n\t\t */\n\t\tvoid setMesh(const sibr::Mesh::Ptr mesh);\n\n\t\t/** Reproject a set of images into the texture map, using the associated cameras.\n\t\t* \\param cameras the cameras poses\n\t\t* \\param images the images to reproject\n\t\t*/\n\t\tvoid reproject(const std::vector<InputCamera::Ptr> & cameras, const std::vector<sibr::ImageRGB::Ptr> & images, const float sampleRatio = 1.0);\n\n\t\t/** Get the final result. \n\t\t* \\param options the options to apply to the generated texture map.\n\t\t*/\n\t\tsibr::ImageRGB::Ptr getTexture(uint options = NONE) const;\n\n\t\t/** Performs flood fill of an image, following a mask.\n\t\t* \\param image the image to fill\n\t\t* \\param mask mask where the zeros regions will be filled\n\t\t* \\return the filled image.\n\t\t*/\n\t\ttemplate<typename T_Type, unsigned int T_NumComp>\n\t\tstatic typename Image< T_Type, T_NumComp>::Ptr floodFill(const Image<T_Type, T_NumComp> & image, const sibr::ImageL8 & mask) {\n\n\t\t\ttypename Image< T_Type, T_NumComp>::Ptr filled(new Image< T_Type, T_NumComp>(image.w(), image.h()));\n\n\t\t\tSIBR_LOG << \"[Texturing] Flood filling...\" << std::endl;\n\t\t\t// Perform filling.\n\t\t\t// We need the empty pixels marked as non zeros, and the filled marked as zeros.\n\t\t\tcv::Mat1b flipMask = mask.toOpenCV().clone();\n\t\t\tflipMask = 255 - flipMask;\n\t\t\tcv::Mat1f dummyDist(flipMask.rows, flipMask.cols, 0.0f);\n\t\t\tcv::Mat1i labels(flipMask.rows, flipMask.cols, 0);\n\n\t\t\t// Run distance transform to obtain the IDs.\n\t\t\tcv::distanceTransform(flipMask, dummyDist, labels, cv::DIST_L2, cv::DIST_MASK_5, cv::DIST_LABEL_PIXEL);\n\n\t\t\t// Build a pixel ID to source pixel table, using the pixels in the mask.\n\t\t\tconst sibr::Vector2i basePos(-1, -1);\n\t\t\tstd::vector<sibr::Vector2i> colorTable(flipMask.rows*flipMask.cols, basePos);\n#pragma omp parallel\n\t\t\tfor (int py = 0; py < flipMask.rows; ++py) {\n\t\t\t\tfor (int px = 0; px < flipMask.cols; ++px) {\n\t\t\t\t\tif (flipMask(py, px) != 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst int label = labels(py, px);\n\t\t\t\t\tcolorTable[label] = { px,py };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Now we can turn the label image into a color image again.\n#pragma omp parallel\n\t\t\tfor (int py = 0; py < flipMask.rows; ++py) {\n\t\t\t\tfor (int px = 0; px < flipMask.cols; ++px) {\n\t\t\t\t\t// Don't touch existing pixels.\n\t\t\t\t\tif (flipMask(py, px) == 0) {\n\t\t\t\t\t\tfilled(px, py) = image(px, py);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst int label = labels(py, px);\n\t\t\t\t\tfilled(px, py) = image(colorTable[label]);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn filled;\n\t\t}\n\n\t\t/** Performs poisson fill of an image, following a mask.\n\t\t* \\param image the image to fill\n\t\t* \\param mask mask where the zeros regions will be filled\n\t\t* \\return the filled image.\n\t\t* \\warning This is slow for large images (>8k).\n\t\t*/\n\t\tstatic sibr::ImageRGB32F::Ptr poissonFill(const sibr::ImageRGB32F & image, const sibr::ImageL8 & mask);\n\n\tprivate:\n\n\t\t/** Test if the UV-space mesh covers a pixel of the texture map.\n\t\t* \\param px pixel x coordinate\n\t\t* \\param py pixel y coordinate\n\t\t* \\param finalHit the hit information if there is coverage\n\t\t* \\return true if there is coverage.\n\t\t*/\n\t\tbool hitTest(int px, int py, RayHit & finalHit);\n\n\t\t/** Test if the UV-space mesh approximately covers a pixel of the texture map, by sampling a neighborhood in uv-space.\n\t\t* \\param px pixel x coordinate\n\t\t* \\param py pixel y coordinate\n\t\t* \\param hit the hit information if there is coverage\n\t\t* \\return true if there is coverage.\n\t\t*/\n\t\tbool sampleNeighborhood(int px, int py, RayHit& hit);\n\n\t\t/** Compute the interpolated position and normal at the intersection point on the initial mesh.\n\t\t* \\param hit the intersection information\n\t\t* \\param vertex will contain the interpolated position\n\t\t* \\param normal will contain the interpolated normal\n\t\t*/\n\t\tvoid interpolate(const sibr::RayHit & hit, sibr::Vector3f & vertex, sibr::Vector3f & normal) const;\n\n\t\tsibr::ImageRGB32F _accum; ///< Color accumulator.\n\t\tsibr::ImageL8 _mask; ///< Mask indicating which regions of the texture map have been covered.\n\n\t\tsibr::Mesh::Ptr _mesh; ///< The original world-space mesh.\n\t\tsibr::Raycaster _worldRaycaster; ///< The world-space mesh raycaster.\n\t\tsibr::Raycaster _uvsRaycaster; ///< The uv-space mesh raycaster.\n\n\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/PoissonReconstruction.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"PoissonReconstruction.hpp\"\n#include <queue>   \n#include <Eigen/Sparse>\n\n\nnamespace sibr {\n\n\n\nPoissonReconstruction::PoissonReconstruction(\n\tconst cv::Mat3f & gradientsX,\n\tconst cv::Mat3f & gradientsY,\n\tconst cv::Mat1f & mask,\n\tconst cv::Mat3f & img_target)\n{\n\t// Make a copy of the target as we are going to modify it.\n\t_img_target = img_target.clone();\n\t_gradientsX = gradientsX;\n\t_gradientsY = gradientsY;\n\t_mask = mask;\n\t\n}\n\nvoid PoissonReconstruction::solve(void)\n{\n\tparseMask();\n\n\t//solve Ai X=bi , Ai = A : coefs , bi : b_terms , i for each RGB\n\tstd::vector< Eigen::Triplet<double> >  coefs;\n\tstd::vector<Eigen::VectorXd> b_terms;\n\n\tfor (int k = 0; k < 3; ++k) {\n\t\tb_terms.push_back(Eigen::VectorXd::Zero(_pixels.size()));\n\t}\n\n\tfor ( int p=0; p<(int)_pixels.size(); p++ ) { \n\t\tsibr::Vector2i pos(_pixels[p]);\n\t\tstd::vector< sibr::Vector2i >  nPos ( getNeighbors(pos, _img_target.cols, _img_target.rows ));\n\t\tint num_neighbors = 0;\n\t\tcv::Vec3f new_term(0, 0, 0);\n\n\t\tfor( int n_id = 0; n_id<nPos.size(); n_id++){ \n\t\t\tsibr::Vector2i npos(nPos[n_id]);\n\n\t\t\tint nId = _pixelsId[npos.x() + _mask.cols * npos.y()];\n\t\t\tif( nId < -1 ) { continue; }\n\t\t\t++num_neighbors;\t\n\n\t\t\tif( isInMask(npos) ) { //pair inside mask\n\t\t\t\tif(nId < 0 ) { std::cerr << \"#\"; }\n\t\t\t\tcoefs.push_back(Eigen::Triplet<double>(p,nId,-1));\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t// Four possibilities:\n\t\t\t\tif(npos.x() > pos.x()){ // right pixel\n\t\t\t\t\tnew_term -= _gradientsY.at<cv::Vec3f>(pos.y(), pos.x());\n\t\t\t\t} else if (npos.x() < pos.x()){ // left pixel\n\t\t\t\t\tnew_term += _gradientsY.at<cv::Vec3f>(npos.y(), npos.x());\n\t\t\t\t} else if (npos.y() > pos.y()){ // bottom pixel\n\t\t\t\t\tnew_term -= _gradientsX.at<cv::Vec3f>(pos.y(), pos.x());\n\t\t\t\t} else if(npos.y() < pos.y()){ // top pixel\n\t\t\t\t\tnew_term += _gradientsX.at<cv::Vec3f>(npos.y(), npos.x());\n\t\t\t\t} \n\n\t\t\t} else if(!isIgnored(npos)) { //boundary\n\t\t\t\tnew_term += _img_target.at<cv::Vec3f>(npos.y(),npos.x()); // color of target\n\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\tcoefs.push_back(Eigen::Triplet<double>(p,p,(double)num_neighbors)); \n\n\t\tfor (int k = 0; k < 3; ++k) {\n\t\t\tb_terms[k](p) = new_term(k);\n\t\t}\n\t\t\t\n\t}\n\n\tEigen::SparseMatrix<double> A((int)_pixels.size(),(int)_pixels.size());\n\tA.setFromTriplets(coefs.begin(),coefs.end());\n\t\n\tstd::vector<Eigen::VectorXd> solutions;\n\tEigen::SimplicialLDLT< Eigen::SparseMatrix<double> > eigenSolver;\n\n\teigenSolver.compute(A);\n\n\tif(eigenSolver.info()!=Eigen::Success) {\n\t\tstd::cerr << \"decomp = failure\" <<std::endl;\n\t\treturn;\n\t} \n\n\tfor (int k = 0; k < 3; ++k) {\n\t\tsolutions.push_back(eigenSolver.solve(b_terms[k]));\n\t\tif (eigenSolver.info() != Eigen::Success) {\n\t\t\tstd::cerr << \"decomp = failure\" << std::endl;\n\t\t}\n\n\t\tfloat error = (float)(A*solutions[k] - b_terms[k]).squaredNorm();\n\t\tif (error > 1) {\n\t\t\tstd::cerr << \"distance to solution: \" << error << std::endl;\n\t\t}\n\t}\n\n\tfor (int p = 0; p<(int)_pixels.size(); p++) {\n\t\tsibr::Vector2i pos(_pixels[p]);\n\t\tcv::Vec3f color;\n\t\tfor (int k = 0; k < 3; ++k) {\n\t\t\tcolor(k) = std::min(1.0f, std::max((float)solutions[k][p], 0.0f));\n\t\t}\n\t\t_img_target.at<cv::Vec3f>(pos.y(), pos.x()) = color;\n\t}\n\n\tpostProcessing();\n\tpostProcessing();\n\t\n}\n\nvoid PoissonReconstruction::parseMask( void )\n{\n\t_pixels.resize(0);\n\t_boundaryPixels.resize(0);\n\t_pixelsId.resize(_mask.rows*_mask.cols,-2);\n\n\t//std::cerr << \"size : \" <<  _mask.cols << \" x \" << _mask.rows << std::endl;\n\t\n\t//first find boundaries\n\tfor( int j=0; j<(int)_mask.rows; j++ ) {\n\t\tfor( int i=0; i<(int)_mask.cols; i++) { \n\t\t\tsibr::Vector2i pos(i,j);\n\n\t\t\tif(isIgnored(pos)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif( !isInMask(pos) ) { \n\t\t\t\tstd::vector< sibr::Vector2i > neighbors = getNeighbors(pos, _img_target.cols, _img_target.rows);\n\t\t\t\tfor( int n_id = 0; n_id<neighbors.size(); n_id++){ //if at least one neighbor is in mask, considered as boundary\n\t\t\t\t\tsibr::Vector2i npos(neighbors[n_id]);\n\t\t\t\t\tif( isInMask(npos) && !isIgnored(npos)) {\n\t\t\t\t\t\t_pixelsId[i+_mask.cols*j] = -1;\n\t\t\t\t\t\t_boundaryPixels.push_back(pos);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_pixelsId[i+_mask.cols*j] = 0;\n\t\t\t}\n\n\t\t}\n\t}\n\n\tcheckConnectivity();\n\n\t//then find all valid pixels to edit\n\tfor( int i=0; i<(int)_mask.cols; i++) { \n\t\tfor( int j=0; j<(int)_mask.rows; j++ ) {\n\t\t\tsibr::Vector2i pos(i, j);\n\t\t\tif( isIgnored(pos) ||  _pixelsId[i+_mask.cols*j] < 0 ) { \n\t\t\t\tcontinue;\n\t\t\t} else {\t\n\t\t\t\t_pixelsId[i+_mask.cols*j] = (int)_pixels.size();\n\t\t\t\t_pixels.push_back(sibr::Vector2i(i,j));\t\t\t\t\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n\nstd::vector< sibr::Vector2i > PoissonReconstruction::getNeighbors( sibr::Vector2i pos, int width, int height )\n{\n\tstd::vector< sibr::Vector2i > output;\n\tint offset_list_4[4][2] = {\t{0,1},{0,-1},{1,0},{-1,0} };\n\n\tfor( int i=0; i<4; i++){\n\t\tsibr::Vector2i n_pos( pos[0]+offset_list_4[i][0], pos[1]+offset_list_4[i][1]);\n\t\tint x = n_pos.x();\n\t\tint y = n_pos.y();\n\t\tif( x>=0 && x< width && y>=0 && y< height) {\n\t\t\toutput.push_back(n_pos);\n\t\t}\n\t}\n\treturn output;\n}\n\nvoid PoissonReconstruction::computeGradients(const cv::Mat3f& src, cv::Mat3f& gradX, cv::Mat3f& gradY) {\n\tgradX = cv::Mat3f(src.size());\n\tgradY = cv::Mat3f(src.size());\n\tfor (int i = 0; i < src.rows; ++i) {\n\t\tfor (int j = 0; j < src.cols; ++j) {\n\t\t\t// Compute forward differences.\n\t\t\tconst int ip = std::min(i + 1, src.rows - 1);\n\t\t\tconst int jp = std::min(j + 1, src.cols - 1);\n\t\t\t\n\t\t\tconst cv::Vec3f c = src.at<cv::Vec3f>(i, j);\n\t\t\tconst cv::Vec3f d = src.at<cv::Vec3f>(ip, j);\n\t\t\tconst cv::Vec3f r = src.at<cv::Vec3f>(i, jp);\n\t\t\tconst cv::Vec3f dX = d - c;\n\t\t\tconst cv::Vec3f dY = r - c;\n\t\t\tgradX.at<cv::Vec3f>(i, j) = dX;\n\t\t\tgradY.at<cv::Vec3f>(i, j) = dY;\n\t\t}\n\t}\n}\n\nvoid PoissonReconstruction::checkConnectivity( void )\n{\n\t// R(x) : 0 -> not connected to boundary, 1 -> connected, G(y) : 0 -> not checked, 1 -> checked\n\tsibr::ImageRGB connectivity( _mask.cols , _mask.rows);\n\t\n\tstd::queue<sibr::Vector2i> pixelsToCheck;\n\tfor(int p=0; p<(int)_boundaryPixels.size(); p++){\n\t\tsibr::Vector2i pos(_boundaryPixels[p]);\n\t\tpixelsToCheck.push(pos);\n\t\tconnectivity(pos.x(),pos.y()).x() = 1; //boundaries are connected to boundaries\n\t}\n\n\t//propagate connectivity\n\twhile(pixelsToCheck.size()>0){\n\t\tsibr::Vector2i pos(pixelsToCheck.front()); \n\t\tpixelsToCheck.pop();\n\t\tconnectivity(pos.x(),pos.y()).y() = 1;\n\n\t\tstd::vector<sibr::Vector2i> neighbors( getNeighbors(pos, _img_target.cols, _img_target.rows) );\n\t\tfor(int n_id=0; n_id<(int)neighbors.size(); n_id++){\n\t\t\tsibr::Vector2i npos(neighbors[n_id]); \n\t\t\tif( connectivity(npos.x(),npos.y()).y()==1 || isIgnored(npos)) { \n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tconnectivity(npos.x(),npos.y()).x() = 1;\n\t\t\t\tconnectivity(npos.x(),npos.y()).y() = 1;\n\t\t\t\tpixelsToCheck.push( npos );\n\t\t\t}\n\t\t}\n\t}\n\n\t//discard non connected pixel\n\tfor(int i=0; i<(int)_mask.cols; i++){\n\t\tfor(int j=0; j<(int)_mask.rows; j++){\n\t\t\tif( connectivity(i,j).x() == 0 ) {\n\t\t\t\t_pixelsId[i + _mask.cols * j] = -2;\n\t\t\t\tsibr::Vector2i coords(i,j);\n\t\t\t\tif( isInMask(coords) && !isIgnored(coords) ) {\n\t\t\t\t\t_img_target.at<cv::Vec3f>(j, i) = cv::Vec3f(0.0f,0.0f,0.0f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n\nvoid PoissonReconstruction::postProcessing(void)\n{\n\t//std::cerr << \"[PoissonRecons] Post Processing\" << std::endl;\n\t/*\n\t * for( int j=0; j<(int)_mask.rows; j++ ) {\n\t\tfor( int i=0; i<(int)_mask.cols; i++) { \n\t\t\tsibr::Vector2i pos(i,j);\n\t\t\tif( !isInMask(pos) ) { \n\t */\n#pragma omp parallel for\n\tfor (int j = 0; j < (int)_mask.rows; j++) {\n\t\tfor (int i = 0; i < (int)_mask.cols; i++) {\n\t\t\t// mask: 0 -> in reconstruction, 1 -> keep fixed.\n\t\t\t//return (_mask.at<float>(pos.y(), pos.x())<0.5);\n\t\t\tif (std::abs(_mask.at<float>(j,i)) < 0.5f && cv::norm(_img_target.at<cv::Vec3f>(j,i)) == 0.0f) {\n\t\t\t\tstd::vector<sibr::Vector2i> neighbors(getNeighbors(sibr::Vector2i(i, j), _mask.cols, _mask.rows));\n\t\t\t\tstd::vector<bool> neighIsBlack(neighbors.size(), false);\n\t\t\t\tint black_neighbor = false;\n\t\t\t\tfor (uint n_id = 0; n_id < neighbors.size() && !black_neighbor; n_id++) {\n\t\t\t\t\tsibr::Vector2i npos(neighbors[n_id]);\n\t\t\t\t\tif (cv::norm(_img_target.at<cv::Vec3f>(npos.y(), npos.x())) == 0.0f) {\n\t\t\t\t\t\tneighIsBlack[n_id] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!black_neighbor && neighbors.size() > 0) {\n\t\t\t\t\tcv::Vec3f new_color(0, 0, 0);\n\t\t\t\t\tint count = 0;\n\t\t\t\t\tfor (uint n_id = 0; n_id < neighbors.size(); n_id++) {\n\t\t\t\t\t\tif(neighIsBlack[n_id]) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsibr::Vector2i npos(neighbors[n_id]);\n\t\t\t\t\t\tnew_color += _img_target.at<cv::Vec3f>(npos.y(), npos.x());\n\t\t\t\t\t\t++count;\n\t\t\t\t\t}\n\t\t\t\t\t_img_target.at<cv::Vec3f>(j,i) = ((1.0f / (float)count)*new_color);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool PoissonReconstruction::isInMask( sibr::Vector2i & pos)\n{\n\tconst float maskVal = _mask.at<float>(pos.y(), pos.x());\n\treturn (std::abs(maskVal) < 0.5f);\n}\n\nbool PoissonReconstruction::isIgnored(sibr::Vector2i & pos)\n{\n\treturn (_mask.at<float>(pos.y(), pos.x()) <= -0.5f);\n}\n\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/PoissonReconstruction.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n#include <core/graphics/Image.hpp>\n\n\nnamespace sibr {\n\n\t/** \\brief Performs gradient integration for tasks such as Poisson-based inpainting, smooth filling, ...\n\t * See the constructor for additional details.\n\t * \\ingroup sibr_imgproc\n\t */\n\tclass SIBR_IMGPROC_EXPORT PoissonReconstruction\n\t{\n\tpublic:\n\n\t\t/** Initialize reconstructor for a given problem. Gradients and target are expected to be RGB32F, mask is L32F.\n\t\t  In the mask, pixels with value = 0 are to be inpainted, value > 0.5 are pixels to be used as source/constraint,  value < -0.5 are pixels to be left unchanged and unused.\n\t\t  To compute the gradients from an image, prefer using PoissonReconstruction::computeGradients (weird results have been observed when using cv::Sobel and similar).\n\t\t\\param gradientsX the RGB32F horizontal color gradients to integrate along\n\t\t\\param gradientsY the RGB32F vertical color gradients to integrate along\n\t\t\\param mask the L32F mask denoting how each pixel should be treated. \n\t\t\\param img_target the RGB32 image to use as a source constraint (will be copied internally)\n\t\t**/\n\t\tPoissonReconstruction(\n\t\t\tconst cv::Mat3f & gradientsX,\n\t\t\tconst cv::Mat3f & gradientsY,\n\t\t\tconst cv::Mat1f & mask,\n\t\t\tconst cv::Mat3f & img_target\n\t\t);\n\n\t\t/** Solve the reconstruction problem. */\n\t\tvoid solve(void);\n\n\t\t/** \\return the result of the reconstruction */\n\t\tcv::Mat result() const { return _img_target; }\n\n\t\t/** helper to get the pixel coordinates of valid pixels for agiven pixel and image size.\n\t\t *\\param pos the central pixel position\n\t\t *\\param width number of columns/width\n\t\t *\\param height number of rows/height\n\t\t *\\return a vector containing neighboring pixels coordinates.\n\t\t */\n\t\tstatic std::vector< sibr::Vector2i > getNeighbors(sibr::Vector2i pos, int width, int height);\n\n\t\t/** Compute the gradients of an RGB32F matrix using forward finite differences.\n\t\t *\\param src the matrix to compute the gradients of\n\t\t *\\param gradX will contain the horizontal gradients\n\t\t *\\param gradY will contain the vertical gradients\n\t\t */\n\t\tstatic void computeGradients(const cv::Mat3f & src, cv::Mat3f & gradX, cv::Mat3f & gradY);\n\t\t\n\tprivate:\n\t\tcv::Mat _img_target; ///< Main image.\n\t\tcv::Mat _gradientsX; ///< Gradients.\n\t\tcv::Mat _gradientsY; ///< Gradients.\n\t\tcv::Mat _mask; ///< Mask guide.\n\n\t\tstd::vector<sibr::Vector2i> _pixels; ///< list of valid pixels.\n\t\tstd::vector<sibr::Vector2i> _boundaryPixels; ///< List of boundary pixels.\n\t\tstd::vector<int > _pixelsId; ///< Pixel IDs list.\n\t\tstd::vector<std::vector<int> > _neighborMap; ///< Each pixel valid neighbors.\n\n\t\t/** Parse the mask and the additional label condition into a list of pixels to modified and boundaries conditions. */\n\t\tvoid parseMask(void);\n\n\t\t/** Make sure that every modified pixel is connected to some boundary condition, all non connected pixels are discarded. */\n\t\tvoid checkConnectivity(void);\n\n\t\t/** Heuristic to fill isolated black pixels. */\n\t\tvoid postProcessing(void);\n\n\t\t/** Are we in the mask (ie mask==0). \n\t\t\\param pos the pixel to test for\n\t\t\\return true if mask(pix) == 0\n\t\t*/\n\t\tbool isInMask(sibr::Vector2i & pos);\n\n\t\t/* Are we ignored (ie mask==-1).\n\t\t\\param pos the pixel to test for\n\t\t\\return true if mask(pix) == -1\n\t\t*/\n\t\tbool isIgnored(sibr::Vector2i & pos);\n\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/imgproc/sibr_imgproc.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_imgproc sibr_imgproc\n\n\t\\brief Image processing utilities.\n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nproject(sibr_raycaster)\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\r\nsource_group(\"Source Files\" FILES ${SOURCES})\r\n\r\n## Specify target rules\r\nadd_library(${PROJECT_NAME} SHARED ${SOURCES} ${LIBS_SOURCES})\r\n\r\ninclude_directories(\r\n\t${Boost_INCLUDE_DIRS}\r\n\t${nanoflann_INCLUDE_DIRS}\r\n)\r\nif(WIN32)\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\tOpenMP::OpenMP_CXX\r\n\tembree3\r\n\tsibr_graphics\r\n\tsibr_assets\r\n\tnanoflann\r\n)\r\nelse()\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\tOpenMP::OpenMP_CXX\r\n\tembree\r\n# CLUSTER\r\n#\t/data/graphdeco/share/tbb/lib64/libtbb.so\r\n\tsibr_graphics\r\n\tsibr_assets\r\n\tnanoflann\r\n)\r\nendif()\r\n\r\nadd_definitions( -DSIBR_RAYCASTER_EXPORTS -DBOOST_ALL_DYN_LINK  )\r\n\r\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\r\n\r\n## High level macro to install in an homogen way all our ibr targets\r\ninclude(install_runtime)\r\nibr_install_target(${PROJECT_NAME}\r\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\r\n)\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/CameraRaycaster.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <boost/filesystem/path.hpp>\n#include <core/system/Vector.hpp>\n#include \"core/raycaster/CameraRaycaster.hpp\"\n\n\nnamespace sibr\n{\n\n\t/*static*/ void\tCameraRaycaster::computePixelDerivatives( const sibr::InputCamera& cam, \n\t\tsibr::Vector3f& dx, sibr::Vector3f& dy, sibr::Vector3f& upLeftOffset )\n\t{\n\t\tsibr::Vector3f dir = cam.dir();\n\t\tsibr::Vector3f up = cam.up();\n\t\tfloat aspect = cam.aspect();\n\n\t\tsibr::Vector2f screenWorldSize;\n\t\t{ \n\t\t\t// screenWorldSize.y = 2*tan(fov/2) because screenDist = 1 (indeed\n\t\t\t// we use normalized cam.dir() to build this derivative)\n\t\t\tfloat heightWorldSize = 2.f*tanf(cam.fovy()/2.f);\n\t\t\tscreenWorldSize = sibr::Vector2f( heightWorldSize*aspect, heightWorldSize ); \n\t\t}\n\n\t\tsibr::Vector3f right = cross(cam.dir(), up);\n\t\tsibr::Vector3f rowSize = right*screenWorldSize[0];\n\t\tsibr::Vector3f colSize = -up*screenWorldSize[1];\n\n\t\tdx = rowSize / (float)cam.w();\n\t\tdy = colSize / (float)cam.h();\n\n\t\tupLeftOffset = dir - rowSize/2.f - colSize/2.f;\n\t\t//upLeftOffset = upLeftOffset + dx/2.f + dy/2.f;\t// Used to start from the center of a pixel\n\t\tupLeftOffset += cam.position();\n\t}\n\n\tvoid CameraRaycaster::computeClippingPlanes(const sibr::Mesh & mesh, std::vector<InputCamera::Ptr>& cams, std::vector<sibr::Vector2f> & nearsFars)\n\t{\n\t\t\n\t\tnearsFars.clear();\n\t\tsibr::Raycaster raycaster;\n\t\traycaster.init();\n\t\tsibr::Mesh::Ptr localMesh = mesh.invertedFacesMesh2();\n\t\traycaster.addMesh(*localMesh);\n\t\tSIBR_LOG << \" [CameraRaycaster] computeAutoClippingPlanes() : \" << std::flush;\n\n\t\tint deltaPix = 15;\n\n\t\tnearsFars.resize(cams.size());\n\n\t\t#pragma omp parallel for\n\t\tfor (int cam_id = 0; cam_id < (int)cams.size(); ++cam_id) {\n\t\t\tsibr::InputCamera & cam = *cams[cam_id];\n\n\t\t\tsibr::Vector3f dx, dy, upLeftOffset;\n\t\t\tsibr::CameraRaycaster::computePixelDerivatives(cam, dx, dy, upLeftOffset);\n\t\t\tsibr::Vector3f camZaxis = cam.dir().normalized();\n\t\t\tfloat maxD = -1.0f, minD = -1.0f;\n\n\t\t\tfor (int i = 0; i < (int)cam.h(); i += deltaPix) {\n\t\t\t\tfor (int j = 0; j < (int)cam.w(); j += deltaPix) {\n\t\t\t\t\tsibr::Vector3f worldPos = ((float)j + 0.5f)*dx + ((float)i + 0.5f)*dy + upLeftOffset;\n\t\t\t\t\tsibr::Vector3f dir = (worldPos - cam.position()).normalized();\n\n\t\t\t\t\tsibr::RayHit hit = raycaster.intersect(sibr::Ray(cam.position(), dir));\n\n\t\t\t\t\tif (!hit.hitSomething()) { continue; }\n\n\t\t\t\t\tfloat dist = hit.dist();\n\n\t\t\t\t\tfloat clipDist = dist * std::abs(dir.dot(camZaxis));\n\n\t\t\t\t\tmaxD = (maxD<0 || clipDist > maxD ? clipDist : maxD);\n\t\t\t\t\tminD = (minD<0 || clipDist < minD ? clipDist : minD);\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tfloat znear = 0.5f*minD;\n\t\t\tfloat zfar = 2.0f*maxD;\n\n\t\t\twhile (zfar / znear < 100.0f) {\n\t\t\t\tzfar *= 1.1f;\n\t\t\t\tznear *= 0.9f;\n\t\t\t}\n\n\t\t\tcam.znear(znear);\n\t\t\tcam.zfar(zfar);\n\n\t\t\tnearsFars[cam_id] = sibr::Vector2f(znear, zfar);\n\n\t\t\tstd::cout << cam_id << \" \" << std::flush;\n\t\t}\n\t\tstd::cout << \" done.\" << std::endl;\n\n\t\t\n\t}\n\n\n\tsibr::Vector3f CameraRaycaster::computeRayDir( const sibr::InputCamera& cam, const sibr::Vector2f & pixel )\n\t{\n\t\tsibr::Vector3f dx, dy, upLeftOffset;\n\t\tCameraRaycaster::computePixelDerivatives(cam, dx, dy, upLeftOffset);\n\n\t\tsibr::Vector3f worldPos = pixel.x()*dx + pixel.y()*dy + upLeftOffset; //at dist 1 from cam center\n\t\treturn (worldPos - cam.position()).normalized();\n\t}\n\n\tbool\tCameraRaycaster::init( void )\n\t{\n\t\treturn _raycaster.init();\n\t}\n\n\tvoid\tCameraRaycaster::addMesh( const sibr::Mesh& mesh )\n\t{\n\t\t_raycaster.addMesh(mesh);\n\t}\n\n\tvoid\tCameraRaycaster::castForEachPixel( const sibr::InputCamera& cam, ICameraRaycasterProcessor* processors[], uint nbProcessors, const std::string& optLogMsg )\n\t{\n\t\t//SIBR_PROFILESCOPE;\n\n\t\t// Check there is no NULL process\n\t\tfor (uint i = 0; i < nbProcessors; ++i)\n\t\t\tif (processors[i] == nullptr)\n\t\t\tSIBR_ERR << \"camera-raycaster process NULL detected\" << std::endl;\n\n\t\tsibr::Vector3f dx, dy, upLeftOffset;\n\t\tCameraRaycaster::computePixelDerivatives(cam, dx, dy, upLeftOffset);\n\n\t\t//sibr::LoadingProgress\tprogress(cam.w()*cam.h(), optLogMsg);\n\t\t(void)optLogMsg;\n\n\t\t// For each pixel of the camera's image\n\t\tfor (uint py = 0; py < cam.h(); ++py)\n\t\t{\n\t\t\tfor (uint px = 0; px < cam.w(); ++px)\n\t\t\t{ \n\t\t\t\t//progress.walk();\n\t\t\t\tsibr::Vector3f worldPos = (float)px*dx + (float)py*dy + upLeftOffset;\n\t\t\t\t// Cast a ray\n\t\t\t\tsibr::Vector3f dir =  worldPos - cam.position();\n\t\t\t\tRayHit hit = _raycaster.intersect(Ray( cam.position(), dir));\n\n\t\t\t\tfor (uint i = 0; i < nbProcessors; ++i)\n\t\t\t\t\tprocessors[i]->onCast(px, py, hit);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tRaycastingCamera::RaycastingCamera(const sibr::InputCamera & cam) : sibr::InputCamera(cam) {\n\t\tCameraRaycaster::computePixelDerivatives(*this, dx, dy, upLeftOffsetMinusPos);\n\t\tupLeftOffsetMinusPos -= position();\n\n\t\tstd::vector<sibr::Vector2f> corners = {\n\t\t\t{-1,-1}, {-1, 1}, {1, 1}, {1, -1}\n\t\t};\n\t\tstd::vector<sibr::Vector3f> pts_near, pts_far;\n\t\tfor (const auto & c : corners) {\n\t\t\tpts_near.push_back(unproject({ c[0], c[1], -1 }));\n\t\t\tpts_far.push_back(unproject({ c[0], c[1], +1 }));\n\t\t}\n\n\t\tfrustum_planes = {\n\t\t\t//HPlane::Through(pts_near[0], pts_near[3], pts_near[2]), // near_plane,\n\t\t\tHPlane::Through(pts_far[0], pts_far[2], pts_far[3]),\t// far_plane\n\t\t\tHPlane::Through(pts_near[2], pts_far[2], pts_far[1]),\t// top_plane, \n\t\t\tHPlane::Through(pts_near[3], pts_near[0], pts_far[3]),\t// bottom_plane, \n\t\t\tHPlane::Through(pts_far[0], pts_near[0], pts_far[1]),\t// left_plane\n\t\t\tHPlane::Through(pts_near[3], pts_far[3], pts_far[2])\t// right_plane;\n\t\t};\n\n\t\t//sibr::Vector3f pt = unproject({ 0, 0, 0 });\n\t\t//std::cout << \" debug planes : \";\n\t\t//for (uint i = 0; i < frustum_planes.size(); ++i) {\n\t\t//\tstd::cout << sibr::Vector4f(pt[0], pt[1], pt[2], 1).dot(frustum_planes[i].coeffs()) << \" \";\n\t\t//}\n\t\t//std::cout << std::endl;\n\n\t}\n\n\tsibr::Vector3f RaycastingCamera::rayDirNotNormalized(const sibr::Vector2f & pixel) const\n\t{\n\t\treturn pixel.x()*dx + pixel.y()*dy + upLeftOffsetMinusPos;\n\t}\n\n\tsibr::Vector3f RaycastingCamera::rayDir(const sibr::Vector2f & pixel) const\n\t{\n\t\treturn rayDirNotNormalized(pixel).normalized();\n\t}\n\n\tRay RaycastingCamera::getRay(const sibr::Vector2f & pixel) const\n\t{\n\t\treturn Ray(position(), rayDir(pixel));\n\t}\n\n\tsibr::Vector2f RaycastingCamera::rayProjection(const Line3 & line) const\n\t{\n\t\tsibr::Vector2f out(-1, -1);\n\t\tuint id = 0;\n\t\tif (isInsideFrustum(line.origin())) {\n\t\t\tout[id] = 0;\n\t\t\t++id;\n\t\t}\n\n\t\tstd::vector<float> intersection_params;\n\t\tintersection_params.reserve(frustum_planes.size());\n\n\t\tfor (uint i = 0; i < frustum_planes.size(); ++i) {\n\t\t\tfloat param = line.intersectionParameter(frustum_planes[i]);\n\t\t\tif (param >= 0) {\n\t\t\t\tintersection_params.push_back(param);\n\t\t\t}\n\t\t}\n\n\t\tstd::sort(intersection_params.begin(), intersection_params.end());\n\t\tfor (float t : intersection_params) {\n\t\t\tif (isInsideFrustum(line.pointAt(t))) {\n\t\t\t\tout[id] = t;\n\t\t\t\tif (id == 1) {\n\t\t\t\t\treturn out;\n\t\t\t\t}\n\t\t\t\t++id;\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\tbool RaycastingCamera::isInsideFrustum(const sibr::Vector3f & pt, float eps) const\n\t{\n\t\tfor (uint i = 0; i < frustum_planes.size(); ++i) {\n\t\t\tif (sibr::Vector4f(pt[0], pt[1], pt[2], 1).dot(frustum_planes[i].coeffs()) < -eps) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tsibr::Vector2f RaycastingCamera::projectImg_outside_frustum_correction(const Vector3f & pt3d) const\n\t{\n\t\tsibr::Vector3f pos2dGL = project(pt3d);\n\n\t\tif ((pt3d - position()).dot(dir()) < 0) {\n\t\t\tpos2dGL.x() = -pos2dGL.x();\n\t\t} else {\n\t\t\tpos2dGL.y() = -pos2dGL.y();\n\t\t}\n\t\treturn 0.5f*(pos2dGL.xy() + sibr::Vector2f(1, 1)).cwiseProduct(sibr::Vector2f(w(), h()));\n\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/CameraRaycaster.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <array>\n# include <core/graphics/Image.hpp>\n# include <core/assets/InputCamera.hpp>\n# include \"core/raycaster/Config.hpp\"\n# include \"core/raycaster/Raycaster.hpp\"\n\nnamespace sibr\n{\n\n\t/** Used to process casted rays from image pixels. Implement\n\t this interface and write your custom behavior.\n\t (e.g. see CameraRaycasterProcessor.hpp for built-in processor)\n\t \\ingroup sibr_raycaster\n\t*/\n\tclass SIBR_RAYCASTER_EXPORT ICameraRaycasterProcessor\n\t{\n\tpublic:\n\n\t\t/// Destructor.\n\t\tvirtual ~ICameraRaycasterProcessor( void ) {}\n\n\t\t/** Called for each casted ray (that hit or not).\n\t\t\\param px pixel source pixel x coordinate\n\t\t\\param py pixel source pixel y coordinate\n\t\t\\param hit the (potential) hit information\n\t\t*/\n\t\tvirtual void\tonCast( uint px, uint py, const RayHit& hit ) = 0;\n\n\t};\n\n\t/**  Used for casting each pixel of an image into a raycaster scene.\n\t \\ingroup sibr_raycaster\n\t*/\n\tclass SIBR_RAYCASTER_EXPORT CameraRaycaster\n\t{\n\tpublic:\n\n\t\t/// Constructor.\n\t\tCameraRaycaster( void ) { }\n\n\t\t/// Initialize (will be done when adding a mesh).\n\t\tbool\tinit( void );\n\n\t\t/** Add a mesh to the raycaster\n\t\t \\param mesh the mesh\n\t\t */\n\t\tvoid\taddMesh( const sibr::Mesh& mesh );\n\n\t\t/** For each image pixel, send a ray and compute data using the provided processors.\n\t\t\\param cam the source camera\n\t\t\\param processors a list of processors to call for each cast ray\n\t\t\\param nbProcessors the number of processors in the list\n\t\t\\param optLogMessage log message\n\t\t*/\n\t\tvoid\tcastForEachPixel( const sibr::InputCamera& cam, ICameraRaycasterProcessor* processors[], uint nbProcessors,\n\t\t\t\t\t\t\t\t\tconst std::string& optLogMessage=\"Executing camera raycasting\");\n\n\t\t/** This function returns the step (in both x- and y-coordinates) between each pixel in the world\n\t\t space. Thus, if go through each pixel of an can image but you need their 3d world position,\n\t\t using this function you can get it using:\n\t\t\t\tpixel3d = dx*pixel2d.x + dy*pixel2d.y + upLeftOffset\n\t\t where   dx is the step between each horizontal pixel,\n\t\t         dy is the step between each vertical pixel,\n\t\t\\param cam the source camera\n\t\t\\param dx will contain the horizontal step\n\t\t\\param dy will contain the vertical step\n\t\t\\param upLeftOffset will contain the 3D coordinates of the top-left pixel\n\t\t*/\n\t\tstatic void\tcomputePixelDerivatives( const sibr::InputCamera& cam, sibr::Vector3f& dx, sibr::Vector3f& dy, sibr::Vector3f& upLeftOffset );\n\n\t\t/**\tCompute the ray direction from the camera position to a given pixel.\n\t\t\\param cam the source camera\n\t\t\\param pixel the pixel in [0,w-1]x[0,h-1]\n\t\t\\return the ray direction from the camera position to the center of the input pixel.\n\t\t*/\n\t\tstatic sibr::Vector3f computeRayDir( const sibr::InputCamera& cam, const sibr::Vector2f & pixel );\n\n\t\t/** Estimate the clipping planes for a set of cameras so that the mesh is entirely visible in each camera.\n\t\t\\param mesh the mesh to visualize\n\t\t\\param cams the list of cameras\n\t\t\\param nearsFars will contain the near and far plane of each camera\n\t\t*/\n\t\tstatic void computeClippingPlanes(const sibr::Mesh & mesh, std::vector<InputCamera::Ptr>& cams, std::vector<sibr::Vector2f> & nearsFars);\n\n\t\t/// \\return the internal raycaster\n\t\tRaycaster&\t\t\traycaster( void )\t\t\t{ return _raycaster; }\n\t\t/// \\return the internal raycaster\n\t\tconst Raycaster&\traycaster( void ) const \t{ return _raycaster; }\n\n\tprivate:\n\n\t\tRaycaster\t\t\t\t\t\t\t\t\t_raycaster; ///< Internal raycaster.\n\t};\n\n\t/** A raycasting camera is an input camera augmented with additional casting and frustum helpers.\n\t\\ingroup sibr_raycaster\n\t*/\n\tclass SIBR_RAYCASTER_EXPORT RaycastingCamera : public sibr::InputCamera {\n\t\tSIBR_CLASS_PTR(RaycastingCamera);\n\tpublic:\n\t\tusing HPlane = Eigen::Hyperplane<float, 3>;\n\t\tusing Line3 = Eigen::ParametrizedLine<float, 3>;\n\n\t\t/** Constructor from an InputCamera\n\t\t\\param cam the camera\n\t\t*/\n\t\tRaycastingCamera(const sibr::InputCamera & cam);\n\n\t\t/**\tCompute the unormalized ray direction from the camera position to a given pixel.\n\t\t\\param pixel the pixel in [0,w-1]x[0,h-1]\n\t\t\\return the ray direction from the camera position to the center of the input pixel.\n\t\t*/\n\t\tsibr::Vector3f rayDirNotNormalized(const sibr::Vector2f & pixel) const;\n\n\t\t/**\tCompute the normalized ray direction from the camera position to a given pixel.\n\t\t\\param pixel the pixel in [0,w-1]x[0,h-1]\n\t\t\\return the ray direction from the camera position to the center of the input pixel.\n\t\t*/\n\t\tsibr::Vector3f rayDir(const sibr::Vector2f & pixel) const;\n\n\t\t/**\tGenerate the ray going from the camera position to a given pixel.\n\t\t\\param pixel the pixel in [0,w-1]x[0,h-1]\n\t\t\\return the ray from the camera position to the center of the input pixel.\n\t\t*/\n\t\tRay getRay(const sibr::Vector2f & pixel) const;\n\n\t\t/** Compute the (up to) two intersections of a oriented line with the camera frustum.\n\t\t\\param line the parametrized oriented line to test\n\t\t\\return the intersection parameters of the two intersection points with the frustum.\n\t\t*/\n\t\tsibr::Vector2f rayProjection(const Line3 & line) const;\n\n\t\t/** Check if a point is in the camera frustum.\n\t\t\\param pt the 3D point to test\n\t\t\\param eps the tolerance threshold\n\t\t\\return true if the point is inside\n\t\t*/\n\t\tbool isInsideFrustum(const sibr::Vector3f & pt, float eps = 0.0001) const;\n\n\t\t/** Project a 3D point on the image plane, including points behind the camera (horizontal flip).\n\t\t\\param pt3d the 3d point\n\t\t\\return the pixel coordinates in [0,w]x(0,h]\n\t\t*/\n\t\tsibr::Vector2f projectImg_outside_frustum_correction(const Vector3f& pt3d) const;\n\n\t\tsibr::Vector3f dx, dy, upLeftOffsetMinusPos; ///< Camera raycasting parameters.\n\n\t\tstd::vector<HPlane> frustum_planes; ///< Frustum planes: near, far, top, bottom, left, right\t\n\t};\n\n\t///// DEFINITIONS /////\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Config.hpp>\n\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_RAYCASTER_DEFINE\n#    define SIBR_RAYCASTER_EXPORT\n#    define SIBR_NO_RAYCASTER_EXPORT\n#  else\n#    ifndef SIBR_RAYCASTER_EXPORT\n#      ifdef SIBR_RAYCASTER_EXPORTS\n          /* We are building this library */\n#        define SIBR_RAYCASTER_EXPORT __declspec(dllexport)\n#      else\n          /* We are using this library */\n#        define SIBR_RAYCASTER_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#  endif\n# else\n#  define SIBR_RAYCASTER_EXPORT\n# endif\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Intersector2D.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"Intersector2D.h\"\n#include <core/graphics/Shader.hpp>\n#include <core/graphics/RenderTarget.hpp>\n#include <core/graphics/Mesh.hpp>\n\nnamespace sibr {\n\n\n\tfloat Intersector2D::sign(sibr::Vector2f p1, sibr::Vector2f p2, sibr::Vector2f p3)\n\t{\n\t\treturn (p1.x() - p3.x()) * (p2.y() - p3.y()) - (p2.x() - p3.x()) * (p1.y() - p3.y());\n\t}\n\n\tbool Intersector2D::PointInTriangle(sibr::Vector2f pt, sibr::Vector2f v1, sibr::Vector2f v2, sibr::Vector2f v3)\n\t{\n\t\tbool b1, b2, b3;\n\n\t\tb1 = sign(pt, v1, v2) < 0.0f;\n\t\tb2 = sign(pt, v2, v3) < 0.0f;\n\t\tb3 = sign(pt, v3, v1) < 0.0f;\n\n\t\treturn ((b1 == b2) && (b2 == b3));\n\t}\n\n\t//Segment are a->b and c->d\n\tbool Intersector2D::LineLineIntersect(sibr::Vector2f a, sibr::Vector2f b, sibr::Vector2f c, sibr::Vector2f d)\n\t{\n\t\tfloat den = ((d.y() - c.y())*(b.x() - a.x()) - (d.x() - c.x())*(b.y() - a.y()));\n\t\tfloat num1 = ((d.x() - c.x())*(a.y() - c.y()) - (d.y() - c.y())*(a.x() - c.x()));\n\t\tfloat num2 = ((b.x() - a.x())*(a.y() - c.y()) - (b.y() - a.y())*(a.x() - c.x()));\n\t\tfloat u1 = num1 / den;\n\t\tfloat u2 = num2 / den;\n\n\t\tif (den == 0 && num1 == 0 && num2 == 0)\n\t\t\t/* The two lines are coincidents */\n\t\t\treturn false;\n\t\tif (den == 0)\n\t\t\t/* The two lines are parallel */\n\t\t\treturn false;\n\t\tif (u1 < 0 || u1 > 1 || u2 < 0 || u2 > 1)\n\t\t\t/* Lines do not collide */\n\t\t\treturn false;\n\t\t/* Lines DO collide */\n\t\treturn true;\n\t}\n\n\tbool Intersector2D::TriTriIntersect(sibr::Vector2f t0_0, sibr::Vector2f t0_1, sibr::Vector2f t0_2,\n\t\tsibr::Vector2f t1_0, sibr::Vector2f t1_1, sibr::Vector2f t1_2) {\n\n\t\t//Test if lines intersects\n\t\tif (LineLineIntersect(t0_0, t0_1, t1_0, t1_1)) { return true; };\n\t\tif (LineLineIntersect(t0_0, t0_1, t1_0, t1_2)) { return true; };\n\t\tif (LineLineIntersect(t0_0, t0_1, t1_1, t1_2)) { return true; };\n\t\tif (LineLineIntersect(t0_0, t0_2, t1_0, t1_1)) { return true; };\n\t\tif (LineLineIntersect(t0_0, t0_2, t1_0, t1_2)) { return true; };\n\t\tif (LineLineIntersect(t0_0, t0_2, t1_1, t1_2)) { return true; };\n\t\tif (LineLineIntersect(t0_1, t0_2, t1_0, t1_1)) { return true; };\n\t\tif (LineLineIntersect(t0_1, t0_2, t1_0, t1_2)) { return true; };\n\t\tif (LineLineIntersect(t0_1, t0_2, t1_1, t1_2)) { return true; };\n\n\n\t\t//Test if one point in triangle :\n\t\tif (PointInTriangle(t0_0, t1_0, t1_1, t1_2) ||\n\t\t\tPointInTriangle(t0_1, t1_0, t1_1, t1_2) ||\n\t\t\tPointInTriangle(t0_2, t1_0, t1_1, t1_2) ||\n\t\t\tPointInTriangle(t1_0, t0_0, t0_1, t0_2) ||\n\t\t\tPointInTriangle(t1_1, t0_0, t0_1, t0_2) ||\n\t\t\tPointInTriangle(t1_2, t0_0, t0_1, t0_2)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tbool Intersector2D::QuadQuadIntersect(sibr::Vector2f q0_0, sibr::Vector2f q0_1, sibr::Vector2f q0_2, sibr::Vector2f q0_3,\n\t\tsibr::Vector2f q1_0, sibr::Vector2f q1_1, sibr::Vector2f q1_2, sibr::Vector2f q1_3)\n\t{\n\t\tif (TriTriIntersect(\n\t\t\tq0_0, q0_1, q0_3,\n\t\t\tq1_0, q1_1, q1_3)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (TriTriIntersect(\n\t\t\tq0_0, q0_1, q0_3,\n\t\t\tq1_1, q1_2, q1_3)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (TriTriIntersect(\n\t\t\tq0_1, q0_2, q0_3,\n\t\t\tq1_0, q1_1, q1_3)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (TriTriIntersect(\n\t\t\tq0_1, q0_2, q0_3,\n\t\t\tq1_1, q1_2, q1_3)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tstd::vector<std::vector<bool>> Intersector2D::frustrumQuadsIntersect(std::vector<quad> & quads, const std::vector<InputCamera::Ptr> & cams)\n\t{\n\t\tstd::clock_t previous;\n\t\tdouble duration;\n\t\tprevious = std::clock();\n\n\t\tstd::vector<std::vector<bool>> result(cams.size(), std::vector<bool>(quads.size(), false));\n\n\t\tsibr::GLShader\t\t\t\tshader;\n\t\tsibr::GLParameter\t\t\tshader_proj;\n\n\n\t\tstd::string vertexShader =\n\t\t\t\"#version 420\\n\"\n\t\t\t\"uniform mat4 MVP;\\n\"\n\t\t\t\"layout(location = 0) in vec3 in_vertex;\\n\"\n\t\t\t\"void main(void) {\\n\"\n\t\t\t\"\tgl_Position = MVP * vec4(in_vertex, 1.0);\\n\"\n\t\t\t\"}\\n\";\n\n\n\t\tstd::string fragmentShader =\n\t\t\t\"#version 420\\n\"\n\t\t\t\"out float out_color;\\n\"\n\t\t\t\"void main(void) {\\n\"\n\t\t\t\"\t\tout_color = 1.0;\\n\"\n\t\t\t\"}\\n\";\n\n\t\tshader.init(\"quadShader\", vertexShader, fragmentShader);\n\t\tshader_proj.init(shader, \"MVP\");\n\n\t\tstd::shared_ptr<sibr::RenderTargetLum> rtLum;\n\n\t\tfor (int c = 0; c < cams.size(); c++) {\n\t\t\tconst sibr::InputCamera & cam = *cams[c];\n\n\t\t\tfloat ratio = (float)cam.h() / (float)cam.w();\n\t\t\tint w = std::min(400, (int)cam.w());\n\t\t\tint h = int(w*ratio);\n\n\t\t\trtLum.reset(new sibr::RenderTargetLum(w, h));\n\n\t\t\tfor (int q = 0; q < quads.size(); q++) {\n\n\t\t\t\tquad & quad = quads[q];\n\n\t\t\t\tsibr::ImageL8 imLum;\n\n\t\t\t\tstd::shared_ptr<sibr::Mesh> quadMesh = std::shared_ptr<sibr::Mesh>(new sibr::Mesh(true));\n\n\t\t\t\tstd::vector<sibr::Vector3f> vertexBuffer;\n\t\t\t\tvertexBuffer.push_back(quad.q1);\n\t\t\t\tvertexBuffer.push_back(quad.q2);\n\t\t\t\tvertexBuffer.push_back(quad.q3);\n\t\t\t\tvertexBuffer.push_back(quad.q4);\n\n\t\t\t\tint indices[12] = { 0, 1, 2, 0, 2, 3, 1, 2, 3, 0, 1, 3 }; //triangle added, can be optimized if quad ensured with good order\n\t\t\t\tstd::vector<uint> indicesBuffer(&indices[0], &indices[0] + 12);\n\n\t\t\t\tquadMesh->vertices(vertexBuffer);\n\t\t\t\tquadMesh->triangles(indicesBuffer);\n\n\t\t\t\tglViewport(0, 0, w, h);\n\t\t\t\trtLum->bind();\n\t\t\t\tglClearColor(0.0, 0.0, 0.0, 0.0);\n\t\t\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n\t\t\t\tshader.begin();\n\t\t\t\tshader_proj.set(cam.viewproj());\n\n\t\t\t\tquadMesh->render(false, false, sibr::Mesh::RenderMode::FillRenderMode);\n\n\t\t\t\tshader.end();\n\n\t\t\t\trtLum->readBack(imLum);\n\n\t\t\t\tbool nonBlack = false;\n\t\t\t\tbool breakLoop = false;\n\t\t\t\tfor (int j = 0; j < (int)(rtLum->h()); j++) {\n\t\t\t\t\tfor (int i = 0; i < (int)(rtLum->w()); i++) {\n\t\t\t\t\t\tif (imLum(i, j).x() != 0) {\n\t\t\t\t\t\t\tnonBlack = true;\n\t\t\t\t\t\t\tbreakLoop = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (breakLoop)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t//std::cout << \"result \" << q << \" : \" << nonBlack << std::endl;\n\t\t\t\tif (nonBlack)\n\t\t\t\t\tresult[c][q] = true;\n\n\t\t\t}\n\t\t}\n\n\t\tduration = (std::clock() - previous) / (double)CLOCKS_PER_SEC;\n\t\tstd::cout << \"render : \" << duration << std::endl;\n\t\tprevious = std::clock();\n\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Intersector2D.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/raycaster/Config.hpp\"\n#include <vector>\n#include <core/system/Vector.hpp>\n#include <core/assets/InputCamera.hpp>\n\n\n\n/** Struct representing a 3D quad, along with load/save utilities.\n\t\\todo move in proper namespace without breaking anything.\n\t\\ingroup sibr_raycaster\n*/\nstruct  quad {\n\n\tsibr::Vector3f q1;\n\tsibr::Vector3f q2;\n\tsibr::Vector3f q3;\n\tsibr::Vector3f q4;\n\n\t/** Save quad to file on disk.\n\t\\param path destination file.\n\t*/\n\tvoid save(std::string path) {\n\t\tsibr::ByteStream bs;\n\t\tbs << q1.x() << q1.y() << q1.z()\n\t\t\t<< q2.x() << q2.y() << q2.z()\n\t\t\t<< q3.x() << q3.y() << q3.z()\n\t\t\t<< q4.x() << q4.y() << q4.z();\n\t\tbs.saveToFile(path);\n\t}\n\n\t/** Load quad from file on disk.\n\t\\param path source file.\n\t*/\n\tvoid load(std::string path) {\n\t\tsibr::ByteStream bs;\n\t\tbs.load(path);\n\t\tbs >> q1.x() >> q1.y() >> q1.z()\n\t\t\t>> q2.x() >> q2.y() >> q2.z()\n\t\t\t>> q3.x() >> q3.y() >> q3.z()\n\t\t\t>> q4.x() >> q4.y() >> q4.z();\n\t}\n\n};\n\nnamespace sibr {\n\n\n\t/** This class provides utilities to compute point/line/triangle/quad intersections.\n\t\\ingroup sibr_raycaster\n\t*/\n\tclass SIBR_RAYCASTER_EXPORT Intersector2D\n\t{\n\n\tpublic:\n\t\t/// Constructor.\n\t\tIntersector2D(void) = delete;\n\n\t\t/// Destructor\n\t\t~Intersector2D(void) = delete;\n\n\t\t/**\n\t\tHaving defined a straight line in the 2D plane, this method can be used to know in which half-space (defined by the line) a point lies.\n\t\t\\param p1 the 2D point to locate wrt to the line.\n\t\t\\param p2 a 2D point on the line.\n\t\t\\param p3 another 2D point on the line.\n\t\t\\return a signed value indicating on which side of the line the point is.\n\t\t*/\n\t\tstatic float sign(sibr::Vector2f p1, sibr::Vector2f p2, sibr::Vector2f p3);\n\n\t\t/**\n\t\tTests if a point falls inside a triangle, in 2D space.\n\t\t\\param pt the point to test.\n\t\t\\param v1 first triangle vertex.\n\t\t\\param v2 second triangle vertex.\n\t\t\\param v3 third triangle vertex.\n\t\t\\return a boolean denoting if the point belong to the triangle or not.\n\t\t*/\n\t\tstatic bool PointInTriangle(sibr::Vector2f pt, sibr::Vector2f v1, sibr::Vector2f v2, sibr::Vector2f v3);\n\n\t\t/**\n\t\tTests if a line intersects another line, in 2D space.\n\t\t\\param a first point on the first line.\n\t\t\\param b second point on the first line.\n\t\t\\param c first point on the second line.\n\t\t\\param d second point on the second line.\n\t\t\\return a boolean denoting if the lines intersects.\n\t\t*/\n\t\tstatic bool LineLineIntersect(sibr::Vector2f a, sibr::Vector2f b, sibr::Vector2f c, sibr::Vector2f d);\n\n\t\t/**\n\t\tTests if two triangles overlap, in 2D space.\n\t\t\\param t0_0 first vertex of the first triangle.\n\t\t\\param t0_1 second vertex of the first triangle.\n\t\t\\param t0_2 third vertex of the first triangle.\n\t\t\\param t1_0 first vertex of the second triangle.\n\t\t\\param t1_1 second vertex of the second triangle.\n\t\t\\param t1_2 third vertex of the second triangle.\n\t\t\\return a boolean denoting if the triangles overlap.\n\t\t*/\n\t\tstatic bool TriTriIntersect(sibr::Vector2f t0_0, sibr::Vector2f t0_1, sibr::Vector2f t0_2,\n\t\t\tsibr::Vector2f t1_0, sibr::Vector2f t1_1, sibr::Vector2f t1_2);\n\n\t\t/**\n\t\tTests if two quads overlap, in 2D space.\n\t\t\\param q0_0 first vertex of the first quad.\n\t\t\\param q0_1 second vertex of the first quad.\n\t\t\\param q0_2 third vertex of the first quad.\n\t\t\\param q0_3 fourth vertex of the first quad.\n\t\t\\param q1_0 first vertex of the second quad.\n\t\t\\param q1_1 second vertex of the second quad.\n\t\t\\param q1_2 third vertex of the second quad.\n\t\t\\param q1_3 fourth vertex of the second quad.\n\t\t\\return a boolean denoting if the quads overlap.\n\t\t*/\n\t\tstatic bool QuadQuadIntersect(sibr::Vector2f q0_0, sibr::Vector2f q0_1, sibr::Vector2f q0_2, sibr::Vector2f q0_3,\n\t\t\tsibr::Vector2f q1_0, sibr::Vector2f q1_1, sibr::Vector2f q1_2, sibr::Vector2f q1_3);\n\n\t\t/**\n\t\tPerform multiple quads/camera frusta intersections at once.\n\t\t\\warning Requires an existing and current OpenGL context.\n\t\t\\param quads an array of quads to test against each camera frustum.\n\t\t\\param cams an array of cameras against which frusta the intersections tests should be performed.\n\t\t\\return a double-array of booleans denoting, for each camera, for each quad, if the quad intersects the frustum volume.\n\t\t*/\n\t\tstatic std::vector<std::vector<bool>> frustrumQuadsIntersect(std::vector<quad> & quads, const std::vector<InputCamera::Ptr> & cams);\n\n\t};\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/KdTree.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include \"Config.hpp\"\r\n\r\n#include \"core/system/Vector.hpp\"\r\n#include \"nanoflann/nanoflann.hpp\"\r\n\r\nnamespace sibr { \r\n\r\n\t/**\r\n\t * \\class KdTree\r\n\t * \\brief Represent a 3D hierachical query structure baked by a nanoflann KdTree.\r\n\t * \\note With the default L2 distance, all distances and radii are expected to be \r\n\t * the squared values (this is a nanoflann constraint). For other metrics, use the distance directly.\r\n\t * \\ingroup sibr_raycaster\r\n\t */\r\n\ttemplate <typename num_t = double, class Distance = nanoflann::metric_L2>\r\n\tclass  KdTree\r\n\t{\r\n\t\tSIBR_CLASS_PTR(KdTree);\r\n\t\r\n\tpublic:\r\n\r\n\t\ttypedef\tEigen::Matrix<num_t, 3, 1, Eigen::DontAlign> Vector3X;\r\n\t\ttypedef KdTree<num_t, Distance> self_t;\r\n\t\ttypedef typename Distance::template traits<num_t, self_t>::distance_t metric_t;\r\n\t\ttypedef nanoflann::KDTreeSingleIndexAdaptor< metric_t, self_t, 3, size_t>  index_t;\r\n\t\ttypedef std::vector<std::pair<size_t, num_t>> Results;\r\n\r\n\t\t/**\r\n\t\t * Constructor.\r\n\t\t * The KdTree will do a copy of the positions vector.\r\n\t\t * \\param positions a list of 3D points\r\n\t\t * \\param leafMaxSize maximum number of points per leaf\r\n\t\t */\r\n\t\tKdTree(const std::vector<Vector3X> & positions, size_t leafMaxSize = 10);\r\n\r\n\t\t/** Destructor. */\r\n\t\t~KdTree();\r\n\r\n\t\t/** Get the closest point stored in the KdTree for the specified distance\r\n\t\t* \\param pos the reference point\r\n\t\t* \\param distanceSq will contain the squared distance from pos to the closest point in the tree.\r\n\t\t* \\return the index of the closest point in the tree.\r\n\t\t*/\r\n\t\tsize_t getClosest(const Vector3X & pos, num_t & distanceSq) const;\r\n\r\n\t\t/** Get the closest point stored in the KdTree for the specified distance\r\n\t\t* \\param pos the reference point\r\n\t\t* \\param count the number of neighbours to query\r\n\t\t* \\param idDistSqs will contain the indices of the closest points and their squared distances to the reference point\r\n\t\t*/\r\n\t\tvoid getClosest(const Vector3X & pos, size_t count, Results & idDistSqs) const;\r\n\r\n\t\t/** Get all points in a sphere of a given radius around a reference point.\r\n\t\t *\\param pos the reference point\r\n\t\t *\\param maxDistanceSq the squared sphere radius\r\n\t\t *\\param sorted should the points be sorted in ascending distance order\r\n\t\t *\\param idDistSqs will contain the indices of the points in the sphere and their squared distances to the reference point\r\n\t\t */\r\n\t\tvoid getNeighbors(const Vector3X & pos, double maxDistanceSq, bool sorted, Results & idDistSqs) const;\r\n\r\n\t\t/// Interface expected by nanoflann for an adapter.\r\n\t\tconst self_t & derived() const {\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\t\t/// Interface expected by nanoflann for an adapter.\r\n\t\tself_t & derived() {\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\t\t/// Interface: Must return the number of data points\r\n\t\tinline size_t kdtree_get_point_count() const {\r\n\t\t\treturn _points.size();\r\n\t\t}\r\n\r\n\t\t/// Interface: Returns the dim'th component of the idx'th point in the class:\r\n\t\tinline num_t kdtree_get_pt(const size_t idx, const size_t dim) const {\r\n\t\t\treturn _points[idx][dim];\r\n\t\t}\r\n\r\n\t\t/// Interface: Optional bounding-box computation: \\return false to default to a standard bbox computation loop.\r\n\t\ttemplate <class BBOX>\r\n\t\tbool kdtree_get_bbox(BBOX & /*bb*/) const {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\tprivate:\r\n\r\n\t\tconst std::vector<Vector3X> _points;\r\n\t\tindex_t * _index;\r\n\t};\r\n\r\n\ttemplate <typename num_t, class Distance>\r\n\tKdTree<num_t, Distance>::KdTree(const std::vector<Vector3X>& positions, size_t leafMaxSize) : _points(positions) {\r\n\t\tif(positions.empty()) {\r\n\t\t\tSIBR_ERR << \"[KdTree] Trying to build a Kd-Tree from an empty list of points.\" << std::endl;\r\n\t\t}\r\n\t\t_index = new index_t(3, *this, nanoflann::KDTreeSingleIndexAdaptorParams(leafMaxSize));\r\n\t\t_index->buildIndex();\r\n\t}\r\n\r\n\ttemplate<typename num_t, class Distance>\r\n\tKdTree<num_t, Distance>::~KdTree()\r\n\t{\r\n\t\tdelete _index;\r\n\t}\r\n\r\n\ttemplate <typename num_t, class Distance>\r\n\tinline size_t KdTree<num_t, Distance>::getClosest(const Vector3X& pos, num_t & distanceSq) const {\r\n\t\tsize_t index = 0;\r\n\t\t_index->knnSearch(&pos[0], 1, &index, &distanceSq);\r\n\t\treturn index;\r\n\t}\r\n\r\n\ttemplate <typename num_t, class Distance>\r\n\tinline void KdTree<num_t, Distance>::getClosest(const Vector3X & pos, size_t count, Results & idDistSqs) const {\r\n\t\tstd::vector<size_t> outIds(count);\r\n\t\tstd::vector<num_t> outDists(count);\r\n\t\tconst size_t foundCount = _index->knnSearch(&pos[0], count, &outIds[0], &outDists[0]);\r\n\t\tidDistSqs.resize(foundCount);\r\n\t\tfor(size_t i = 0; i < foundCount; ++i) {\r\n\t\t\tidDistSqs[i] = std::make_pair(outIds[i], outDists[i]);\r\n\t\t}\r\n\t}\r\n\r\n\ttemplate <typename num_t, class Distance>\r\n\tinline void KdTree<num_t, Distance>::getNeighbors(const Vector3X & pos, double maxDistanceSq, bool sorted, Results & idDistSqs) const {\r\n\t\t_index->radiusSearch(&pos[0], float(maxDistanceSq), idDistSqs, nanoflann::SearchParams(32, 0.0f, sorted));\r\n\t}\r\n\r\n\r\n} /*namespace sibr*/ \r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/PlaneEstimator.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"PlaneEstimator.hpp\"\n#include <random>\n\ntypedef Eigen::Array<bool, Eigen::Dynamic, 1> ArrayXb;\n\nPlaneEstimator::PlaneEstimator() {}\n\nPlaneEstimator::PlaneEstimator(const std::vector<sibr::Vector3f> & vertices, bool excludeBB)\n{\n\n\tEigen::AlignedBox<float, 3> boxScaled;\n\tif (excludeBB) {\n\t\tEigen::AlignedBox<float, 3> box;\n\t\tfor (const auto & vertex : vertices) {\n\t\t\tbox.extend(vertex);\n\t\t}\n\t\tfor (const auto & vertex : vertices) {\n\t\t\tboxScaled.extend(box.center()+0.99f*(vertex- box.center()));\n\t\t}\n\n\t}\n\tint bboxReject = 0;\n\tif (vertices.size() > 200000) {\n\t\tstd::cout << \"Found more than 200000 points reducing point cloud size ...\" << std::endl;\n\n\t\tstd::random_device rd;\n\t\tstd::mt19937 mt(rd());\n\t\tstd::uniform_real_distribution<double> dist(0.0, 1.0);\n\n\t\tfor (const auto & v : vertices) {\n\t\t\tdouble random = dist(mt);\n\t\t\tif (random < 200000.0 / double(vertices.size())) {\n\t\t\t\tif (!excludeBB || (boxScaled.exteriorDistance(v)==0) )\n\t\t\t\t\t_Points.push_back(v);\n\t\t\t\telse if (excludeBB && boxScaled.exteriorDistance(v) > 0) {\n\t\t\t\t\tbboxReject++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (excludeBB)\n\t\t\tstd::cout << bboxReject << \" points where rejected becaused considered on the bounding box\" << std::endl;\n\t}\n\telse {\n\t\t_Points = vertices;\n\t}\n\tstd::cout << \"Point Cloud size: \" << _Points.size() << std::endl;\n\t_numPoints3D = (int)_Points.size();\n\t_remainPoints3D.resize(_Points.size(), 3);\n\t_remainNormals3D.resize(_Points.size(), 3);\n\n\tfor (int i = 0; i < _Points.size(); i++) {\n\t\t_remainPoints3D.row(i) = _Points[i];\n\t\t_remainNormals3D.row(i) = sibr::Vector3f(0, 0, 0);\n\n\t}\n\n\t_planeComputed = false;\n}\n\nvoid PlaneEstimator::computePlanes(const int numPlane, const  float delta, const int numTry) {\n\n\n\t_planeComputed = true; // we know that the planes were computed\n\n\tstd::cout << \"Original number of points \" << _remainPoints3D.rows() << std::endl;\n\tfor (int i = 0; i < numPlane; i++) {\n\n\t\tif (_remainPoints3D.rows() < _numPoints3D * 5 / 100)\n\t\t{\n\t\t\tstd::cout << \"Not enough points remaining, stop searching. \" << i << \" planes found.\" << std::endl;\n\t\t\tbreak;\n\t\t}\n\n\n\t\tsibr::Vector3f color(((float)rand() / (RAND_MAX)), ((float)rand() / (RAND_MAX)), ((float)rand() / (RAND_MAX)));\n\n\t\tEigen::MatrixXi mask;\n\t\t//Eigen::MatrixXf maskNormals;\n\t\tstd::pair<Eigen::MatrixXf, sibr::Vector3f> covMean;\n\t\tint vote = -1;\n\t\tsibr::Vector4f plane = estimatePlane(delta, numTry, mask, vote, covMean);\n\n\t\tif (vote < _numPoints3D * 2 / 100 && i >= 12) {\n\t\t\tstd::cout << \"Not enough points in candidate plane, stop searching. \" << i << \" planes found.\" << std::endl;\n\t\t\tbreak;\n\t\t}\n\t\t//\n\n\t\tEigen::MatrixXf remainPoints3DTemp(_remainPoints3D.rows() - vote, 3);\n\t\tEigen::MatrixXf remainNormals3DTemp(_remainNormals3D.rows() - vote, 3);\n\t\t//std::vector<sibr::Vector3i> remainImPosTemp;\n\n\t\tint notSel = 0;\n\t\tstd::vector<sibr::Vector3f> pointsPlane;\n\t\tfor (int rIt = 0; rIt < _remainPoints3D.rows(); rIt++) {\n\t\t\tif (mask.row(rIt)(0) == 0) { // not selected\n\t\t\t\tremainPoints3DTemp.row(notSel) = _remainPoints3D.row(rIt);\n\t\t\t\tremainNormals3DTemp.row(notSel) = _remainNormals3D.row(rIt);\n\t\t\t\t//remainImPosTemp.push_back(_remainImPos[rIt]);\n\t\t\t\tnotSel++;\n\t\t\t}\n\n\t\t\telse { // In the plane\n\t\t\t\tpointsPlane.push_back(_remainPoints3D.row(rIt));\n\t\t\t}\n\t\t}\n\n\t\tstd::cout << \"vote :\" << vote << \" notSel \" << notSel << \" supposed total \" << _remainPoints3D.rows() << std::endl;\n\t\t_remainPoints3D = remainPoints3DTemp;\n\t\t_remainNormals3D = remainNormals3DTemp;\n\t\t//_remainImPos=remainImPosTemp;\n\t\tstd::cout << \"Remaining number of points \" << _remainPoints3D.rows() << std::endl;\n\n\t\tsibr::Vector3f center = plane.w()*plane.xyz();\n\n\t\tsibr::Vector4f finalPlane = plane;\n\n\t\t_planes.push_back(finalPlane);\n\t\t_points.push_back(pointsPlane);\n\t\t// centers and basis\n\t\t_centers.push_back(center);\n\n\t\t//plane statistical informations\n\t\t_covMeans.push_back(covMean);\n\t\t_votes.push_back(vote);\n\t}\n\n}\n\nsibr::Vector4f PlaneEstimator::estimatePlane(const float delta, const int numTry, Eigen::MatrixXi & bestMask, int & bestVote, std::pair<Eigen::MatrixXf, sibr::Vector3f> & bestCovMean) {\n\n\tsibr::Vector4f bestPlane;\n\n\tfloat bestWVote = 0;\n#pragma omp parallel for\n\tfor (int i = 0; i < numTry; i++) {\n\n\t\tEigen::MatrixXi mask;\n\t\tsibr::Vector4f plane = plane3Pts();\n\t\tif (plane.xyz().norm() > 0) {\n\t\t\tstd::pair<int, float> votePair = votePlane(plane, delta, mask);\n\n#pragma omp critical\n\t\t\t{\n\t\t\t\t//std::cout << i << \" \";\n\t\t\t\tif (votePair.second > bestWVote) {\n\t\t\t\t\tbestWVote = votePair.second;\n\t\t\t\t\tbestVote = votePair.first;\n\t\t\t\t\tbestPlane = plane;\n\t\t\t\t\tbestMask = std::move(mask); // move to avoid copy\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstd::cout << \"Best vote \" << bestVote << \" Best plane \" << bestPlane << std::endl;\n\t/*\n\tstd::cout << \"Plane refinement ...\" << std::endl;\n\n\t////////////////// Plane fitting\n\n\tEigen::MatrixXf data(3, bestVote);\n\tint sel = 0;\n\n\tfor (int rIt = 0; rIt < _remainPoints3D.rows(); rIt++) {\n\t\tif (bestMask.row(rIt)(0) == 1) {\n\t\t\tdata.col(sel) = _remainPoints3D.row(rIt);\n\t\t\tsel++;\n\t\t}\n\t}\n\n\n\tstd::cout << \"Sel \" << sel << std::endl;\n\n\tsibr::Vector3f center = data.rowwise().mean();\n\tEigen::MatrixXf dataCentered = data.colwise() - center;\n\n\tbestCovMean.first = (dataCentered*dataCentered.adjoint()) / float(dataCentered.cols() - 1);\n\tbestCovMean.second = center;\n\n\tstd::cout << \"Cov Matrix : \" << bestCovMean.first << \" Mean : \" << bestCovMean.second << std::endl;\n\tstd::cout << \"Cov Determinant : \" << bestCovMean.first.determinant() << std::endl;\n\n\tEigen::JacobiSVD<Eigen::MatrixXf> svd(dataCentered, Eigen::ComputeFullU | Eigen::ComputeThinV);\n\n\tstd::cout << \"old normal\" << \" \" << bestPlane.xyz();\n\t//the normal to the fitting plane is the eigenvector associated to the smallest eigenvalue (i.e. the direction in which the variance of all points is the smallest) \n\tsibr::Vector3f normal = svd.matrixU().col(2);\n\tnormal.normalize();\n\n\tfloat d = center.dot(normal);\n\n\tbestPlane = sibr::Vector4f(normal.x(), normal.y(), normal.z(), d);\n\n\tbestVote = votePlane(bestPlane, 10.0*delta, bestMask, 0.8f).first;*/\n\n\tstd::cout << \" new normal\" << \" \" << bestPlane.xyz() << std::endl;\n\t// normal coherency\n\t//Eigen::ArrayXf dotWithOriNormal=(_remainNormals3D * bestPlane.xyz()).array().cwiseAbs();\n\t//bestMaskNormals = (bestMask.array().cast<float>()*dotWithOriNormal);\n\n\tstd::cout << \"Vote refined \" << bestVote << \" Plane refined \" << bestPlane << /*\" Normals coherency \" << bestMaskNormals.sum()/bestVote  <<*/ std::endl;\n\n\treturn bestPlane;\n\n}\n\n\nsibr::Vector4f PlaneEstimator::plane3Pts() {\n\n\tstd::random_device rd;  //Will be used to obtain a seed for the random number engine\n\tstd::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()\n\tstd::uniform_int_distribution<> dis(0, int(_remainPoints3D.rows() - 1));\n\n\tsibr::Vector3f pointA = _remainPoints3D.row(dis(gen));\n\tsibr::Vector3f pointB = _remainPoints3D.row(dis(gen));\n\tsibr::Vector3f pointC = _remainPoints3D.row(dis(gen));\n\n\tsibr::Vector3f normal = (pointB - pointA).cross(pointC - pointA);\n\tnormal.normalize();\n\n\tfloat d = normal.dot(pointA);\n\n\treturn sibr::Vector4f(normal.x(), normal.y(), normal.z(), d);\n\n}\n\nstd::pair<int, float> PlaneEstimator::votePlane(const sibr::Vector4f plane, const float delta, Eigen::MatrixXi & mask, float normalDot) {\n\n\tsibr::Vector3f normal = plane.xyz();\n\tfloat d = plane.w();\n\n\t//std::cout << \"size \" << _points3D.size() << \" \" << normal.size() << \" d \" << d << std::endl;\n\n\tEigen::ArrayXf distances = (_remainPoints3D * normal).array();\n\n\tEigen::ArrayXf dotWithOriNormal = (_remainNormals3D * normal).array();\n\n\t/*for(int i=0; i< 10; i++){\n\tstd::cout << distances.row(i) << \" \";\n\t}\n\tstd::cout << std::endl;*/\n\n\tdistances = (distances - d * Eigen::ArrayXf::Ones(distances.rows())).cwiseAbs();\n\tdotWithOriNormal = dotWithOriNormal.cwiseAbs();\n\n\t/*for(int i=0; i< 10; i++){\n\tstd::cout << distances.row(i) << \" \";\n\t}\n\tstd::cout << std::endl;*/\n\tmask = (distances < delta && (dotWithOriNormal > normalDot || dotWithOriNormal == 0)).cast<int>();\n\t\n\tEigen::ArrayXf voteW = (distances+ 0.1f*delta* Eigen::ArrayXf::Ones(distances.rows()));\n\tvoteW = mask.array().cast<float>().cwiseQuotient(voteW);\n\t//std::cout << \"SUM \" << mask.sum() << std::endl;\n\n\treturn std::make_pair(mask.sum(), voteW.sum());\n\n}\n\nsibr::Vector4f PlaneEstimator::estimateGroundPlane(sibr::Vector3f roughUp)\n{\n\tif (_planeComputed) {\n\n\t\t//find the floor plane\n\t\tint bestId = -1;\n\t\tint bestVote = 0;\n\n\t\tfor (int p = 0; p < _planes.size(); p++) {\n\t\t\tif (abs(_planes[p].xyz().dot(roughUp)) > 0.87) {\n\t\t\t\tif (_votes[p] > bestVote) {\n\t\t\t\t\tbestId = p;\n\t\t\t\t\tbestVote = _votes[p];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn _planes[bestId];\n\t}\n\telse {\n\t\tstd::cout << \"Error : Plane not computed, you should call computePlanes first\" << std::endl;\n\t\tSIBR_ERR;\n\t\treturn { 0.0f, 0.0f, 0.0f, 0.0f };\n\t}\n}\n\n\nsibr::Vector3f PlaneEstimator::estimateMedianVec(const std::vector<sibr::Vector3f> & ups)\n{\n\n\tstd::vector<float> medUpX;\n\tstd::vector<float> medUpY;\n\tstd::vector<float> medUpZ;\n\n\tfor (const auto & up : ups) {\n\n\t\tmedUpX.push_back(up.x());\n\t\tmedUpY.push_back(up.y());\n\t\tmedUpZ.push_back(up.z());\n\n\t}\n\tstd::sort(medUpX.begin(), medUpX.end());\n\tstd::sort(medUpY.begin(), medUpY.end());\n\tstd::sort(medUpZ.begin(), medUpZ.end());\n\n\tconst size_t medPos = medUpX.size() / 2;\n\n\tsibr::Vector3f upMed(medUpX[medPos], medUpY[medPos], medUpZ[medPos]);\n\tupMed.normalize();\n\n\treturn upMed;\n}\n\nsibr::Mesh PlaneEstimator::getMeshPlane(sibr::Vector4f plane, sibr::Vector3f center, float radius)\n{\n\tsibr::Mesh planeMesh;\n\n\tsibr::Vector3f projCenter = center - (center - plane.w()*plane.xyz()).dot(plane.xyz())*plane.xyz();\n\n\tsibr::Mesh::Vertices vert;\n\tsibr::Mesh::Triangles tri;\n\tsibr::Mesh::Normals nml;\n\tsibr::Mesh::UVs tex;\n\n\tsibr::Vector3f u = (projCenter - plane.w()*plane.xyz()).normalized();\n\tsibr::Vector3f v = plane.xyz().cross(u).normalized();\n\n\tint numP = 50;\n\tfor (int i = 0; i < numP; i++) {\n\t\tvert.push_back(projCenter + radius * cos(2 * M_PI*i / numP)*u + radius * sin(2 * M_PI*i / numP)*v);\n\t\tnml.push_back(plane.xyz().normalized());\n\t\ttri.push_back(sibr::Vector3u(numP, i, (i + 1) % numP));\n\t}\n\n\tvert.push_back(projCenter);\n\n\tplaneMesh.vertices(vert);\n\tplaneMesh.normals(nml);\n\tplaneMesh.triangles(tri);\n\treturn planeMesh;\n}\n\nvoid PlaneEstimator::displayPCAndPlane(sibr::Window::Ptr window)\n{\n}\n\nPlaneEstimator::~PlaneEstimator(void)\n{\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/PlaneEstimator.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <core/raycaster/Config.hpp>\n\n#include <core/system/Utils.hpp>\n#include <core/system/Vector.hpp>\n#include <core/graphics/Image.hpp>\n#include <core/system/Array2d.hpp>\n#include <core/graphics/Mesh.hpp>\n#include <core/graphics/Window.hpp>\n\n\n/**\n\tFit a plane to a point cloud using an improved RANSAC approach.\n\t\\ingroup sibr_raycaster\n*/\nclass SIBR_RAYCASTER_EXPORT PlaneEstimator {\n\npublic:\n\n\t/// Default constructor.\n\tPlaneEstimator();\n\n\t/** Constructor.\n\t\\param vertices the point cloud\n\t\\param excludeBB if true, reject points that are close to the vertices bounding box\n\t*/\n\tPlaneEstimator(const std::vector<sibr::Vector3f> & vertices, bool excludeBB=false);\n\n\t/** Compute one or more planes fitting the data using RANSAC. Points that are well fitted by a plan will bre moved from the set.\n\t\\param numPlane number of planes to fit\n\t\\param delta fit validity threshold\n\t\\param numTry number of attempts to perform for each plane\n\t*/\n\tvoid computePlanes(const int numPlane,const float delta,const int numTry);\n\t\n\t/** Estimate the best plane in the remaining points set using RANSAC.\n\t\\param delta fit validity threshold\n\t\\param numTry number of attempts to perform for each plane\n\t\\param bestMask for each point, will be set to 1 if the plane explains the point well\n\t\\param vote will contain the number of points that fit\n\t\\param bestCovMean unused\n\t\\return the plane parameters\n\t*/\n\tsibr::Vector4f estimatePlane(const float delta,const int numTry, Eigen::MatrixXi & bestMask, int & vote, std::pair<Eigen::MatrixXf,sibr::Vector3f> & bestCovMean);\n\t\n\t/** Choose randomly 3 points among the vertices and compute the corresponding plane.\n\t\\return the plane parameters\n\t*/\n\tsibr::Vector4f plane3Pts(); \n\n\t/** Given a plane and a threshold, this function return the num of point that fit the plane in the remaining points and also the associated mask.\n\t\\param plane the plane parameters\n\t\\param delta validity threshold\n\t\\param mask for each point, will be set to 1 if the plane explains the point well\n\t\\param normalDot normal validity threshold\n\t\\return number of points that fit and overall weighted score (based on normal similarity)\n\t*/\n\tstd::pair<int, float> votePlane(const sibr::Vector4f plane, const float delta, Eigen::MatrixXi & mask, float normalDot=0.98);\n\t\n\t/** For visualization, display the point cloud and fitted plane in a window.\n\t\\param window the windo to use for display\n\t\\deprecated Empty, won't do anything.\n\t*/\n\tvoid displayPCAndPlane(sibr::Window::Ptr window);\n\n\t/** Estimate a fitting plane that is as orthogonal to the given up vector as possible.\n\t\\param roughUp an estimation of the scene up vector\n\t\\return the plane parameters.\n\t*/\n\tsibr::Vector4f estimateGroundPlane(sibr::Vector3f roughUp);\n\t\n\t/** Estimate the scene zenith from a set of camera up vectors (assuming photogrametric capture).\n\t\\param ups a set of up vector\n\t\\return the estimated median zenith vector\n\t*/\n\tstatic sibr::Vector3f estimateMedianVec(const std::vector<sibr::Vector3f> & ups);\n\n\t/** Generate a mesh representing a plane.\n\t\\param plane the parameters of the plane to represent\n\t\\param center center of the plane mesh\n\t\\param radius extent of the plane mesh\n\t\\return the generated plane mesh\n\t*/\n\tstatic sibr::Mesh getMeshPlane(sibr::Vector4f plane , sibr::Vector3f center, float radius);\n\n\tstd::vector<sibr::Vector3f> _Points; ///< All initial points.\n\tint _numPoints3D; ///< Number of initial points.\n\n\tstd::vector<sibr::Vector4f> _planes; ///< Planes are represented as Vector4f(n.x,n.y,n.z,d)\n\tstd::vector<std::vector<sibr::Vector3f>> _points; ///< For each plane, list of fitting points.\n\tstd::vector<sibr::Vector3f> _centers; ///< Plane centers.\n\tstd::vector<int> _votes; ///< Number of votes per plane.\n\tstd::vector<std::pair<Eigen::MatrixXf,sibr::Vector3f>> _covMeans; ///< Unused.\n\t\n\t/// Destructor.\n\t~PlaneEstimator(void);\n\nprotected:\n\n\tEigen::MatrixXf _remainPoints3D; ///< Points to consider.\n\tEigen::MatrixXf _remainNormals3D; ///< Associated normals to consider.\n\tstd::vector<sibr::Vector3u> _Triangles; ///< Triangle list.\n\tbool _planeComputed; ///< Has the plane been computed.\n};\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Ray.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"Ray.hpp\"\n\nnamespace sibr\n{\n\t/*static*/ SIBR_RAYCASTER_EXPORT const float\tRayHit::InfinityDist = std::numeric_limits<float>::infinity();\n\n\tRay::Ray( const sibr::Vector3f& orig, const sibr::Vector3f& dir )\n\t\t: _orig(orig), _dir(dir)\n\t{\n\t\tif (_dir[0] != 0.f || _dir[1] != 0.f || _dir[2] != 0.f)\n\t\t\t_dir.normalize();\n\t}\n\n\tRayHit::RayHit( const Ray& r, float dist, const BCCoord& coord,\n\t\t\t\t\tconst sibr::Vector3f& normal, const Primitive& prim )\n\t\t\t\t\t: _ray(r), _dist(dist), _coord(coord), _normal(normal), _prim(prim)\n\t{\n\t\t_dist = std::max(dist, 0.f);\n\n\t\t// normalize '_normal'\n\t\tfloat len = length(_normal);\n\t\tif (len > 1e-10)\n\t\t\t_normal = _normal / len;\n\n\t}\n\n\tsibr::Vector3f\t\t\tRayHit::interpolateUV( void ) const\n\t{\n\t\tfloat ucoord = barycentricCoord().u;\n\t\tfloat vcoord = barycentricCoord().v;\n\t\treturn sibr::Vector3f(std::max((1.f-ucoord-vcoord), 0.f), ucoord, vcoord);\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Ray.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <string>\n# include <vector>\n# include <core/system/Vector.hpp>\n# include \"core/raycaster/Config.hpp\"\n\nnamespace sibr\n{\n\n\t///\n\t/// Represents a simple ray\n\t/// \\ingroup sibr_raycaster\n\t///\n\tclass SIBR_RAYCASTER_EXPORT Ray\n\t{\n\tpublic:\n\t\t/** Construct a ray from parameters.\n\t\t\\param orig ray origin\n\t\t\\param dir ray direction\n\t\t\\note The direction will be normalized.\n\t\t*/\n\t\tRay( const sibr::Vector3f& orig = sibr::Vector3f(0.f, 0.f, 0.f),\n\t\t\tconst sibr::Vector3f& dir = sibr::Vector3f(0.f, 0.f, -1.f) );\n\n\t\t/** Set the position from where the ray starts.\n\t\t\\param o the new origin\n\t\t*/\n\t\tinline void\t\torig( const sibr::Vector3f& o );\n\t\t\n\t\t/// \\return the ray origin\n\t\tinline const sibr::Vector3f&\torig( void ) const;\n\n\t\t/** Set the direction of the ray. Additionally,\n\t\t you can precise if you want this direction to be automatically\n\t\t normalized or not.\n\t\t \\param d the new direction\n\t\t \\param normalizeIt should normalization be applied\n\t\t */\n\t\tinline void\t\tdir( const sibr::Vector3f& d, bool normalizeIt=true );\n\n\t\t/// \\return the direction of the ray.\n\t\tinline const sibr::Vector3f&\tdir( void ) const;\n\n\t\t/** Return the 3D point such that p = orig + t * dir;\n\t\t\\param t the distance along the ray\n\t\t\\return the 3D point\n\t\t*/\n\t\tVector3f at(float t) const;\n\n\tprivate:\n\t\tsibr::Vector3f\t\t_orig;\t///< Position from where the ray starts\n\t\tsibr::Vector3f\t\t_dir;\t///< Direction where the ray goes\n\t};\n\n\t///\n\t/// Contains information about a ray hit\n\t/// \\ingroup sibr_raycaster\n\t///\n\tclass SIBR_RAYCASTER_EXPORT RayHit\n\t{\n\tpublic:\n\t\tstatic const float\tInfinityDist;\n\n\t\t/// Infos about the object that was hit\n\t\tstruct Primitive\n\t\t{\n\t\t\tuint triID;\t\t///< triangle id of the mesh that was hit\n\t\t\tuint geomID;\t///< mesh id loaded in the raycaster\n\t\t\tuint instID;\t///< id of the instance loaded in the raycaster\n\t\t};\n\n\t\t/// Barycentric coordinates\n\t\tstruct BCCoord\n\t\t{\n\t\t\tfloat u;\t///< u-coordinates (ranging from 0.0 to 1.0)\n\t\t\tfloat v;\t///< v-coordinates (ranging from 0.0 to 1.0)\n\t\t};\n\t\t\n\t\t/** Construct a hit record.\n\t\t\\param r the ray\n\t\t\\param dist intersection distance\n\t\t\\param coord barycentric coordinates\n\t\t\\param normal surface normal\n\t\t\\param prim intersected primitive\n\t\t*/\n\t\tRayHit( const Ray& r, float dist, const BCCoord& coord,\n\t\t\tconst sibr::Vector3f& normal, const Primitive& prim );\n\n\t\t/// Non-hit constructor.\n\t\tRayHit() {};\n\n\t\t/// \\return the ray that was casted\n\t\tinline const Ray&\t\t\tray( void ) const;\n\n\t\t/// \\return the distance from the ray origin to the hit\n\t\tinline float\t\t\t\tdist( void ) const;\n\n\t\t/// \\return the barycentric coordinates of the hit point on the triangle that was hit\n\t\tinline const BCCoord&\t\tbarycentricCoord( void ) const;\n\t\t\n\t\t/** Return the proper barycentric factors for interpolating information stored\n\t\t at each vertex of a triangle.\n\t\t e.g: get fragment color using\n\t\t   color = factor[0]*colorVert0 + factor[1]*colorVert1 + factor[2]*colorVert2\n\t\t It consider the following triangle: https://embree.github.io/images/triangle_uv.png\n\t\t\\return the barycentric coordinates\n\t\t*/\n\t\tsibr::Vector3f\t\t\tinterpolateUV( void ) const;\n\n\t\t/// \\return the normal of the triangle that was hit.\n\t\tinline const sibr::Vector3f&\t\t\tnormal( void ) const;\n\n\t\t/// \\return information about the primitive that was hit.\n\t\tinline const Primitive&\t\tprimitive( void ) const;\n\n\t\t/// \\return true if an object was hit.\n\t\tinline bool\thitSomething( void ) const;\n\n\tprivate:\n\t\tRay\t\t\t_ray;\t\t///< casted ray\n\t\tfloat\t\t_dist;\t\t///< distance from the ray's origin to the hit\n\t\tBCCoord\t\t_coord;\t\t///< barycentric coordinate on the triangle that was hit\n\t\tsibr::Vector3f\t_normal;///< normal of the triangle that was hit\n\t\tPrimitive\t_prim;\t\t///< infos about the primitive that was hit\n\t};\n\n\t///// DEFINITION /////\n\t\n\tvoid\t\tRay::orig( const sibr::Vector3f& o ) {\n\t\t_orig = o;\n\t}\n\tconst sibr::Vector3f&\tRay::orig( void ) const {\n\t\treturn _orig;\n\t}\n\n\tvoid\t\tRay::dir( const sibr::Vector3f& d, bool normalizeIt) {\n\t\t_dir = (normalizeIt)? sibr::Vector3f(d.normalized()) : d;\n\t}\n\tconst sibr::Vector3f&\tRay::dir( void ) const {\n\t\treturn _dir;\n\t}\n\n\tinline Vector3f Ray::at(float t) const\n\t{\n\t\treturn orig() + t * dir();\n\t}\n\n\n\n\tconst Ray&\t\t\tRayHit::ray( void ) const {\n\t\treturn _ray;\n\t}\n\tfloat\t\t\t\tRayHit::dist( void ) const {\n\t\treturn _dist;\n\t}\n\tconst RayHit::BCCoord&\t\tRayHit::barycentricCoord( void ) const {\n\t\treturn _coord;\n\t}\n\tconst sibr::Vector3f&\t\t\tRayHit::normal( void ) const {\n\t\treturn _normal;\n\t}\n\tconst RayHit::Primitive&\t\tRayHit::primitive( void ) const {\n\t\treturn _prim;\n\t}\n\n\tbool\tRayHit::hitSomething( void ) const {\n\t\treturn (_dist != RayHit::InfinityDist);\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Raycaster.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"Raycaster.hpp\"\n\nnamespace sibr\n{\n\t/*static*/ SIBR_RAYCASTER_EXPORT const Raycaster::geomId\t\tRaycaster::InvalidGeomId = RTC_INVALID_GEOMETRY_ID;\n\t/*static*/ bool\t\t\t\t\t\t\t\t\t\t\t\t\tRaycaster::g_initRegisterFlag = false;\n\t/*static*/ Raycaster::RTCDevicePtr\t\t\t\t\t\t\t\tRaycaster::g_device = nullptr;\n\n\t/*static*/ void Raycaster::rtcErrorCallback(void* userPtr, RTCError code, const char* msg)\n\t{\n\t\tstd::string err;\n\n\t\tswitch (code)\n\t\t{\n\t\tcase RTC_ERROR_UNKNOWN: err = std::string(\"RTC_ERROR_UNKNOWN\"); break;\n\t\tcase RTC_ERROR_INVALID_ARGUMENT: err = std::string(\"RTC_ERROR_INVALID_ARGUMENT\"); break;\n\t\tcase RTC_ERROR_INVALID_OPERATION: err = std::string(\"RTC_ERROR_INVALID_OPERATION\"); break;\n\t\tcase RTC_ERROR_OUT_OF_MEMORY: err = std::string(\"RTC_ERROR_OUT_OF_MEMORY\"); break;\n\t\tcase RTC_ERROR_UNSUPPORTED_CPU: err = std::string(\"RTC_ERROR_UNSUPPORTED_CPU\"); break;\n\t\tcase RTC_ERROR_CANCELLED: err = std::string(\"RTC_ERROR_CANCELLED\"); break;\n\t\tdefault: err = std::string(\"invalid error code\"); break;\n\t\t}\n\n\t\tSIBR_ERR << \"Embree reported the following issue - \"\n\t\t\t<< \"[\" << err << \"]'\" << msg << \"'\" << std::endl;\n\t}\n\n\tRaycaster::~Raycaster(void)\n\t{\n\t\t_scene = nullptr;\n\t\t_devicePtr = nullptr;\n\t\tif (g_device && g_device.use_count() == 1)\n\t\t\tg_device = nullptr; // if nobody use it, free it\n\t}\n\n\tbool\tRaycaster::init(RTCSceneFlags sceneType)\n\t{\n\t\tif (!g_device)\n\t\t{\n\t\t\t// The two following macros set flagbits on the control register\n\t\t\t// used by SSE (see http://softpixel.com/~cwright/programming/simd/sse.php)\n\t\t\t_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);\t\t\t// Enable 'Flush Zero' bit\n\t\t\t_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);\t// Enable 'Denormals Zero'bit\n\n\t\t\tSIBR_LOG << \"Initializing Raycaster\" << std::endl;\n\n\t\t\tg_device = std::make_shared<RTCDevice>(rtcNewDevice(NULL));\n\n\t\t\tif (g_device == nullptr) {\n\t\t\t\tSIBR_LOG << \"Cannot create an embree device : \" << rtcGetDeviceError(*g_device.get()) << std::endl;\n\t\t\t}\n\n\t\t\trtcSetDeviceErrorFunction(*g_device.get(), &Raycaster::rtcErrorCallback, nullptr); // Set callback error function\n\t\t\t_devicePtr = g_device; //Moved in the init\n\t\t}\n\n\t\tif (_scene)\n\t\t\treturn true;\n\n\n\n\t\t_scene = std::make_shared<RTCScene>( // define a new scene\n\t\t\t/// \\todo create a new static scene optimized for primary rays (TODO: test perf with RTC_SCENE_ROBUST)\n\t\t\trtcNewScene(*g_device.get())\n\t\t\t); // set a custom deleter\n\n\t\tif (_scene == nullptr)\n\t\t\tSIBR_LOG << \"Cannot create an embree scene\" << std::endl;\n\t\telse {\n\t\t\t//SIBR_LOG << \"Embree device and scene created\" << std::endl;\n\t\t\t//SIBR_LOG << \"Warning Backface culling state : \"<< rtcGetDeviceProperty(*g_device, RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED) << std::endl;\n\t\t\treturn true; // Success\n\t\t}\n\t\treturn false; // Fail\n\t}\n\n\tRaycaster::geomId\tRaycaster::addMesh(const sibr::Mesh& mesh)\n\t{\n\t\treturn addGenericMesh(mesh, RTC_BUILD_QUALITY_HIGH);\n\t}\n\n\tRaycaster::geomId\tRaycaster::addDynamicMesh(const sibr::Mesh& mesh)\n\t{\n\t\treturn addGenericMesh(mesh, RTC_BUILD_QUALITY_LOW);\n\t}\n\n\tRaycaster::geomId\tRaycaster::addGenericMesh(const sibr::Mesh& mesh, RTCBuildQuality type)\n\t{\n\t\tif (init() == false)\n\t\t\treturn Raycaster::InvalidGeomId;\n\n\t\tconst sibr::Mesh::Vertices& vertices = mesh.vertices();\n\t\tconst sibr::Mesh::Triangles& triangles = mesh.triangles();\n\n\t\tRTCGeometry geom_0 = rtcNewGeometry(*g_device.get(), RTC_GEOMETRY_TYPE_TRIANGLE); // EMBREE_FIXME: check if geometry gets properly committed\n\t\trtcSetGeometryBuildQuality(geom_0, type);\n\t\trtcSetGeometryTimeStepCount(geom_0, 1);\n\t\tgeomId id = rtcAttachGeometry(*_scene.get(), geom_0);\n\t\t//rtcReleaseGeometry(geom_0);\n\n\t\tif (id == Raycaster::InvalidGeomId) {\n\t\t\trtcCommitGeometry(geom_0);\n\t\t\treturn Raycaster::InvalidGeomId;\n\t\t}\n\n\t\tstruct Vertex { float x, y, z, a; };\n\t\tstruct Triangle { int v0, v1, v2; };\n\n\t\t{ // Fill vertices of the geometry\n\t\t\tVertex* vert = (Vertex*)rtcSetNewGeometryBuffer(geom_0, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 4 * sizeof(float), vertices.size());\n\t\t\tfor (uint i = 0; i < mesh.vertices().size(); ++i)\n\t\t\t{\n\t\t\t\tvert[i].x = vertices[i][0];\n\t\t\t\tvert[i].y = vertices[i][1];\n\t\t\t\tvert[i].z = vertices[i][2];\n\t\t\t\tvert[i].a = 1.f;\n\t\t\t}\n\n\t\t}\n\n\t\t{ // Fill triangle indices of the geometry\n\t\t\tTriangle* tri = (Triangle*)rtcSetNewGeometryBuffer(geom_0, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * sizeof(int), triangles.size());\n\t\t\tfor (uint i = 0; i < triangles.size(); ++i)\n\t\t\t{\n\t\t\t\ttri[i].v0 = triangles[i][0];\n\t\t\t\ttri[i].v1 = triangles[i][1];\n\t\t\t\ttri[i].v2 = triangles[i][2];\n\t\t\t}\n\n\t\t}\n\n\t\trtcCommitGeometry(geom_0);\n\n\t\t// Commit all changes on the scene\n\t\trtcCommitScene(*_scene.get());\n\n\t\treturn id;\n\t}\n\n\t// xform a mesh by transformation matrix \"mat\". Note that the original positions\n\t// are always stored in mesh.vertices -- we only xform the vertices in the embree buffer\n\tvoid\tRaycaster::xformRtcMeshOnly(sibr::Mesh& mesh, geomId mesh_id, sibr::Matrix4f& mat, sibr::Vector3f& centerPt, float& maxlen)\n\t{\n\t\tstruct Vertex { float x, y, z, a; };\n\t\tVertex* vert = (Vertex*)rtcGetGeometryBufferData(rtcGetGeometry(*_scene.get(), mesh_id), RTC_BUFFER_TYPE_VERTEX, 0) /* EMBREE_FIXME: check if this should be rtcSetNewGeometryBuffer */;\n\t\tsibr::Vector4f averagePt = sibr::Vector4f(0, 0, 0, 1);\n\t\tmaxlen = 0;\n\n\t\tconst sibr::Mesh::Vertices& vertices = mesh.vertices();\n\t\t//const sibr::Mesh::Normals& normals = mesh.normals();\n\t\tfor (uint i = 0; i < mesh.vertices().size(); ++i)\n\t\t{\n\t\t\tsibr::Vector4f v;\n\n\t\t\t// reset to original position\n\t\t\tv[0] = vert[i].x = vertices[i][0];\n\t\t\tv[1] = vert[i].y = vertices[i][1];\n\t\t\tv[2] = vert[i].z = vertices[i][2];\n\t\t\tv[3] = vert[i].a = 1.f;\n\n\t\t\tv = mat * v;\n\t\t\tvert[i].x = v[0], vert[i].y = v[1], vert[i].z = v[2];\n\t\t\taveragePt += v;\n\t\t\tfloat d = sibr::Vector3f(sibr::Vector4f(averagePt / (float)((i == 0) ? 1 : i)).xyz() - v.xyz()).norm();\n\t\t\tif (d > maxlen)\n\t\t\t\tmaxlen = d;\n\t\t}\n\n\t\tsibr::Vector4f cp = averagePt / (float)mesh.vertices().size();\n\t\tcenterPt = sibr::Vector3f(cp[0], cp[1], cp[2]);\n\n\t\t// Update mesh\n\t\trtcCommitGeometry(rtcGetGeometry(*_scene.get(), mesh_id));\n\t\t// Commit changes to scene\n\t\trtcCommitScene(*_scene.get());\n\t}\n\n\tbool\tRaycaster::hitSomething(const Ray& inray, float minDist)\n\t{\n\t\tassert(minDist >= 0.f);\n\n\t\tRTCRay ray;\n\t\tray.flags = 0;\n\t\tray.org_x = inray.orig()[0];\n\t\tray.org_y = inray.orig()[1];\n\t\tray.org_z = inray.orig()[2];\n\t\tray.dir_x = inray.dir()[0];\n\t\tray.dir_y = inray.dir()[1];\n\t\tray.dir_z = inray.dir()[2];\n\n\t\tray.tnear = minDist;\n\t\tray.tfar = RayHit::InfinityDist;\n\n\t\tif (init() == false)\n\t\t\tSIBR_ERR << \"cannot initialize embree, failed cast rays.\" << std::endl;\n\t\telse\n\t\t{\n\t\t\tRTCIntersectContext context;\n\t\t\trtcInitIntersectContext(&context);\n\t\t\trtcOccluded1(*_scene.get(), &context, &ray);\n\t\t}\n\t\treturn ray.tfar < 0.0f;\n\t}\n\n\tstd::array<bool, 8>\tRaycaster::hitSomething8(const std::array<Ray, 8> & inray, float minDist)\n\t{\n\t\tassert(minDist >= 0.f);\n\n\t\tRTCRay8 ray;\n\t\tfor (int r = 0; r < 8; r++) {\n\t\t\tray.org_x[r] = inray[r].orig()[0];\n\t\t\tray.org_y[r] = inray[r].orig()[1];\n\t\t\tray.org_z[r] = inray[r].orig()[2];\n\t\t\tray.dir_x[r] = inray[r].dir()[0];\n\t\t\tray.dir_y[r] = inray[r].dir()[1];\n\t\t\tray.dir_z[r] = inray[r].dir()[2];\n\n\t\t\tray.tnear[r] = minDist;\n\t\t\tray.tfar[r] = RayHit::InfinityDist;\n\t\t}\n\n\t\tint valid8[8] = { -1,-1,-1,-1, -1, -1, -1, -1 };\n\t\tif (init() == false)\n\t\t\tSIBR_ERR << \"cannot initialize embree, failed cast rays.\" << std::endl;\n\t\telse\n\t\t{\n\t\t\tRTCIntersectContext context;\n\t\t\trtcInitIntersectContext(&context);\n\t\t\trtcOccluded8(valid8, *_scene.get(), &context, &ray);\n\t\t}\n\n\t\tstd::array<bool, 8> res;\n\t\tfor (int r = 0; r < 8; r++) {\n\t\t\tbool hit = (ray.tfar[r] < 0.0f );\n\t\t\tres[r] = hit;\n\t\t}\n\n\t\treturn res;\n\t}\n\n\tRayHit\tRaycaster::intersect(const Ray& inray, float minDist)\n\t{\n\t\tassert(minDist >= 0.f);\n\n\t\tRTCRayHit rh;\n\t\trh.ray.flags = 0;\n\t\trh.ray.org_x = inray.orig()[0];\n\t\trh.ray.org_y = inray.orig()[1];\n\t\trh.ray.org_z = inray.orig()[2];\n\t\trh.ray.dir_x = inray.dir()[0];\n\t\trh.ray.dir_y = inray.dir()[1];\n\t\trh.ray.dir_z = inray.dir()[2];\n\n\t\trh.ray.tnear = minDist;\n\t\trh.ray.tfar = RayHit::InfinityDist;\n\t\trh.hit.geomID = RTC_INVALID_GEOMETRY_ID;\n\n\t\tif (init() == false)\n\t\t\tSIBR_ERR << \"cannot initialize embree, failed cast rays.\" << std::endl;\n\t\telse\n\t\t{\n\t\t\tRTCIntersectContext context;\n\t\t\trtcInitIntersectContext(&context);\n\t\t\trtcIntersect1(*_scene.get(), &context, &rh);\n\t\t\trh.hit.Ng_x = -rh.hit.Ng_x; // EMBREE_FIXME: only correct for triangles,quads, and subdivision surfaces\n\t\t\trh.hit.Ng_y = -rh.hit.Ng_y;\n\t\t\trh.hit.Ng_z = -rh.hit.Ng_z;\n\t\t}\n\n\t\t// Convert to the RayHit struct (used for abstract embree)\n\n\t\tRayHit::Primitive prim;\n\t\tprim.geomID = rh.hit.geomID;\n\t\tprim.instID = rh.hit.instID[0];\n\t\tprim.triID = rh.hit.primID;\n\n\t\tRayHit::BCCoord coord;\n\t\tcoord.u = rh.hit.u;\n\t\tcoord.v = rh.hit.v;\n\n\t\tsibr::Vector3f normal = sibr::Vector3f(rh.hit.Ng_x, rh.hit.Ng_y, rh.hit.Ng_z);\n\n\t\t// Return the result.\n\t\treturn RayHit(inray, rh.ray.tfar, coord, normal, prim);\n\t}\n\n\tstd::array<RayHit, 8>\tRaycaster::intersect8(const std::array<Ray, 8> & inray, const std::vector<int> & valid8, float minDist)\n\t{\n\t\tassert(minDist >= 0.f);\n\n\t\tRTCRayHit8 rh;\n\t\tfor (int r = 0; r < 8; r++) {\n\t\t\trh.ray.org_x[r] = inray[r].orig()[0];\n\t\t\trh.ray.org_y[r] = inray[r].orig()[1];\n\t\t\trh.ray.org_z[r] = inray[r].orig()[2];\n\t\t\trh.ray.dir_x[r] = inray[r].dir()[0];\n\t\t\trh.ray.dir_y[r] = inray[r].dir()[1];\n\t\t\trh.ray.dir_z[r] = inray[r].dir()[2];\n\n\t\t\trh.ray.tnear[r] = minDist;\n\t\t\trh.ray.tfar[r] = RayHit::InfinityDist;\n\t\t\trh.hit.geomID[r] = RTC_INVALID_GEOMETRY_ID;\n\t\t}\n\n\t\tif (init() == false)\n\t\t\tSIBR_ERR << \"cannot initialize embree, failed cast rays.\" << std::endl;\n\t\telse\n\t\t{\n\t\t\tRTCIntersectContext context;\n\t\t\trtcInitIntersectContext(&context);\n\t\t\trtcIntersect8(valid8.data(), *_scene.get(), &context, &rh);\n\t\t}\n\n\t\tstd::array<RayHit, 8> res;\n\t\tfor (int r = 0; r < 8; r++) {\n\t\t\tif (valid8[r])\n\t\t\t\tres[r] = {\n\t\t\t\t\tinray[r],\n\t\t\t\t\trh.ray.tfar[r],\n\t\t\t\t\tRayHit::BCCoord{\n\t\t\t\t\t\trh.hit.u[r],rh.hit.v[r]\n\t\t\t\t\t},\n\t\t\t\t\tsibr::Vector3f(rh.hit.Ng_x[r], rh.hit.Ng_y[r], rh.hit.Ng_z[r]),\n\t\t\t\t\tRayHit::Primitive{\n#ifdef SIBR_OS_WINDOWS\n\t\t\t\t\t\t(uint)rh.hit.primID[r] ,(uint)rh.hit.geomID[r],(uint)rh.hit.instID[r]\n#else\n\t\t\t\t\t\t// Considering RTC_MAX_INSTANCE_LEVEL_COUNT to be 1 (Single-level instancing); see https://www.embree.org/api.html#rtchit\n\t\t\t\t\t\t(uint)rh.hit.primID[r] ,(uint)rh.hit.geomID[r],(uint)rh.hit.instID[0][r]\n#endif\n\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\t\treturn res;\n\t}\n\n\tvoid Raycaster::clearGeometry()\n\t{\n\t\t_scene.reset();\n\t}\n\n\tsibr::Vector3f Raycaster::smoothNormal(const sibr::Mesh& mesh, const RayHit& hit)\n\t{\n\t\tif (!mesh.hasNormals()) {\n\t\t\tSIBR_ERR << \" cannot compute smoothed normals if the mesh does not have normals \" << std::endl;\n\t\t}\n\t\tconst sibr::Mesh::Normals& normals = mesh.normals();\n\t\tconst sibr::Vector3u& tri = mesh.triangles()[hit.primitive().triID];\n\n\t\tconst float ucoord = hit.barycentricCoord().u;\n\t\tconst float vcoord = hit.barycentricCoord().v;\n\t\tfloat wcoord = 1.f - ucoord - vcoord;\n\t\twcoord = (wcoord >= 0.0f ? (wcoord <= 1.0f ? wcoord : 1.0f) : 0.0f);\n\n\t\treturn (wcoord * normals[tri[0]] + ucoord * normals[tri[1]] + vcoord * normals[tri[2]]).normalized();\n\t}\n\n\tsibr::Vector3f Raycaster::smoothColor(const sibr::Mesh& mesh, const RayHit& hit)\n\t{\n\t\tif (!mesh.hasColors()) {\n\t\t\tSIBR_ERR << \" cannot compute smoothed color if the mesh does not have colors \" << std::endl;\n\t\t}\n\t\tconst sibr::Mesh::Colors& colors = mesh.colors();\n\t\tconst sibr::Vector3u& tri = mesh.triangles()[hit.primitive().triID];\n\n\t\tconst float ucoord = hit.barycentricCoord().u;\n\t\tconst float vcoord = hit.barycentricCoord().v;\n\t\tfloat wcoord = 1.f - ucoord - vcoord;\n\t\twcoord = (wcoord >= 0.0f ? (wcoord <= 1.0f ? wcoord : 1.0f) : 0.0f);\n\n\t\treturn wcoord * colors[tri[0]] + ucoord * colors[tri[1]] + vcoord * colors[tri[2]];\n\t}\n\n\tsibr::Vector2f Raycaster::smoothUV(const sibr::Mesh& mesh, const RayHit& hit)\n\t{\n\t\tif (!mesh.hasTexCoords()) {\n\t\t\tSIBR_ERR << \" cannot compute UV if the mesh does not have texcoords \" << std::endl;\n\t\t}\n\t\tconst sibr::Mesh::UVs& uvs = mesh.texCoords();\n\t\tconst sibr::Vector3u& tri = mesh.triangles()[hit.primitive().triID];\n\n\t\tconst float ucoord = hit.barycentricCoord().u;\n\t\tconst float vcoord = hit.barycentricCoord().v;\n\t\tfloat wcoord = 1.f - ucoord - vcoord;\n\t\twcoord = (wcoord >= 0.0f ? (wcoord <= 1.0f ? wcoord : 1.0f) : 0.0f);\n\n\t\treturn wcoord * uvs[tri[0]] + ucoord * uvs[tri[1]] + vcoord * uvs[tri[2]];\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/Raycaster.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# pragma warning(push, 0)\r\n#  include <embree3/rtcore.h>\r\n#  include <embree3/rtcore_ray.h>\r\n#  include <xmmintrin.h>\t// functions for setting the control register\r\n#  include <pmmintrin.h>\t// functions for setting the control register\r\n# pragma warning(pop)\r\n\r\n# include <core/graphics/Mesh.hpp>\r\n# include <core/system/Matrix.hpp>\r\n# include \"core/raycaster/Config.hpp\"\r\n# include \"core/raycaster/Ray.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\t///\r\n\t/// This class can be used to cast rays against a scene containing triangular\r\n\t/// meshes. You can check for intersections with the geometry and get\r\n\t/// information about the hit (such as coordinates, distance, triangle id).\r\n\t///\r\n\t/// You should have one or few instance of this class (for performance\r\n\t/// purposes). Each instance can run in parallel.\r\n\t///\r\n\t/// \\note This abstraction is built on top of Embree.\r\n\t/// \\warning There is no backface culling applied.\r\n\t/// \\ingroup sibr_raycaster\r\n\t///\r\n\tclass SIBR_RAYCASTER_EXPORT Raycaster\r\n\t{\r\n\tpublic:\r\n\t\ttypedef std::shared_ptr<RTCDevice>\tRTCDevicePtr;\r\n\t\ttypedef std::shared_ptr<RTCScene>\t\tRTCScenePtr;\r\n\t\ttypedef std::shared_ptr<Raycaster>\t\tPtr;\r\n\r\n\t\ttypedef\tuint\tgeomId;\r\n\t\t/// Stores a number representing an invalid geom id.\r\n\t\tstatic const geomId InvalidGeomId; \r\n\r\n\t\t/// Destructor.\r\n\t\t~Raycaster( void );\r\n\r\n\r\n\t\t/// Init the raycaster.\r\n\t\t/// Called automatically whenever you call a member that need this\r\n\t\t/// instance to be init. However, you can call it manually to check\r\n\t\t/// error on init.\r\n\t\t/// \\param sceneType the type of scene, see Embree doc.\r\n\t\t/// \\return a success flag\r\n\t\tbool\tinit(RTCSceneFlags sceneType = RTC_SCENE_FLAG_NONE );\r\n\r\n\t\t/// Add a triangle mesh to the raycast scene, taht you won't modify frequently\r\n\t\t/// Return the id  of the geometry added so you can track your mesh (and compare\r\n\t\t/// its id to the one stored in RayHits).\r\n\t\t/// \\param mesh the mesh to add\r\n\t\t/// \\return the mesh ID or Raycaster::InvalidGeomId if it fails.\r\n\t\tgeomId\taddMesh( const sibr::Mesh& mesh );\r\n\r\n\t\t/// Add a triangle mesh to the raycast scene, that you will frequently update.\r\n\t\t/// \\param mesh the mesh to add\r\n\t\t/// \\return the mesh ID or Raycaster::InvalidGeomId if it fails.\r\n\t\tgeomId\taddDynamicMesh( const sibr::Mesh& mesh );\r\n\r\n\t\t/// Add a triangle mesh to the raycast scene.\r\n\t\t/// \\param mesh the mesh to add\r\n\t\t/// \\param type the type of mesh\r\n\t\t/// \\return the mesh ID or Raycaster::InvalidGeomId if it fails.\r\n\t\tgeomId\taddGenericMesh( const sibr::Mesh& mesh, RTCBuildQuality type );\r\n\r\n\t\t/// Transform the vertices of a mesh by applying a sibr::Matrix4f mat.\r\n\t\t/// \\note The original positions are always stored *unchanged* in mesh.vertices -- we only xform the vertices in the embree buffer\r\n\t\t/// \\param mesh the mesh to transform\r\n\t\t/// \\param mesh_id the corresponding raycaster mesh id\r\n\t\t/// \\param mat the transformation to apply\r\n\t\t/// \\param centerPt will contain the new centroid\r\n\t\t/// \\param maxlen will contain the maximum distance from a vertex to the centroid\r\n\t\t/// \\bug maxlen is computed incrementally and may be incorrect\r\n\t\tvoid xformRtcMeshOnly(sibr::Mesh& mesh, geomId mesh_id, sibr::Matrix4f& mat, sibr::Vector3f& centerPt, float& maxlen);\r\n\r\n\t\t/// Launch a ray into the raycaster scene. Return information about\r\n\t\t/// this cast in RayHit. To simply know if something has been hit, use RayHit::hitSomething().\r\n\t\t/// \\sa hitSomething\r\n\t\t/// \\param ray the ray to cast\r\n\t\t/// \\param minDist Any intersection closer than minDist from the ray origin will be ignored. Useful to avoid self intersections. \r\n\t\t/// \\return the (potential) intersection information\r\n\t\tRayHit\tintersect( const Ray& ray, float minDist=0.f  );\r\n\r\n\t\t/// Launch 8 rays into the raycaster scene in an optimized fashion, reporting intersections infos.\r\n\t\t/// \\param inray the rays to cast\r\n\t\t/// \\param valid8 an indication of which of the rays should be cast\r\n\t\t/// \\param minDist Any intersection closer than minDist from the ray origin will be ignored. Useful to avoid self intersections. \r\n\t\t/// \\return the list of (potential) intersection informations\r\n\t\tstd::array<RayHit, 8>\tintersect8(const std::array<Ray, 8>& inray,const std::vector<int> & valid8=std::vector<int>(8,-1), float minDist = 0.f );\r\n\r\n\t\t/// Optimized ray-cast that only tells you if an intersection occured.\r\n\t\t/// \\sa intersect\r\n\t\t/// \\param ray the ray to cast\r\n\t\t/// \\param minDist Any intersection closer than minDist from the ray origin will be ignored. Useful to avoid self intersections. \r\n\t\t/// \\return true if an intersection took place\r\n\t\tbool\thitSomething( const Ray& ray, float minDist=0.f );\r\n\r\n\t\t/// Launch 8 rays into the raycaster scene in an optimized fashion, reporting if intersections occured.\r\n\t\t/// \\param inray the rays to cast\r\n\t\t/// \\param minDist Any intersection closer than minDist from the ray origin will be ignored. Useful to avoid self intersections. \r\n\t\t/// \\return a list of boolean denoting if intersections happened\r\n\t\tstd::array<bool, 8>\thitSomething8(const std::array<Ray, 8>& inray, float minDist = 0.f);\r\n\r\n\t\t/// Disable geometry to avoid raycasting against it (eg background when only intersecting a foreground object).\r\n\t\t/// \\param id the mesh to disable\r\n\t\t/// \\todo Untested.\r\n\t\tvoid\tdisableGeom(geomId id) { rtcDisableGeometry(rtcGetGeometry((*_scene.get()),id)); rtcCommitGeometry(rtcGetGeometry(*_scene.get(),id)); rtcCommitScene(*_scene.get()); }\r\n\r\n\t\t/// Enable geometry to start raycasting it again.\r\n\t\t/// \\param id the geometry to enable \r\n\t\t/// \\todo Untested.\r\n\t\tvoid\tenableGeom(geomId id) { rtcEnableGeometry(rtcGetGeometry((*_scene.get()),id)); rtcCommitGeometry(rtcGetGeometry(*_scene.get(),id)); rtcCommitScene(*_scene.get());}\r\n\r\n\t\t/// Delete geometry\r\n\t\t/// \\param id the geometry to delete\r\n\t\tvoid\tdeleteGeom(geomId id) { rtcReleaseGeometry(rtcGetGeometry((*_scene.get()),id)); rtcCommitGeometry(rtcGetGeometry(*_scene.get(),id)); rtcCommitScene(*_scene.get());} \r\n\r\n\t\t/// Clears internal scene..\r\n\t\tvoid clearGeometry();\r\n\r\n\t\t/// Returns the normalized smooth normal (shading normal) from a hit, assuming the mesh has normals\r\n\t\t/// \\param mesh sibr::Mesh used by raycaster\r\n\t\t/// \\param hit intersection basic information\r\n\t\t/// \\return the interpolated normalized normal\r\n\t\tstatic sibr::Vector3f smoothNormal(const sibr::Mesh & mesh, const RayHit & hit);\r\n\r\n\t\t/// Interpolate color at a hit (barycentric interpolation), assuming the mesh has colors.\r\n\t\t/// \\param mesh sibr::Mesh used by raycaster\r\n\t\t/// \\param hit intersection basic information\r\n\t\t/// \\return the interpolated color\r\n\t\tstatic sibr::Vector3f smoothColor(const sibr::Mesh & mesh, const RayHit & hit);\r\n\r\n\t\t/// Interpolate texcoords from a hit (barycentric interpolation), assuming the mesh has UVs.\r\n\t\t/// \\param mesh sibr::Mesh used by raycaster\r\n\t\t/// \\param hit intersection basic information\r\n\t\t/// \\‚eturn the interpolated texture coordinates\r\n\t\tstatic sibr::Vector2f smoothUV(const sibr::Mesh & mesh, const RayHit & hit);\r\n\r\n\t\t/// \\return true if the raycaster is initialized. \r\n\t\tbool isInit() { return g_device && _scene; }\r\n\r\n\tprivate: \r\n\r\n\t\t/// Will be called by embree whenever an error occurs\r\n\t\t/// \\param userPtr the user data pointer\r\n\t\t/// \\param code the error code\r\n\t\t/// \\param msg additional info message.\r\n\t\tstatic void rtcErrorCallback(void* userPtr, RTCError code, const char* msg);\r\n\r\n\t\t\r\n\t\tstatic bool g_initRegisterFlag; ///< Used to initialize flag of registers used by SSE\r\n\t\tstatic RTCDevicePtr\tg_device;\t///< embree device (context for a raycaster)\r\n\r\n\t\t/// \\return the internal scene pointer\r\n\t\tRTCScenePtr\tscene() \t{ return _scene; }\r\n\r\n\t\tRTCScenePtr\t\t_scene;\t\t///< scene storing raycastable meshes\r\n\t\tRTCDevicePtr\t_devicePtr;\t///< embree device (context for a raycaster)\r\n\t};\r\n\r\n\t///// DEFINITION /////\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/VoxelGrid.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"VoxelGrid.hpp\"\n\nnamespace sibr {\n\n\tVoxelGridBase::VoxelGridBase(const Box & boundingBox, int n, bool forceCube)\n\t\t: VoxelGridBase(boundingBox, sibr::Vector3i(n, n, n), forceCube)\n\t{\n\t}\n\n\tVoxelGridBase::VoxelGridBase(const Box & boundingBox, const sibr::Vector3i & numsPerDim, bool forceCube)\n\t\t: box(boundingBox), dims(numsPerDim), _generator(0), _distribution(-1.0, 1.0)\n\t{\n\t\tif (forceCube) {\n\t\t\tfloat maxDimSize = box.sizes().cwiseQuotient(dims.cast<float>()).maxCoeff();\n\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\tdims[c] = (int)std::round(box.sizes()[c] / maxDimSize);\n\t\t\t}\n\t\t}\n\t\tcellSize = box.sizes().cwiseQuotient(dims.cast<float>());\n\t\tcellSizeNorm = cellSize.norm();\n\n\t\t//std::cout << dims << std::endl;\n\t\t//std::cout << getCellSize() << std::endl;\n\n\t\tstatic const sibr::Mesh::Triangles trianglesBorders =\n\t\t{\n\t\t\t{ 0,4, 4 },\n\t\t{ 5,1, 1 },\n\t\t{ 4,5, 5 },\n\t\t{ 0,1, 1 },\n\t\t{ 2,6, 6 },\n\t\t{ 7,3, 3 },\n\t\t{ 6,7, 7 },\n\t\t{ 2,3, 3 },\n\t\t{ 0,2, 2 },\n\t\t{ 1,3, 3 },\n\t\t{ 4,6, 6 },\n\t\t{ 5,7, 7 }\n\t\t};\n\n\n\t\tBox baseCell;\n\t\tbaseCell.extend(box.min());\n\t\tbaseCell.extend(box.min() + getCellSize());\n\n\n\t\tsibr::Mesh::Vertices vs(8);\n\t\tfor (int i = 0; i < 8; ++i) {\n\t\t\tvs[i] = baseCell.corner((Box::CornerType)i);\n\t\t}\n\n\t\tbaseCellMesh.reset(new sibr::Mesh(false));\n\t\tbaseCellMesh->vertices(vs);\n\t\tbaseCellMesh->triangles(trianglesBorders);\n\n\t\tstatic const sibr::Mesh::Triangles trianglesFilled =\n\t\t{\n\t\t\t{ 0,1,5 },\n\t\t{ 0,5,4 },\n\t\t{ 1,3,7 },\n\t\t{ 1,7,5 },\n\t\t{ 3,2,6 },\n\t\t{ 3,6,7 },\n\t\t{ 2,0,4 },\n\t\t{ 2,4,6 },\n\t\t{ 0,2,3 },\n\t\t{ 0,3,1 },\n\t\t{ 4,5,7 },\n\t\t{ 4,7,6 }\n\t\t};\n\n\n\t\tbaseCellMeshFilled.reset(new sibr::Mesh(false));\n\t\tbaseCellMeshFilled->vertices(vs);\n\t\tbaseCellMeshFilled->triangles(trianglesFilled);\n\t}\n\n\tbool VoxelGridBase::isInside(const sibr::Vector3f & worldPos) const\n\t{\n\t\treturn box.contains(worldPos);\n\t}\n\n\tbool VoxelGridBase::outOfBounds(const sibr::Vector3i & v) const\n\t{\n\t\treturn (v.array() < 0).any() || (v.array() >= dims.array()).any();\n\t\t//return v[0] < 0 || v[0] >= dims[0] || v[1] < 0 || v[1] >= dims[1] || v[2] < 0 || v[2] >= dims[2];\n\t}\n\n\tsize_t VoxelGridBase::getNumCells() const\n\t{\n\t\treturn (size_t)dims.prod();\n\t}\n\n\tconst sibr::Vector3i & VoxelGridBase::getDims() const\n\t{\n\t\treturn dims;\n\t}\n\n\tsibr::Vector3i VoxelGridBase::getCell(size_t cellId) const\n\t{\n\t\tif (cellId >= getNumCells()) {\n\t\t\tSIBR_ERR;\n\t\t}\n\n\t\tsibr::Vector3i cell;\n\t\n\t\tstd::div_t div;\n\t\tfor (int i = 0; i < 2; ++i) {\n\t\t\tdiv = std::div((int)cellId, dims[i]);\n\t\t\tcell[i] = div.rem;\n\t\t\tcellId = div.quot;\n\t\t}\n\t\tcell[2] = (int)cellId;\n\n\t\tif (outOfBounds(cell)) {\n\t\t\tSIBR_ERR << cell << \" \" << dims;\n\t\t}\n\n\t\t//if ((cell.array() < 0).any() || (cell.array() >= dims.array()).any()) {\n\t\t//\tSIBR_ERR;\n\t\t//}\n\n\t\treturn cell;\n\t}\n\n\tsibr::Vector3i VoxelGridBase::getCell(const sibr::Vector3f & worldPos) const\n\t{\n\t\tsibr::Vector3f posUV = (worldPos - box.min()).cwiseQuotient(box.sizes());\n\t\tsibr::Vector3i cellCoord = (dims.cast<float>().cwiseProduct(posUV)).unaryExpr([](float f) { return std::floor(f); }).cast<int>();\n\n\t\tif ((cellCoord.array() < 0).any() || (cellCoord.array() >= dims.array()).any()) {\n\t\t\tSIBR_ERR;\n\t\t}\n\n\t\treturn cellCoord;\n\t}\n\n\tsibr::Vector3i VoxelGridBase::getCellInclusive(const sibr::Vector3f & worldPos) const\n\t{\n\t\tsibr::Vector3f posUV = (worldPos - box.min()).cwiseQuotient(box.sizes());\n\t\tsibr::Vector3i cellCoord = (dims.cast<float>().cwiseProduct(posUV).unaryExpr([](float f) { return std::floor(f); })).cast<int>();\n\n\t\t//because of the floor function, a pixel exactly at the boundary would be outside\n\t\tfor (int c = 0; c < 3; c++) {\n\t\t\tif (cellCoord[c] == -1){\n\t\t\t\t++cellCoord[c];\n\t\t\t}\n\t\t\tif (cellCoord[c] == dims[c]) {\n\t\t\t\t--cellCoord[c];\n\t\t\t}\n\t\t}\n\n\t\tif ((cellCoord.array() < 0).any() || (cellCoord.array() >= dims.array()).any()) {\n\t\t\tSIBR_ERR << worldPos << \" \" << box.min() << \" \" << box.max() << \" \" << cellCoord;\n\t\t}\n\n\t\treturn cellCoord;\n\t}\n\n\tstd::vector<size_t> VoxelGridBase::rayMarch(const Ray & ray) const\n\t{\n\t\tsibr::Vector3f start = ray.orig();\n\n\t\tif (!isInside(start)) {\n\t\t\tsibr::Vector3f intersection;\n\t\t\tif (intersectionWithBox(ray, intersection)) {\n\t\t\t\tstart = intersection;\n\t\t\t} else {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t}\n\t\t\n\t\tstart = start.cwiseMax(box.min()).cwiseMin(box.max() - 0.01f*getCellSize());\n\n\t\tsibr::Vector3i currentVoxel = getCell(start);\n\t\n\t\tsibr::Vector3i steps = ray.dir().unaryExpr([](float f) { return f >= 0 ? 1 : -1; }).cast<int>();\n\n\t\tconst sibr::Vector3f deltas = getCellSize().cwiseQuotient(ray.dir().cwiseAbs());\n\t\tconst sibr::Vector3f frac = (start - box.min()).cwiseQuotient(getCellSize()).unaryExpr([](float f) { return f - std::floor(f); });\n\t\tsibr::Vector3i finalVoxels; \n\t\tsibr::Vector3f ts;\n\t\tfor (int c = 0; c < 3; c++) {\n\t\t\tts[c] = deltas[c] * (ray.dir()[c] >= 0 ? 1.0f - frac[c] : frac[c]);\n\t\t\tfinalVoxels[c] = (ray.dir()[c] >= 0 ? dims[c] : -1);\n\t\t}\n\n\t\tstd::vector<size_t> visitedCellsIds;\n\t\twhile (true) {\n\t\t\tvisitedCellsIds.push_back(getCellId(currentVoxel));\n\t\t\n\t\t\tint c = getMinIndex(ts);\n\t\t\tcurrentVoxel[c] += steps[c];\n\t\t\tif (currentVoxel[c] == finalVoxels[c]) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tts[c] += deltas[c];\n\t\t}\n\n\t\treturn visitedCellsIds;\n\t}\n\n\tsibr::Mesh::Ptr VoxelGridBase::getCellMesh(const sibr::Vector3i & cell) const\n\t{\n\t\treturn getCellMeshInternal(cell, false);\n\t}\n\n\tsibr::Mesh::Ptr VoxelGridBase::getAllCellMesh() const\n\t{\n\t\treturn getAllCellMeshInternal(false);\n\t}\n\n\tsibr::Mesh::Ptr VoxelGridBase::getCellMeshFilled(const sibr::Vector3i & cell) const\n\t{\n\t\treturn getCellMeshInternal(cell, true);\n\t}\n\n\tsibr::Mesh::Ptr VoxelGridBase::getAllCellMeshFilled() const\n\t{\n\t\treturn getAllCellMeshInternal(true);\n\t}\n\n\tEigen::AlignedBox3f VoxelGridBase::getCellBox(size_t cellId) const\n\t{\n\t\tsibr::Vector3i cell = getCell(cellId);\n\t\tsibr::Vector3f center = getCellCenter(cell);\n\t\tsibr::Vector3f half_diagonal = 0.5f*getCellSize();\n\n\t\tEigen::AlignedBox3f out;\n\t\tout.extend(center - half_diagonal);\n\t\tout.extend(center + half_diagonal);\n\t\treturn out;\n\t}\n\n\tstd::vector<size_t> VoxelGridBase::getNeighbors(size_t cellId) const\n\t{\n\t\tstatic const sibr::Vector3f shifts[6] = {\n\t\t\t{-1, 0, 0}, { +1 ,0, 0 },\n\t\t\t{0, -1, 0},{0, +1, 0},\n\t\t\t{0, 0, -1},{0, 0, +1}\n\t\t};\n\n\t\tsibr::Vector3f pos = getCellCenter(cellId);\n\n\t\tstd::vector<size_t> n_ids;\n\t\tfor (int i = 0; i < 6; ++i) {\n\t\t\tsibr::Vector3f n_pos = pos + shifts[i].cwiseProduct(getCellSize());\n\t\t\tif (getBBox().contains(n_pos)) {\n\t\t\t\tn_ids.push_back(getCellId(n_pos));\n\t\t\t}\n\t\t}\n\t\treturn n_ids;\n\t}\n\n\tVoxelGridBase VoxelGridBase::extend(int numCells) const\n\t{\n\t\tsibr::Vector3f additionalSize = ((float)numCells)*getCellSize();\n\t\t//sibr::Vector3f half_diagonal = 0.5*box.diagonal() + additionalSize;\n\n\t\tBox extendedBox;\n\t\textendedBox.extend(box.max() + additionalSize);\n\t\textendedBox.extend(box.min() - additionalSize);\n\n\t\t//extendedBox.extend(box.center() + (additionalSize.norm() + 0.5f*box.diagonal().norm()) *box.diagonal().normalized());\n\t\t//extendedBox.extend(box.center() - (additionalSize.norm() + 0.5f*box.diagonal().norm()) *box.diagonal().normalized());\n\t\t\n\t\tVoxelGridBase extendedGrid = VoxelGridBase(extendedBox, dims.array() + 2*numCells);\n\t\treturn extendedGrid;\n\t}\n\n\tbool VoxelGridBase::intersectionWithBox(const Ray & ray, sibr::Vector3f & intersection) const\n\t{\n\t\t//adpated from https://github.com/papaboo/smalldacrt/\n\n\t\tsibr::Vector3f minTs = (box.min() - ray.orig()).cwiseQuotient(ray.dir());\n\t\tsibr::Vector3f maxTs = (box.max() - ray.orig()).cwiseQuotient(ray.dir());\n\n\t\tfloat nearT = (minTs.cwiseMin(maxTs)).maxCoeff();\n\t\tfloat farT = (minTs.cwiseMax(maxTs)).minCoeff();\n\n\t\tif (nearT <= farT && 0 <= nearT) {\n\t\t\tintersection = ray.orig() + nearT*ray.dir();\n\t\t\treturn true;\n\t\t} \n\t\treturn false;\t\n\t}\n\n\tconst sibr::Vector3f & VoxelGridBase::getCellSize() const\n\t{\n\t\treturn cellSize;\n\t}\n\n\tfloat VoxelGridBase::getCellSizeNorm() const\n\t{\n\t\treturn cellSizeNorm;\n\t}\n\n\tsibr::Vector3f VoxelGridBase::sampleCell(size_t cellId)\n\t{\n\t\tsibr::Vector3f out;\n\t\tout[0] = float(_distribution(_generator));\n\t\tout[1] = float(_distribution(_generator));\n\t\tout[2] = float(_distribution(_generator));\n\t\treturn getCellCenter(getCell(cellId)) + out.cwiseProduct(getCellSize());\n\t}\n\n\tsibr::Mesh::Ptr VoxelGridBase::getCellMeshInternal(const sibr::Vector3i & cell, bool filled) const\n\t{\n\t\tsibr::Mesh::Ptr baseMesh = filled ? baseCellMeshFilled : baseCellMesh;\n\n\t\tconst sibr::Vector3f offset = cell.cast<float>().array()*getCellSize().array();\n\n\t\tauto out = std::make_shared<sibr::Mesh>(true);\n\t\tout->triangles(baseMesh->triangles());\n\t\tsibr::Mesh::Vertices vs(8);\n\t\tfor (int i = 0; i < 8; ++i) {\n\t\t\tvs[i] = baseMesh->vertices()[i] + offset;\n\t\t}\n\t\tout->vertices(vs);\n\t\treturn out;\n\t}\n\n\tsibr::Mesh::Ptr VoxelGridBase::getAllCellMeshInternal(bool filled) const\n\t{\n\t\tauto out = std::make_shared<sibr::Mesh>();\n\n\t\tsibr::Mesh::Ptr baseMesh = filled ? baseCellMeshFilled : baseCellMesh;\n\n\t\tconst int numT = int(baseMesh->triangles().size());\n\t\tconst int numTtotal = int(getNumCells())*numT;\n\t\tconst int numV = int(baseMesh->vertices().size());\n\t\tconst int numVtotal = int(getNumCells())*numV;\n\t\tconst sibr::Vector3u offsetT = sibr::Vector3u(numV, numV, numV);\n\n\t\tsibr::Mesh::Vertices vs(numVtotal);\n\t\tsibr::Mesh::Triangles ts(numTtotal);\n\t\tfor (int i = 0; i < getNumCells(); ++i) {\n\t\t\tconst auto cell = getCell(i);\n\t\t\tconst sibr::Vector3f offsetV = cell.cast<float>().array()*getCellSize().array();\n\n\t\t\tfor (int v = 0; v < numV; ++v) {\n\t\t\t\tvs[i*numV + v] = baseMesh->vertices()[v] + offsetV;\n\t\t\t}\n\t\t\tfor (int t = 0; t < numT; ++t) {\n\t\t\t\tts[i*numT + t] = baseMesh->triangles()[t] + i * offsetT;\n\t\t\t}\n\t\t}\n\n\t\tout->vertices(vs);\n\t\tout->triangles(ts);\n\t\treturn out;\n\t}\n\n\tsize_t VoxelGridBase::getCellId(const sibr::Vector3i & v) const\n\t{\n\t\tif (outOfBounds(v)) {\n\t\t\tSIBR_ERR << v << \" \" << dims;\n\t\t}\n\t\treturn v[0] + dims[0] * (v[1] + dims[1] * v[2]); //v[2] + dims[2] * (v[1] + dims[1] * v[0]);\n\t}\n\n\tsize_t VoxelGridBase::getCellId(const sibr::Vector3f & world_pos) const\n\t{\n\t\treturn getCellId(getCell(world_pos));\n\t}\n\n\tsibr::Vector3f VoxelGridBase::getCellCenter(const sibr::Vector3i & cell) const\n\t{\n\t\treturn box.min() + (0.5f*sibr::Vector3f(1, 1, 1) + cell.cast<float>()).cwiseProduct(getCellSize());\n\t}\n\n\tsibr::Vector3f VoxelGridBase::getCellCenter(size_t cellId) const\n\t{\n\t\treturn getCellCenter(getCell(cellId));\n\t}\n\n\tint VoxelGridBase::getMinIndex(const sibr::Vector3f & v)\n\t{\n\t\tif (v.x() < v.y()) {\n\t\t\treturn v.x() < v.z() ? 0 : 2;\n\t\t} else {\n\t\t\treturn v.y() < v.z() ? 1 : 2;\n\t\t}\n\t}\n\n\tsibr::Vector3f orthoVector(const sibr::Vector3f & v)\n\t{\n\t\treturn std::abs(v[2]) < std::abs(v[0]) ? sibr::Vector3f(v[1], -v[0], 0) : sibr::Vector3f(0, -v[2], v[1]);\n\t}\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/VoxelGrid.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n\n# include <vector>\n#include <random>\n\n# include <core/raycaster/Config.hpp>\n\n#include <core/raycaster/Ray.hpp>\n#include <core/graphics/Mesh.hpp>\n\nnamespace sibr\n{\n\t/**\n\t\\addtogroup sibr_raycaster\n\t@{\n\t*/\n\n\t/** Generate a vector orthogonal to the input one.\n\t\\param v the input vector\n\t\\return the ortogonal vector\n\t\\warning The output vector is not necessarily of unit length.\n\t*/\n\tsibr::Vector3f orthoVector(const sibr::Vector3f & v);\n\n\t/** Default voxel type, storing binary occupancy. */\n\tstruct BasicVoxelType {\n\n\t\t/** \\return true if filled. */\n\t\toperator bool() const { return used; }\n\t\t\n\t\tbool used = true; ///< The voxel status.\n\t};\n\n\t/** Basic voxel grid interface provinding cell manipulation and query helpers. \n\tIt doesn't store any voxel data. */\n\tclass SIBR_RAYCASTER_EXPORT VoxelGridBase {\n\t\tSIBR_CLASS_PTR(VoxelGridBase);\n\n\tpublic:\n\n\t\ttypedef Eigen::AlignedBox<float, 3> Box;\n\n\t\t/** Constructor.\n\t\t\\param boundingBox bounding box delimiting the voxellized region\n\t\t\\param numPerDim number of voxels along each dimension\n\t\t\\param forceCube if true, the largest dimension will be split in numPerDim voxels and the other such that the voxels are cubes in world space\n\t\t*/\n\t\tVoxelGridBase(const Box & boundingBox, int numPerDim, bool forceCube = true);\n\n\n\t\t/** Constructor.\n\t\t\\param boundingBox bounding box delimiting the voxellized region\n\t\t\\param numsPerDim number of voxels along each dimension\n\t\t\\param forceCube if true, the largest dimension will be split in numPerDims voxels and the other such that the voxels are cubes in world space\n\t\t*/\n\t\tVoxelGridBase(const Box & boundingBox, const sibr::Vector3i & numsPerDim, bool forceCube = true);\n\n\t\t/** Check if a position is in the voxel grid.\n\t\t\\param worldPos the world 3D position\n\t\t\\return true if the position is in the grid bounding box\n\t\t*/\n\t\tbool isInside(const sibr::Vector3f & worldPos) const;\n\n\t\t/** Check if a set of indices correspond to a reachable voxel.\n\t\t\\param cell the voxel integer coordinates\n\t\t\\return true if the voxel exists in the grid\n\t\t*/\n\t\tbool outOfBounds(const sibr::Vector3i & cell) const;\n\n\t\t/** \\return the number of voxels. */\n\t\tsize_t getNumCells() const;\n\n\t\t/** \\return the number of voxels along each axis. */\n\t\tconst sibr::Vector3i & getDims() const;\n\n\t\t/** Convert a linear cell ID to a set of 3D indices.\n\t\t\\param cellId linear ID\n\t\t\\return the indices of the voxel along each axis\n\t\t*/\n\t\tsibr::Vector3i getCell(size_t cellId) const;\n\n\t\t/** Convert a voxel 3D indices to a linear ID.\n\t\t\\param cell the voxel integer coordinates\n\t\t\\return the linear ID\n\t\t*/\n\t\tsize_t getCellId(const sibr::Vector3i & cell) const;\n\n\t\t/** Convert a 3D position to the linear ID of the voxel containing it.\n\t\t\\param world_pos the position\n\t\t\\return the linear ID of the voxel\n\t\t*/\n\t\tsize_t getCellId(const sibr::Vector3f & world_pos) const;\n\n\t\t/** Get the position of a voxel center in world space.\n\t\t\\param cell the voxel integer coordinates\n\t\t\\return the center 3D position\n\t\t*/\n\t\tsibr::Vector3f getCellCenter(const sibr::Vector3i & cell) const;\n\n\t\t/** Get the position of a voxel center in world space.\n\t\t\\param cellId linear voxel ID\n\t\t\\return the center 3D position\n\t\t*/\n\t\tsibr::Vector3f getCellCenter(size_t cellId) const;\n\n\t\t/** Intersect a ray with the voxel grid, listing all intersected voxels.\n\t\t\\param ray the ray to cast\n\t\t\\return linear IDs of the intersected voxels\n\t\t*/\n\t\tstd::vector<size_t> rayMarch(const Ray & ray) const;\n\n\t\t/** Generate a wireframe mesh representing a voxel.\n\t\t\\param cell the voxel integer coordinates\n\t\t\\return the generated wireframe cube mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getCellMesh(const sibr::Vector3i & cell) const;\n\n\t\t/** Generate a wireframe mesh representing all voxels.\n\t\t\\return the generated wireframe cube mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getAllCellMesh() const;\n\n\t\t/** Generate a triangle mesh representing a voxel.\n\t\t\\param cell the voxel integer coordinates\n\t\t\\return the generated filled cube mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getCellMeshFilled(const sibr::Vector3i & cell) const;\n\n\t\t/** Generate a triangle mesh representing all voxels.\n\t\t\\return the generated filled cube mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getAllCellMeshFilled() const;\n\n\t\t/** Get a voxel bounding box.\n\t\t\\param cellId the voxel linear index\n\t\t\\return the bounding box.\n\t\t*/\n\t\tEigen::AlignedBox3f getCellBox(size_t cellId) const;\n\n\t\t/** Get a voxel neighbors linear IDs.\n\t\t\\param cellId linear voxel ID\n\t\t\\return the linear IDs of the neigbors.\n\t\t*/\n\t\tstd::vector<size_t> getNeighbors(size_t cellId) const;\n\n\t\t/** Extend the voxel grid along all dimensions. \n\t\tThis means that if the initial count along a given axis was N, the new is N+2*numCells.\n\t\t\\param numCells the number of cells to add\n\t\t\\return the extended voxel grid\n\t\t*/\n\t\tVoxelGridBase extend(int numCells) const;\n\n\t\t/** \\return the voxel grid bounding box. */\n\t\tconst Box & getBBox() const { return box; }\n\n\t\t/** Return the index of the smallest coefficient of the input vector.\n\t\t\\param v the vector\n\t\t\\return the location of the minimum\n\t\t*/\n\t\tstatic int getMinIndex(const sibr::Vector3f & v);\n\t\t\n\t\t/** Get the integer coordinates of the cell containing a position.\n\t\t\\param worldPos the position\n\t\t\\return the cell integer coordinates\n\t\t*/\n\t\tsibr::Vector3i getCell(const sibr::Vector3f & worldPos) const;\n\n\t\t/** Get the integer coordinates of the cell containing a position.\n\t\tPositions along the boundaries of the voxel grid are considered as belonging to the closest cell.\n\t\t\\param worldPos the position\n\t\t\\return the cell integer coordinates\n\t\t*/\n\t\tsibr::Vector3i getCellInclusive(const sibr::Vector3f & worldPos) const;\n\n\t\t/** Check if a ray intersect the voxel grid.\n\t\t\\param ray the ray to cast\n\t\t\\param intersection will contain the intersection position if it exists\n\t\t\\return true if there is an intersection\n\t\t*/\n\t\tbool intersectionWithBox(const Ray & ray, sibr::Vector3f & intersection) const;\n\n\t\t/** \\return the size of a voxel. */\n\t\tconst sibr::Vector3f & getCellSize() const;\n\n\t\t/** \\return the length of a voxel diagonal. */\n\t\tfloat getCellSizeNorm() const;\n\n\t\t/** Sample a random position in a given voxel.\n\t\t\\param cellId the voxel to sample from\n\t\t\\return the sampled position\n\t\t\\note The random generator is seeded at 0 when creating the grid.\n\t\t\\warning The current implementation is sampling in center+(random(-1,1)^3)*cellSize.\n\t\t*/\n\t\tsibr::Vector3f sampleCell(size_t cellId);\n\n\tprotected:\n\n\t\t/** Helper to generate a voxel mesh.\n\t\t\\param cell the coordinates of the voxel to generate\n\t\t\\param filled should the mesh be wireframe (false) or faceted (true)\n\t\t\\return the generated mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getCellMeshInternal(const sibr::Vector3i & cell, bool filled) const;\n\n\t\t/** Helper to generate the voxel grid mesh.\n\t\t\\param filled should the mesh be wireframe (false) or faceted (true)\n\t\t\\return the generated mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getAllCellMeshInternal(bool filled) const;\n\n\t\tsibr::Vector3i dims; ///< Integer grid dimensions.\n\t\tsibr::Vector3f cellSize; ///< World space voxel size.\n\t\tfloat cellSizeNorm; ///< World space voxel diagonal length.\n\t\tBox box; ///< Grid bounding box.\n\t\tsibr::Mesh::Ptr baseCellMesh, baseCellMeshFilled; ///< Base meshes for visualisation.\n\n\t\tstd::mt19937 _generator; ///< Generator for sampling, seeded at 0.\n\t\tstd::uniform_real_distribution<double> _distribution; ///< (-1,1) distribution.\n\n\t};\n\n\n\t/** Voxel grid with custom data storage. */\n\ttemplate<typename CellType = BasicVoxelType> class VoxelGrid : public VoxelGridBase {\n\n\t\tSIBR_CLASS_PTR(VoxelGrid);\n\tpublic:\n\t\tusing VoxelType = CellType;\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t\\param boundingBox bounding box delimiting the voxellized region\n\t\t\\param numPerDim number of voxels along each dimension\n\t\t\\param forceCube if true, the largest dimension will be split in numPerDim voxels and the other such that the voxels are cubes in world space\n\t\t*/\n\t\tVoxelGrid(const Box & boundingBox, int numPerDim, bool forceCube = true)\n\t\t\t: VoxelGrid(boundingBox, sibr::Vector3i(numPerDim, numPerDim, numPerDim) , forceCube)\n\t\t{\n\t\t}\n\n\t\t/** Constructor.\n\t\t\\param boundingBox bounding box delimiting the voxellized region\n\t\t\\param numsPerDim number of voxels along each dimension\n\t\t\\param forceCube if true, the largest dimension will be split in numPerDim voxels and the other such that the voxels are cubes in world space\n\t\t*/\n\t\tVoxelGrid(const Box & boundingBox, const sibr::Vector3i & numsPerDim, bool forceCube = true)\n\t\t: VoxelGridBase(boundingBox, numsPerDim, forceCube) {\n\t\t\tdata.resize(getNumCells());\n\t\t}\n\n\t\t/** Get voxel at a given linear index.\n\t\t\\param cell_id the linear index\n\t\t\\return a reference to the voxel\n\t\t*/\n\t\tCellType & operator[](size_t cell_id) {\n\t\t\treturn data[cell_id];\n\t\t}\n\n\t\t/** Get voxel at a given linear index.\n\t\t\\param cell_id the linear index\n\t\t\\return a reference to the voxel\n\t\t*/\n\t\tconst CellType & operator[](size_t cell_id) const {\n\t\t\treturn data[cell_id];\n\t\t}\n\n\t\t/** Get voxel at given integer 3D coordinates.\n\t\t\\param x x integer coordinate\n\t\t\\param y y integer coordinate\n\t\t\\param z z integer coordinate\n\t\t\\return a reference to the voxel\n\t\t*/\n\t\tCellType & operator()(int x, int y, int z) {\n\t\t\tsibr::Vector3i v(x,y,z);\n\t\t\treturn data[getCellId(v)];\n\t\t}\n\n\t\t/** Get voxel at given integer 3D coordinates.\n\t\t\\param x x integer coordinate\n\t\t\\param y y integer coordinate\n\t\t\\param z z integer coordinate\n\t\t\\return a reference to the voxel\n\t\t*/\n\t\tconst CellType & operator()(int x, int y, int z) const {\n\t\t\tsibr::Vector3i v(x,y,z);\n\t\t\treturn data[getCellId(v)];\n\t\t}\n\n\t\t/** Get voxel at given integer 3D coordinates.\n\t\t\\param v integer coordinates\n\t\t\\return a reference to the voxel\n\t\t*/\n\t\tCellType & operator[](const sibr::Vector3i & v) {\n\t\t\treturn data[getCellId(v)];\n\t\t}\n\n\t\t/** Get voxel at given integer 3D coordinates.\n\t\t\\param v integer coordinates\n\t\t\\return a reference to the voxel\n\t\t*/\n\t\tconst CellType & operator[](const sibr::Vector3i & v) const {\n\t\t\treturn data[getCellId(v)];\n\t\t}\n\n\t\t/** Generate a mesh from all voxels satisfying a condition.\n\t\t\\param filled should the mesh be wireframe (false) or faceted (true)\n\t\t\\param func the predicate to evaluate, will receive as unique argument a voxel (CellType).\n\t\t\\return the generated mesh\n\t\t*/\n\t\ttemplate<typename FuncType>\n\t\tsibr::Mesh::Ptr getAllCellMeshWithCond(bool filled, const FuncType & func) const;\n\n\t\t/** Get cell meshes from their ids.\n\t\t\\param filled should the mesh be wireframe (false) or faceted (true)\n\t\t\\param cell_ids ids of cell meshes.\n\t\t\\return the generated mesh\n\t\t*/\n\t\tsibr::Mesh::Ptr getAllCellMeshWithIds(bool filled, std::vector<std::size_t> cell_ids) const;\n\n\t\t/** List the voxels that statisfy a condition (for instance fullness)\n\t\t\\param func the predicate to evaluate, will receive as unique argument a voxel (CellType).\n\t\t\\return a list of linear indices of all voxels such that func(voxel) is true.\n\t\t*/\n\t\ttemplate<typename FuncType>\n\t\tstd::vector<std::size_t> detect_non_empty_cells(const FuncType & func) const;\n\n\t\t/** \\return the voxel grid data. */\n\t\tconst std::vector<CellType> & getData() const {\n\t\t\treturn data;\n\t\t}\n\n\tprotected:\n\n\t\tstd::vector<CellType> data; ///< Voxels storage.\n\t};\n\n\n\n\ttemplate<typename CellType> template<typename FuncType>\n\tinline std::vector<std::size_t> VoxelGrid<CellType>::detect_non_empty_cells(const FuncType & func) const {\n\t\tstd::vector<std::size_t> out_ids;\n\t\tfor (size_t i = 0; i < data.size(); ++i) {\n\t\t\tif (func(data[i])) {\n\t\t\t\tout_ids.push_back(i);\n\t\t\t}\n\t\t}\n\t\treturn out_ids;\n\t}\n\n\ttemplate<typename CellType> template<typename FuncType>\n\tinline sibr::Mesh::Ptr VoxelGrid<CellType>::getAllCellMeshWithCond(bool filled, const FuncType & f) const\n\t{\n\t\tstd::vector<std::size_t> cell_ids = detect_non_empty_cells(f);\n\t\treturn getAllCellMeshWithIds(filled, cell_ids);\n\t}\n\n\t/** }@ */\n\n\ttemplate<typename CellType>\n\tinline sibr::Mesh::Ptr VoxelGrid<CellType>::getAllCellMeshWithIds(bool filled, std::vector<std::size_t> cell_ids) const\n\t{\n\t\tint numNonZero = (int)cell_ids.size();\n\n\t\tauto out = std::make_shared<sibr::Mesh>();\n\n\t\tsibr::Mesh::Ptr baseMesh = filled ? baseCellMeshFilled : baseCellMesh;\n\n\t\tconst int numT = (int)baseMesh->triangles().size();\n\t\tconst int numTtotal = numNonZero * numT;\n\t\tconst int numV = (int)baseMesh->vertices().size();\n\t\tconst int numVtotal = numNonZero * numV;\n\t\tconst sibr::Vector3u offsetT = sibr::Vector3u(numV, numV, numV);\n\n\t\tsibr::Mesh::Vertices vs(numVtotal);\n\t\tsibr::Mesh::Triangles ts(numTtotal);\n\t\tfor (int i = 0; i < numNonZero; ++i) {\n\t\t\tconst auto cell = getCell(cell_ids[i]);\n\t\t\tconst sibr::Vector3f offsetV = cell.cast<float>().array() * getCellSize().array();\n\n\t\t\tfor (int v = 0; v < numV; ++v) {\n\t\t\t\tvs[i * numV + v] = baseMesh->vertices()[v] + offsetV;\n\t\t\t}\n\t\t\tfor (int t = 0; t < numT; ++t) {\n\t\t\t\tts[i * numT + t] = baseMesh->triangles()[t] + i * offsetT;\n\t\t\t}\n\t\t}\n\n\t\tout->vertices(vs);\n\t\tout->triangles(ts);\n\t\treturn out;\n\t}\n\n} // namespace sibr\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/raycaster/sibr_raycaster.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_raycaster sibr_raycaster\n\n\t\\brief Raycasting utilities.\n\n\tThis module provides functionalities related to raycasting on 2D and 3D geometry. \n\tIt contains basic 2D intersection tests, a wrapper around the embree raycasting library (http://embree.github.io/), \n\tand helpers to perform raycasting over all pixels of an image.\n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/AddShadowRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <core/renderer/AddShadowRenderer.hpp>\n\nnamespace sibr { \n\tAddShadowRenderer::AddShadowRenderer( void )\n\t{\n\t\t_shader.init(\"AddShadowShader\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/texture.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/addshadow.frag\"));\n\t\t_paramInvProj.init(_shader, \"in_inv_proj\");\n\t\t_paramImgSize.init(_shader, \"in_image_size\");\n\t}\n\n\tvoid\tAddShadowRenderer::process(\n\t\tuint backgroundTextureID,\n\t\tuint foregroundTextureID,\n\t\tconst Vector2f& textureSize,\n\t\tconst Camera& camera,\n\n\t\tIRenderTarget& dst )\n\t{\n\t\tdst.bind();\n\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, foregroundTextureID );\n\t\tglActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, backgroundTextureID );\n\t\tglDisable(GL_DEPTH_TEST);\n\t\tglDisable(GL_BLEND);\n\t\tglDepthMask(GL_TRUE);\t\t// but write the current values\n\t\t_shader.begin();\n\t\t_paramInvProj.set(camera.invViewproj());\n\t\t_paramImgSize.set(textureSize);\n\t\tRenderUtility::renderScreenQuad();\n\t\t_shader.end();\n\n\t\tdst.unbind();\n\t}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/AddShadowRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Composite two rendered scenes while generating local cast shadows from the top one to the bottom one.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT AddShadowRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<AddShadowRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tAddShadowRenderer( void );\n\n\t\t/** Composite the two textures, they sjould contain color+depth information in the alpha channel.\n\t\t\\param backgroundTextureID handle of the background image\n\t\t\\param foregroundTextureID handle of the foreground image\n\t\t\\param textureSize the texture size (should be the same)\n\t\t\\param camera the viewpoint used\n\t\t\\param dst the destination rendertarget\n\t\t*/\n\t\tvoid\tprocess(\n\t\t\t/*input*/\tuint backgroundTextureID,\n\t\t\t\t\t\tuint foregroundTextureID,\n\t\t\t\t\t\tconst Vector2f& textureSize,\n\t\t\t\t\t\tconst Camera& camera,\n\t\t\t/*output*/\tIRenderTarget& dst );\n\n\tprivate:\n\n\t\tGLShader\t\t\t_shader; ///< Composite shader.\n\t\tGLParameter\t\t\t_paramInvProj; ///< Inverse proj matrix uniform.\n\t\tGLParameter\t\t\t_paramImgSize; ///< Image size uniform.\n\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/BinaryMeshRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"BinaryMeshRenderer.hpp\"\n\nnamespace sibr {\n\n\tBinaryMeshRenderer::BinaryMeshRenderer()\n\t{\n\t\tstd::string vertex_shader =\n\t\t\tSIBR_SHADER(420,\n\t\t\t\tuniform mat4 MVP;\n\t\tlayout(location = 0) in vec3 in_vertex;\n\t\tvoid main(void) {\n\t\t\tgl_Position = MVP * vec4(in_vertex, 1.0);\n\t\t}\n\t\t);\n\n\t\tstd::string fragment_shader = SIBR_SHADER(420,\n\t\t\tout vec4 out_color;\n\t\t\tuniform float epsilon;\n\t\tvoid main(void) {\n\t\t\tout_color = vec4(1, 1, 1, 1);\n\t\t\tgl_FragDepth = gl_FragCoord.z * (1.0 - epsilon);\n\t\t}\n\t\t);\n\n\t\t_shader.init(\"binaryMeshShader\", vertex_shader, fragment_shader);\n\t\t_paramMVP.init(_shader, \"MVP\");\n\t\tepsilon.init(_shader, \"epsilon\");\n\t}\n\n\tvoid BinaryMeshRenderer::process(const Mesh & mesh, const Camera & eye, IRenderTarget & dst)\n\t{\n\t\tdst.bind();\n\t\t_shader.begin();\n\t\t_paramMVP.set(eye.viewproj());\n\t\tepsilon.send();\n\n\t\tmesh.render(true, false);\n\t\t\n\t\t_shader.end();\n\t\tdst.unbind();\n\t}\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/BinaryMeshRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Render a binary mask of a mesh, with options to limit Z-fighting.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT BinaryMeshRenderer\n\t{\n\t\tSIBR_CLASS_PTR(BinaryMeshRenderer);\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tBinaryMeshRenderer();\n\n\t\t/** Render the mesh mask. \n\t\tRegions covered by the mesh will be filled with (1,1,1,1).\n\t\t\\param mesh the mesh to render\n\t\t\\param eye the viewpoint to use\n\t\t\\param dst the destination rendertarget\n\t\t*/\n\t\tvoid\tprocess( const Mesh& mesh, const Camera& eye, IRenderTarget& dst );\n\n\t\t/** Shift that can be used to modify the depth written, \n\t\tto avoid Z-fighting when rendering multiple masks of \n\t\tthe same mesh or combining masks.\n\t\tIf set to 0.0: no shift, if set to 1.0: all vertices sent to depth 0.0.\n\t\t\\return a reference to the shift\n\t\t*/\n\t\tfloat & getEpsilon() {\n\t\t\treturn epsilon.get();\n\t\t}\n\n\tprivate:\n\n\t\tGLShader\t\t\t\t\t_shader; ///< Mask shader.\n\t\tGLuniform<sibr::Matrix4f>\t_paramMVP; ///< MVP uniform.\n\t\tGLuniform<float>\t\t\tepsilon = 0; ///< Epsilon uniform.\n\t\t\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/BlurRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <core/renderer/BlurRenderer.hpp>\n\nnamespace sibr { \n\tBlurRenderer::BlurRenderer( void )\n\t{\n\t\t_shader.init(\"BlurShader\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/texture.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/blur.frag\"));\n\t\t_paramImgSize.init(_shader, \"in_image_size\");\n\t}\n\n\tvoid\tBlurRenderer::process( uint textureID, const Vector2f& textureSize, IRenderTarget& dst )\n\t{\n\t\tdst.bind();\n\n\t\tglDisable(GL_DEPTH_TEST);\n\t\tglDisable(GL_BLEND);\n\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID );\n\t\tglDisable(GL_DEPTH_TEST);\n\t\t_shader.begin();\n\t\t_paramImgSize.set(textureSize);\n\t\tRenderUtility::renderScreenQuad();\n\t\t_shader.end();\n\n\t\tdst.unbind();\n\t}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/BlurRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Texture.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Blur on color edges present in a texture.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT BlurRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<BlurRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tBlurRenderer( void );\n\n\t\t/** Process the texture.\n\t\t\\param textureID the texture to blur\n\t\t\\param textureSize the texture dimensions\n\t\t\\param dst the destination rendertarget\n\t\t*/\n\t\tvoid\tprocess(\n\t\t\t/*input*/\tuint textureID,\n\t\t\t/*input*/\tconst Vector2f& textureSize,\n\t\t\t/*output*/\tIRenderTarget& dst );\n\n\tprivate:\n\n\t\tGLShader\t\t\t_shader; ///< Blur shader.\n\t\tGLParameter\t\t\t_paramImgSize; ///< Texture size uniform.\n\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(sibr_renderer)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\" \"shaders/*.fp\" \"shaders/*.gp\" \"shaders/*.vp\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\" \"shaders/*.fp\" \"shaders/*.gp\" \"shaders/*.vp\")\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS} .)\nif(WIN32)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_scene\n)\nelse()\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\t${GLFW_LIBRARY}\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_scene\n)\nendif()\n\nadd_definitions( -DSIBR_EXP_RENDERER_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n\tINSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS \"${SHADERS}\"\n\tRSC_FOLDER \"core\"\n\tCOMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/ColoredMeshRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/Texture.hpp\"\n#include <core/renderer/ColoredMeshRenderer.hpp>\n\nnamespace sibr { \n\tColoredMeshRenderer::ColoredMeshRenderer( void )\n\t{\n\t\t_shader.init(\"ColoredMesh\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/colored_mesh.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/colored_mesh.frag\"));\n\t\t_paramMVP.init(_shader,\"MVP\");\n\t}\n\n\tvoid\tColoredMeshRenderer::process( const Mesh& mesh, const Camera& eye, IRenderTarget& target, sibr::Mesh::RenderMode mode, bool backFaceCulling )\n\t{\n\t\t//glViewport(0.f, 0.f, target.w(), target.h());\n\t\ttarget.bind();\n\t\tglClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n\t\t_shader.begin();\n\t\t_paramMVP.set(eye.viewproj());\n\t\tmesh.render(true, backFaceCulling);\n\t\t_shader.end();\n\t\ttarget.unbind();\n\t}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/ColoredMeshRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Render a mesh colored using the per-vertex color attribute.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT ColoredMeshRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<ColoredMeshRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tColoredMeshRenderer( void );\n\n\t\t/** Render the mesh using its vertices colors, interpolated over triangles.\n\t\t\\param mesh the mesh to render\n\t\t\\param eye the viewpoint to use\n\t\t\\param dst the destination rendertarget\n\t\t\\param mode the rendering mode of the mesh\n\t\t\\param backFaceCulling should backface culling be performed\n\t\t*/\n\t\tvoid\tprocess(\n\t\t\t/*input*/\tconst Mesh& mesh,\n\t\t\t/*input*/\tconst Camera& eye,\n\t\t\t/*output*/\tIRenderTarget& dst,\n\t\t\t/*mode*/    sibr::Mesh::RenderMode mode = sibr::Mesh::FillRenderMode,\n\t\t\t/*BFC*/     bool backFaceCulling = true);\n\n\tprivate:\n\n\t\tGLShader\t\t\t_shader; ///< Color shader.\n\t\tGLParameter\t\t\t_paramMVP; ///< MVP uniform.\n\t\t\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_EXP_RENDERER_EXPORT\n#      ifdef SIBR_EXP_RENDERER_EXPORTS\n/* We are building this library */\n#        define SIBR_EXP_RENDERER_EXPORT __declspec(dllexport)\n#      else\n/* We are using this library */\n#        define SIBR_EXP_RENDERER_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_EXP_RENDERER_EXPORT\n# endif\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/CopyRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <core/renderer/CopyRenderer.hpp>\n\nnamespace sibr { \n\tCopyRenderer::CopyRenderer(const std::string& vertFile, const std::string& fragFile)\n\t{\n\t\t_shader.init(\"CopyShader\",\n\t\t\tsibr::loadFile(vertFile),\n\t\t\tsibr::loadFile(fragFile));\n\n\t\t_flip.init(_shader, \"flip\");\n\t}\n\n\tvoid\tCopyRenderer::process( uint textureID, IRenderTarget& dst, bool disableTest )\n\t{\n\t\tif (disableTest)\n\t\t\tglDisable(GL_DEPTH_TEST);\n\t\telse\n\t\t\tglEnable(GL_DEPTH_TEST);\n\n\t\t_shader.begin();\n\t\t_flip.send();\n\n\t\tdst.clear();\n\t\tdst.bind();\n\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID );\n\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\tdst.unbind();\n\t\t_shader.end();\n\t}\n\n\tvoid\tCopyRenderer::copyToWindow(uint textureID, Window& dst)\n\t{\n\t\tglDisable(GL_DEPTH_TEST);\n\n\t\t_shader.begin();\n\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID);\n\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t_shader.end();\n\t}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/CopyRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Window.hpp>\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Texture.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Copy the content of an input texture to another rendertarget or to the window.\n\tIf you need a basic copy, prefer using blit.\n\t\\sa sibr::blit\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT CopyRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<CopyRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/** Constructor. You can specify custom shaders, refer to noproj.vert and copy.frag for examples.\n\t\t\\param vertFile pah to the vertex shader file\n\t\t\\param fragFile pah to the fragment shader file\n\t\t*/\n\t\tCopyRenderer(\n\t\t\tconst std::string& vertFile = sibr::getShadersDirectory(\"core\") + \"/noproj.vert\",\n\t\t\tconst std::string& fragFile = sibr::getShadersDirectory(\"core\") + \"/copy.frag\"\n\t\t);\n\n\t\t/** Copy input texture to the output texture, copy also the input alpha into depth.\n\t\t\\param textureID the texture to copy\n\t\t\\param dst the destination\n\t\t\\param disableTest disable depth testing (depth won't be written)\n\t\t*/\n\t\tvoid\tprocess( uint textureID, IRenderTarget& dst,\n\t\t\tbool disableTest=true);\n\n\t\t/** Copy input texture to a window.\n\t\t\\param textureID the texture to copy\n\t\t\\param dst the destination window\n\t\t*/\n\t\tvoid\tcopyToWindow( uint textureID, Window& dst);\n\n\t\t/** \\return option to flip the texture when copying. */\n\t\tbool & flip() { return _flip.get(); }\n\n\tprivate:\n\t\t\n\t\tGLShader\t\t\t_shader; ///< Copy shader.\n\t\tGLuniform<bool>\t\t_flip = false; ///< Flip the texture when copying.\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/DepthRenderer.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n# include <core/renderer/DepthRenderer.hpp>\r\n# include \"core/graphics/RenderUtility.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\r\n\tDepthRenderer::~DepthRenderer() {};\r\n\r\n\tDepthRenderer::DepthRenderer(int w,int h) \r\n\t{\r\n\t\t_depthShader.init(\"DepthShader\", \r\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"depthRenderer.vp\")), \r\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"depthRenderer.fp\")));\r\n\r\n\t\t_depthShader_MVP.init(_depthShader,\"MVP\");\r\n\t\t_depth_RT.reset(new sibr::RenderTargetLum32F(w,h));\r\n\r\n\t}\r\n\r\n\tvoid DepthRenderer::render( const sibr::InputCamera& cam, const Mesh& mesh, bool backFaceCulling, bool frontFaceCulling)\r\n\t{\r\n\r\n\t\t//sibr::Vector1f cc(1.0);\r\n\t\t//_depth_RT->clear(cc);\r\n\t\t\r\n\t\tglViewport(0, 0, _depth_RT->w(), _depth_RT->h());\r\n\t\t_depth_RT->bind();\r\n\t\tglClearColor(1.0, 1.0, 1.0, 1.0);\r\n\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r\n\r\n\t\t_depthShader.begin();\r\n\t\t_depthShader_MVP.set(cam.viewproj());\r\n\r\n\t\tmesh.render(true, backFaceCulling, sibr::Mesh::FillRenderMode, frontFaceCulling);\r\n\r\n\t\t_depthShader.end();\r\n\r\n\t}\r\n\r\n} // namespace"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/DepthRenderer.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <core/renderer/Config.hpp>\r\n# include \"core/assets/InputCamera.hpp\"\r\n# include \"core/graphics/Texture.hpp\"\r\n# include \"core/graphics/Camera.hpp\"\r\n# include \"core/graphics/RenderUtility.hpp\"\r\n# include \"core/assets/Resources.hpp\"\r\n# include \"core/graphics/Shader.hpp\"\r\n# include \"core/graphics/Mesh.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/** Render a mesh to a depth rendertarget.\r\n\t\\ingroup sibr_renderer\r\n\t*/\r\n\tclass SIBR_EXP_RENDERER_EXPORT DepthRenderer\r\n\t{\r\n\t\r\n\tpublic:\r\n\t\t\r\n\t\tusing Ptr = std::shared_ptr<DepthRenderer>;\r\n\r\n\t\t/** Constructor with a target size.\r\n\t\t\\param w target width\r\n\t\t\\param h target height\r\n\t\t*/\r\n\t\tDepthRenderer(int w,int h)  ;\r\n\t\t\r\n\t\t/// Destructor.\r\n\t\t~DepthRenderer();\r\n\r\n\t\t/** Render a mesh depth in the result rendertarget.\r\n\t\t\\param cam the viewpoint to use\r\n\t\t\\param mesh the mesh to render\r\n\t\t\\param backFaceCulling should perform backface culling\r\n\t\t\\param frontFaceCulling flip culling test orientation\r\n\t\t*/\r\n\t\tvoid render( const sibr::InputCamera &cam, const Mesh& mesh, bool backFaceCulling=false, bool frontFaceCulling=false);\r\n\r\n\t\tstd::shared_ptr<sibr::RenderTargetLum32F> _depth_RT; ///< The result depth rendertarget.\r\n\r\n\tprivate:\r\n\r\n\t\tsibr::GLShader\t\t\t\t_depthShader; ///< Depth shader.\r\n\t\tsibr::GLParameter\t\t\t_depthShader_MVP; ///< Shader MVP.\r\n\r\n\t};\r\n\r\n} // namespace\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/NormalRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n# include <core/renderer/NormalRenderer.hpp>\n# include \"core/graphics/RenderUtility.hpp\"\n\n# define USE_PIXELART_MODEN 0 // just for fun (and e-art!)\n\nnamespace sibr\n{\n\n\tNormalRenderer::~NormalRenderer() {};\n\n\tNormalRenderer::NormalRenderer(int w, int h, bool generate, bool useFloats, bool imSpace)\n\t{\n\t\t_generate = generate;\n\t\t_useFloats = useFloats;\n\n\t\tif (_generate) {\n\n\t\t\t_normalShader.init(\"NormalShader\",\n\t\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"normalRendererGen.vp\")),\n\t\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"normalRenderer.fp\")),\n\t\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"normalRendererGen.gp\")));\n\n\t\t\t_normalShader_projInv.init(_normalShader, \"MVPinv\");\n\t\t}\n\t\telse {\n\n\t\t\t_normalShader.init(\"NormalShader\",\n\t\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"normalRenderer.vp\")),\n\t\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"normalRenderer.fp\")));\n\t\t}\n\n\t\t_normalShader_proj.init(_normalShader, \"MVP\");\n\t\t_normalShader_view.init(_normalShader, \"V\");\n\t\t_normalShader_model.init(_normalShader, \"M\");\n\t\t_normalShader_imSpace.init(_normalShader, \"imSpaceNormals\");\n\t\t_normalShader.begin();\n\t\t_normalShader_imSpace.set(imSpace);\n\t\t_normalShader.end();\n\n\t\tif (_useFloats) {\n\t\t\t_normal_RT_32F.reset(new sibr::RenderTargetRGBA32F(w, h));\n\t\t} else {\n\t\t\t_normal_RT.reset(new sibr::RenderTargetRGB(w, h));\n\t\t}\n\t\t\n\n\t}\n\n\tvoid NormalRenderer::setWH(int w, int h) {\n\t\tif (_useFloats) {\n\t\t\t_normal_RT_32F.reset(new sibr::RenderTargetRGBA32F(w, h));\n\t\t}\n\t\telse {\n\t\t\t_normal_RT.reset(new sibr::RenderTargetRGB(w, h));\n\t\t}\n\t}\n\n\tvoid NormalRenderer::render(const sibr::InputCamera& cam, const Mesh& mesh, const Matrix4f &modelMat, bool clear)\n\t{\n#if USE_PIXELART_MODEN\n\t\tglPointSize(10.f);\n#else\n\t\tglPointSize(2.f);\n#endif\n\n\t\tif (_useFloats) {\n\t\t\tif(clear)\n\t\t\t\t_normal_RT_32F->clear(sibr::Vector4f(0.5f,0.5f,0.5f,1.0f));\n\t\t\tglViewport(0, 0, _normal_RT_32F->w(), _normal_RT_32F->h());\n\t\t\t_normal_RT_32F->bind();\n\t\t} else {\n\t\t\t_normal_RT->bind();\n\t\t\tif (clear) {\n\t\t\t\tglClearColor(0.5f, 0.5f, 0.5f, 1.0f);\n\t\t\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\t\t}\n\t\t\tglViewport(0, 0, _normal_RT->w(), _normal_RT->h());\n\t\t\tglScissor(0, 0, _normal_RT->w(), _normal_RT->h());\n\t\t}\n\n\t\t_normalShader.begin();\n\t\tconst Matrix4f MVP = cam.viewproj() * modelMat;\n\t\t_normalShader_proj.set(MVP);\n\t\t_normalShader_view.set(cam.view());\n\t\t_normalShader_model.set(modelMat);\n\n\t\tif (_generate) {\n\t\t\tconst Matrix4f MVPinv = (cam.viewproj()*modelMat).inverse();\n\t\t\t_normalShader_projInv.set(MVPinv);\n\t\t}\n\n\t//\tstd::cout << cam.znear() << \" \" << cam.zfar() << \" \" << cam.viewproj() << std::endl;\n\t\tmesh.render(true, true, sibr::Mesh::FillRenderMode);\n\n\t\t_normalShader.end();\n\n\t}\n\n} // namespace"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/NormalRenderer.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <core/renderer/Config.hpp>\r\n\r\n# include \"core/assets/InputCamera.hpp\"\r\n# include \"core/graphics/Texture.hpp\"\r\n# include \"core/graphics/Camera.hpp\"\r\n# include \"core/graphics/RenderUtility.hpp\"\r\n# include \"core/assets/Resources.hpp\"\r\n# include \"core/graphics/Shader.hpp\"\r\n# include \"core/graphics/Mesh.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/** Render the world or view space normals of a mesh.\r\n\t\\ingroup sibr_renderer\r\n\t*/\r\n\tclass SIBR_EXP_RENDERER_EXPORT NormalRenderer\r\n\t{\r\n\r\n\tpublic:\r\n\t\t\r\n\t\t/** Constructor.\r\n\t\t\\param w target width\r\n\t\t\\param h target height\r\n\t\t\\param generate if true, use a geoemtry shader to compute normals on the fly, else use vertex normals\r\n\t\t\\param useFloats if true, render in a 32F rendertarget, else use 8U\r\n\t\t\\param imSpace if true, render view space normals, else render world space normals\r\n\t\t*/\r\n\t\tNormalRenderer(int w,int h, bool generate = true, bool useFloats = false, bool imSpace = false) ;\r\n\t\t\r\n\t\t/// Destructor.\r\n\t\t~NormalRenderer();\r\n\r\n\t\t/** Render the mesh normals in the internal render target.\r\n\t\t\\param cam the viewpoint to use\r\n\t\t\\param mesh the mesh to render\r\n\t\t\\param modelMat additional model matrix\r\n\t\t\\param clear clear the rendertarget before rendering\r\n\t\t*/\r\n\t\tvoid render( const sibr::InputCamera &cam, const Mesh& mesh, const Matrix4f &modelMat = Matrix4f::Identity(), bool clear=true);\r\n\t\t\r\n\t\t/** Resize the internal rendertarget.\r\n\t\t\\param w the new width\r\n\t\t\\param h the new height\r\n\t\t*/\r\n\t\tvoid setWH(int w, int h);\r\n\t\t\r\n\t\tstd::shared_ptr<sibr::RenderTargetRGB> _normal_RT; ///< The low-precision normal result rendertarget (used if useFloats is false).\r\n\t\tstd::shared_ptr<sibr::RenderTargetRGBA32F> _normal_RT_32F; ///< The high-precision normal result rendertarget (used if useFloats is true).\r\n\r\n\tprivate:\r\n\r\n\t\tsibr::GLShader\t\t\t\t_normalShader; ///< Normal shader.\r\n\t\tsibr::GLParameter\t\t\t_normalShader_proj; ///< Projection matrix uniform.\r\n\t\tsibr::GLParameter\t\t\t_normalShader_view; ///< View matrix uniform.\r\n\t\tsibr::GLParameter\t\t\t_normalShader_model; ///< Model matrix uniform.\r\n\t\tsibr::GLParameter\t\t\t_normalShader_projInv; ///< Inverse projection matrix uniform.\r\n\t\tsibr::GLParameter\t\t\t_normalShader_imSpace; ///< View space toggle uniform.\r\n\t\tbool _generate; ///< Should normals be generated on the fly.\r\n\t\tbool _useFloats; ///< Should the normals be rendered to a 32F precision target.\r\n\t\t\r\n\t};\r\n\r\n\r\n} // namespace\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/PointBasedRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <core/renderer/PointBasedRenderer.hpp>\n\nnamespace sibr { \n\tPointBasedRenderer::PointBasedRenderer()\n\t{\t\n\t\t_shader.init(\"PointBased\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/alpha_points.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/alpha_points.frag\"));\n\t\t_paramMVP.init(_shader,\"mvp\");\n\t\t_paramAlpha.init(_shader,\"alpha\");\n\t\t_paramRadius.init(_shader,\"radius\");\n\t\t_paramUserColor.init(_shader,\"user_color\");\n\t}\n\n\tvoid PointBasedRenderer::meshToDevice(const Mesh& mesh)\n\t{\n\t\tmesh.forceBufferGLUpdate();\n\t}\n\n\tvoid\tPointBasedRenderer::process(const Mesh& mesh, const Camera& eye, IRenderTarget& dst, bool backfaceCull)\n\t{\n\t\tglEnable(GL_DEPTH_TEST);\n\t\tglEnable(GL_PROGRAM_POINT_SIZE);\n\t\tdst.bind();\n\t\t_shader.begin();\n\t\t_paramMVP.set(eye.viewproj());\n\t\t_paramAlpha.set(float(1.0));\n\t\t_paramRadius.set(3);\n\t\t_paramUserColor.set(Vector3f(.1, .1, 1.0));\n\n\t\tmesh.render_points();\n\t\t_shader.end();\n\t\tdst.unbind();\n\t\tglDisable(GL_PROGRAM_POINT_SIZE);\n\t\tglDisable(GL_DEPTH_TEST);\n\t}\n\n\tvoid\tPointBasedRenderer::process(const Mesh& mesh, const Camera& eye, const sibr::Matrix4f& model, IRenderTarget& dst, bool backfaceCull)\n\t{\n\t\tglEnable(GL_DEPTH_TEST);\n\t\tglEnable(GL_PROGRAM_POINT_SIZE);\n\t\tdst.bind();\n\t\t_shader.begin();\n\t\t_paramMVP.set(sibr::Matrix4f(eye.viewproj() * model));\n\t\t_paramAlpha.set(float(1.0));\n\t\t_paramRadius.set(2);\n\t\t_paramUserColor.set(Vector3f(.1, .1, 1.0));\n\t\tmesh.render_points();\n\t\t_shader.end();\n\t\tdst.unbind();\n\t\tglDisable(GL_PROGRAM_POINT_SIZE);\n\t\tglDisable(GL_DEPTH_TEST);\n\t}\n\n} /*namespace sibr*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/PointBasedRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Render a Point Cloud with colors\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT PointBasedRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<PointBasedRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t*/\n\t\tPointBasedRenderer();\n\n\t\tvoid\tmeshToDevice(const Mesh& mesh);\n\n\t\t/** Render the textured mesh.\n\t\t\\param mesh the mesh to render (should have UV attribute)\n\t\t\\param eye the viewpoint to use\n\t\t\\param dst destination rendertarget\n\t\t\\param backfaceCull should backface culling be performed\n\t\t*/\n\t\tvoid\tprocess(const Mesh& mesh, const Camera& eye, IRenderTarget& dst, bool backfaceCull = true);\n\n\t\t/** Render the textured mesh.\n\t\t\\param mesh the mesh to render (should have UV attribute)\n\t\t\\param eye the viewpoint to use\n\t\t\\param model additional transformation matrix\n\t\t\\param dst destination rendertarget\n\t\t\\param backfaceCull should backface culling be performed\n\t\t*/\n\t\tvoid process(const Mesh & mesh, const Camera & eye, const sibr::Matrix4f & model, IRenderTarget & dst, bool backfaceCull = true);\n\n\tprotected:\n\n\t\tGLShader\t\t\t_shader; ///< The point based shader.\n\t\tGLuniform<Matrix4f> \t_paramMVP; ///< MVP uniform.\n\t\tGLuniform<float> \t_paramAlpha; ///< Alpha uniform.\n\t\tGLuniform<int>\t\t_paramRadius; ///< Radius uniform.\n\t\tGLuniform<Vector3f>\t_paramUserColor;\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/PoissonRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/**\n*\n* Poisson synthesis.\n*/\n\n#include <utility>\n#include <algorithm>\n\n#include <core/assets/Resources.hpp>\n#include <core/graphics/RenderUtility.hpp>\n\n#include <core/renderer/PoissonRenderer.hpp>\n\nusing namespace sibr;\n\n/** Number of levels poisson multi-grid */\n#define POISSON_LEVELS 5\n\n/** Number of relaxation/jacobi iterations at each level */\n#define POISSON_ITERATIONS  2\n\n/** Ratio of successive levels of poisson multi-grid */\n#define MULTIGRID_SCALE 2\n\nnamespace sibr { \n\t// -----------------------------------------------------------------------\n\n\tPoissonRenderer ::PoissonRenderer ( uint w, uint h ) :\n\t\t_size(w, h)\n\t{\n\t\tstd::string vp = sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/texture.vert\");\n\t\t_jacobiShader  .init(\"Jacobi\",  vp, sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/poisson_jacobi.frag\"));\n\t\t_restrictShader.init(\"Restrict\",vp, sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/poisson_restrict.frag\"));\n\t\t_interpShader  .init(\"Interp\",  vp, sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/poisson_interp.frag\"));\n\t\t_divergShader  .init(\"Diverg\",  vp, sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/poisson_diverg.frag\"));\n\n\t\t// GLParameters\n\t\t_jacobi_weights.init(_jacobiShader,   \"weights\");\n\t\t_jacobi_scale.init(_jacobiShader, \"scale\");\n\t\t_restrict_scale.init(_restrictShader, \"scale\");\n\t\t_interp_scale  .init(_interpShader,   \"scale\");\n\n\t\t_poisson_div_RT.resize(POISSON_LEVELS);\n\t\tfor (uint i=0; i<_poisson_div_RT.size(); i++) {\n\t\t\tuint ww = std::max(1u, uint(w/pow( (float)MULTIGRID_SCALE, (int)i)));\n\t\t\tuint hh = std::max(1u, uint(h/pow( (float)MULTIGRID_SCALE, (int)i)));\n\t\t\t_poisson_div_RT[i].reset(new sibr::RenderTargetRGBA(ww,hh, SIBR_CLAMP_UVS));\n\t\t}\n\t\t_poisson_RT.reset(new sibr::RenderTargetRGBA(w,h, SIBR_CLAMP_UVS | SIBR_GPU_LINEAR_SAMPLING));\n\t\t_poisson_tmp_RT.reset(new sibr::RenderTargetRGBA(w,h, SIBR_CLAMP_UVS | SIBR_GPU_LINEAR_SAMPLING));\n\t\t_enableFix = true;\n\n\t}\n\n\t// -----------------------------------------------------------------------\n\n\tuint PoissonRenderer::render( uint texture )\n\t{\n\t\tglPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, \"Poisson filling\");\n\t\t// divergence of gradient map and dirichlet constraints\n\t\t_divergShader.begin();\n\t\t_poisson_div_RT[0]->clear();\n\t\t_poisson_div_RT[0]->bind();\n\t\tglViewport(0, 0, _poisson_div_RT[0]->w(), _poisson_div_RT[0]->h());\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture);\n\t\tRenderUtility::renderScreenQuad();\n\t\t_poisson_div_RT[0]->unbind();\n\t\t_divergShader.end();\n\n\t\t//  restrict the divergence\n\t\tfor (int k=0; k<int(_poisson_div_RT.size())-1; k++) {\n\t\t\t_restrictShader.begin();\n\t\t\t_poisson_div_RT[k+1]->clear();\n\t\t\t_poisson_div_RT[k+1]->bind();\n\t\t\tglViewport(0,0, _poisson_div_RT[k+1]->w(), _poisson_div_RT[k+1]->h());\n\t\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _poisson_div_RT[k]->texture());\n\t\t\t_restrict_scale.set(float(_poisson_div_RT[k]->w())/_poisson_div_RT[k+1]->w());\n\t\t\tRenderUtility::renderScreenQuad();\n\t\t\t_poisson_div_RT[k+1]->unbind();\n\t\t\t_restrictShader.end();\n\t\t}\n\n\t\t// perform jacobi iterations and upsample the result to higher level\n\t\tbool isFirst = _enableFix;\n\t\tfor (int k=(int)_poisson_div_RT.size()-1; k>=0; k--) {\n\t\t\tfor (uint i=0; i<POISSON_ITERATIONS; i++) {\n\t\t\t\tdouble h   =  pow( (float)MULTIGRID_SCALE, (int)k);            // Jacobi relaxation filter kernel taken from\n\t\t\t\tdouble hsq =  h*h;                               // Real-Time Gradient-Domain Painting, SIGGRAPH '08\n\t\t\t\tdouble xh0 = -2.1532 + 1.5070/h + 0.5882/hsq;    // http://graphics.cs.cmu.edu/projects/gradient-paint/\n\t\t\t\tdouble xh1 =  0.1138 + 0.9529/h + 1.5065/hsq;\n\t\t\t\tdouble xh  =  ((i%2 == 0) ? xh0 : xh1);\n\t\t\t\tdouble m   =  (-8*hsq - 4)/(3.0*hsq);\n\t\t\t\tdouble e   =  (hsq + 2)/(3.0*hsq);\n\t\t\t\tdouble c   =  (hsq - 1)/(3.0*hsq);\n\n\t\t\t\tstd::swap(_poisson_tmp_RT, _poisson_RT);\n\n\t\t\t\t_jacobiShader.begin();\n\t\t\t\t_poisson_RT->clear();\n\t\t\t\t_poisson_RT->bind();\n\t\t\t\tglViewport(0,0, _poisson_div_RT[k]->w(), _poisson_div_RT[k]->h());\n\t\t\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _poisson_tmp_RT->texture());\n\t\t\t\t_jacobi_weights.set((float)xh, (float)e, (float)c, (float)(1.0/(m-xh)));\n\t\t\t\t_jacobi_scale.set( isFirst ? (float(_poisson_tmp_RT->w()) / _poisson_div_RT[k]->w()) : 1.0f);\n\t\t\t\tRenderUtility::renderScreenQuad();\n\t\t\t\t_poisson_RT->unbind();\n\t\t\t\t_jacobiShader.end();\n\n\t\t\t\tisFirst = false;\n\t\t\t}\n\n\t\t\tif (k > 0) {\n\t\t\t\tstd::swap(_poisson_tmp_RT, _poisson_RT);\n\t\t\t\t_interpShader.begin();\n\t\t\t\t_poisson_RT->clear();\n\t\t\t\t_poisson_RT->bind();\n\t\t\t\tglViewport(0,0, _poisson_div_RT[k-1]->w(), _poisson_div_RT[k-1]->h());\n\t\t\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _poisson_tmp_RT->texture());\n\t\t\t\tglActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _poisson_div_RT[k-1]->texture());\n\t\t\t\t_interp_scale.set(float(_poisson_div_RT[k-1]->w()) / _poisson_div_RT[k]->w());\n\t\t\t\tRenderUtility::renderScreenQuad();\n\t\t\t\t_poisson_RT->unbind();\n\t\t\t\t_interpShader.end();\n\t\t\t} else {\n\t\t\t\tstd::swap(_poisson_tmp_RT, _poisson_RT);\n\t\t\t\t_interpShader.begin();\n\t\t\t\t_poisson_RT->clear();\n\t\t\t\t_poisson_RT->bind();\n\t\t\t\tglViewport(0,0, _poisson_RT->w(), _poisson_RT->h());\n\t\t\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _poisson_tmp_RT->texture());\n\t\t\t\tglActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texture);\n\t\t\t\t_interp_scale.set(1.0f);\n\t\t\t\tRenderUtility::renderScreenQuad();\n\t\t\t\t_poisson_RT->unbind();\n\t\t\t\t_interpShader.end();\n\t\t\t}\n\t\t}\n\t\tglPopDebugGroup();\n\t\treturn _poisson_RT->texture();\n\t}\n\n\tvoid\tPoissonRenderer::process( const RenderTargetRGBA::Ptr& src, RenderTargetRGBA::Ptr& dst )\n\t{\n\t\tSIBR_ASSERT(src != nullptr);\n\t\t/// \\todo TODO SR: support IRenderTarget instead of just RGBA\n\t\trender(src->texture());\n\t\tstd::swap(dst, _poisson_RT);\n\t}\n\n\tvoid\tPoissonRenderer::process( uint texID, RenderTargetRGBA::Ptr& dst )\n\t{\n\t\trender(texID);\n\t\tstd::swap(dst, _poisson_RT);\n\t}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/PoissonRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"Config.hpp\"\n\n# include <iostream>\n# include <vector>\n# include <memory>\n\n# include <core/graphics/Config.hpp>\n# include <core/graphics/Texture.hpp>\n# include \"core/graphics/Shader.hpp\"\n\nnamespace sibr { \n\n\t/**\n\t* Hole filling by poisson synthesis on an input texture;\n\t* contains all shaders, render targets and render passes.\n\t* All black pixels on the input texture are considered holes\n\t* and Poisson synthesis affects these pixels only, all\n\t* other pixels are treated at Dirichlet boundary conditions.\n\t* \\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT PoissonRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<PoissonRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/**\n\t\t* Initialize Poisson solvers render targets and shaders.\n\t\t* \\param w width of highest resolution multigrid level\n\t\t* \\param h height of highest resolution multigrid level\n\t\t*/\n\t\tPoissonRenderer ( uint w, uint h );\n\n\t\t/** Perform poisson filling.\n\t\t\\param src source rendertarget, black pixels will be filled\n\t\t\\param dst destination rendertarget\n\t\t*/\n\t\tvoid\tprocess(\n\t\t\t/*input*/\tconst RenderTargetRGBA::Ptr& src,\n\t\t\t/*ouput*/\tRenderTargetRGBA::Ptr& dst );\n\n\t\t/** Perform poisson filling.\n\t\t\\param texID source texture handle, black pixels will be filled\n\t\t\\param dst destination rendertarget\n\t\t*/\n\t\tvoid\tprocess(\n\t\t\t/*input*/\tuint texID,\n\t\t\t/*ouput*/\tRenderTargetRGBA::Ptr& dst );\n\n\t\t/**\n\t\t* \\return the size used for in/out textures (defined in ctor)\n\t\t*/\n\t\tconst Vector2i&\t\tgetSize( void ) const;\n\n\t\t/** If true, fix a bug caused by erroneous viewport when initializing the internal pyramid.\n\t\t\tLeft exposed for retrocompatibility reasons.\n\t\t\\return a reference to the bugfix toggle. */\n\t\tbool & enableFix() { return _enableFix; }\n\n\tprivate:\n\t\t/**\n\t\t* Render the full Poisson synthesis on the holes in texture 'tex'.\n\t\t* \\param tex OpenGL texture handle of input texture\n\t\t* \\returns OpenGL texture handle of texture containing Poisson synthesis solution\n\t\t*/\n\t\tuint render( uint tex );\n\n\t\t/** Size defined in the ctor */\n\t\tVector2i\t\t_size;\n\n\t\t/** Shader to perform Jacobi relaxations */\n\t\tsibr::GLShader\t_jacobiShader;\n\n\t\t/** Shader to downsample input texture and boundary conditions from\n\t\t* higher multigrid level to next lower level */\n\t\tsibr::GLShader\t_restrictShader;\n\n\t\t/** Shader to interpolate Poisson synthesis solution from\n\t\t* lower multigrid level to next higher level */\n\t\tsibr::GLShader\t_interpShader;\n\n\t\t/** Shader to compute divergence (second derivative) field of input texture */\n\t\tsibr::GLShader\t_divergShader;\n\n\t\t/** Render target to store Poisson synthesis result */\n\t\tRenderTargetRGBA::Ptr  _poisson_RT;\n\n\t\t/** Helper render target for \\p _poisson_RT to\n\t\t* perform ping-pong render passes during Jacobi relaxations */\n\t\tRenderTargetRGBA::Ptr  _poisson_tmp_RT;\n\n\t\t/** Dirichlet constraints for each multigrid level */\n\t\tstd::vector<RenderTargetRGBA::Ptr> _poisson_div_RT;\n\n\t\t/** Jacobi step parameters. */\n\t\tsibr::GLParameter _jacobi_weights, _jacobi_scale, _restrict_scale;\n\t\t/** Interpolation scale. */\n\t\tsibr::GLParameter _interp_scale;\n\n\t\t/** Enable the \"weird large regions of color\" bugfix. */\n\t\tbool _enableFix = true;\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/PositionRender.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n# include \"PositionRender.hpp\"\n\n#include <core/assets/Resources.hpp>\n\nnamespace sibr\n{\n\tPositionRenderer::PositionRenderer(int w,int h)\n\t{\n\t\t_shader.init(\"positionRendere\", \n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"positionRenderer.vert\")), \n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"positionRenderer.frag\")));\n\n\t\t_MVP.init(_shader,\"MVP\");\n\t\t_RT.reset(new sibr::RenderTargetRGB32F(w,h));\n\t}\n\n\tvoid PositionRenderer::render( const sibr::Camera& cam, const Mesh& mesh, bool backFaceCulling, bool frontFaceCulling)\n\t{\n\t\tglViewport(0, 0, _RT->w(), _RT->h());\n\t\t_RT->bind();\n\t\tglClearColor(0.0, 0.0, 0.0, 1.0);\n\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n\t\t_shader.begin();\n\t\t_MVP.set(cam.viewproj());\n\n\t\tmesh.render(true, backFaceCulling, sibr::Mesh::FillRenderMode, frontFaceCulling);\n\n\t\t_shader.end();\n\t\t_RT->unbind();\n\t}\n\n} // namespace"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/PositionRender.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\n\nnamespace sibr\n{\n\t/** Render the world space positions of a mesh surface.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT PositionRenderer\n\t{\n\t\tSIBR_CLASS_PTR(PositionRenderer);\n\n\tpublic:\n\n\t\t/** Constructor with a target size.\n\t\t\\param w the target width\n\t\t\\param h the target height\n\t\t*/\n\t\tPositionRenderer(int w,int h);\n\n\t\t/** Render the mesh world positions.\n\t\t\\param cam the viewpoint to use\n\t\t\\param mesh the mesh to render\n\t\t\\param backFaceCulling should backface culling be performed\n\t\t\\param frontFaceCulling flip the culling test orientation\n\t\t*/\n\t\tvoid render( const sibr::Camera &cam, const Mesh& mesh, bool backFaceCulling=false, bool frontFaceCulling=false);\n\n\t\t/** \\return the result rendertarget containing world space positions. */\n\t\tconst sibr::RenderTargetRGB32F::Ptr & getPositionsRT() { return _RT; }\n\n\tprivate:\n\n\t\tsibr::GLShader\t\t\t\t\t\t\t_shader; ///< The positions shader.\n\t\tsibr::GLuniform<sibr::Matrix4f>\t\t\t_MVP; ///< MVP uniform.\n\t\tsibr::RenderTargetRGB32F::Ptr\t\t\t_RT; ///< Destination render target.\n\t\t\n\t};\n\n} // namespace\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/RenderMaskHolder.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <core/renderer/RenderMaskHolder.hpp>\n#include <core/assets/Resources.hpp>\n\nnamespace sibr { \n\tvoid\tRenderMaskHolder::setMasks( const std::vector<MaskPtr>& masks )\n\t{\n\t\t_masks = masks;\n\t}\n\n\tconst std::vector<RenderMaskHolder::MaskPtr>&\tRenderMaskHolder::getMasks( void ) const\n\t{\n\t\treturn _masks;\n\t}\n\n\tbool\tRenderMaskHolder::useMasks( void ) const\n\t{\n\t\treturn _masks.empty() == false;\n\t}\n\n\tvoid \tRenderMaskHolder::uploadMaskGPU(sibr::ImageL8& img, int i, std::vector<RenderTargetLum::Ptr> & masks, bool invert) \n\t{\n\t\tsibr::GLShader textureShader;\n\t\ttextureShader.init(\"Texture\",\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"texture.vp\")), \n\t\t\tinvert ? sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/texture-invert.frag\") : sibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/texture.frag\"));\n\n\t\tstd::shared_ptr<sibr::RenderTargetLum> maskRTPtr;\n\t\tmaskRTPtr.reset(new sibr::RenderTargetLum(img.w(), img.h()));\n\n\t\timg.flipH();\n\t\tstd::shared_ptr<sibr::Texture2DLum> rawInputImage(new sibr::Texture2DLum(img));\n\t\timg.flipH();\n\n\t\tglViewport(0,0, img.w(), img.h());\n\t\tmaskRTPtr->clear();\n\t\tmaskRTPtr->bind();\n\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D, rawInputImage->handle());\n\n\t\tglDisable(GL_DEPTH_TEST);            \n\t\ttextureShader.begin();\n\t\tsibr::RenderUtility::renderScreenQuad();\n\t\ttextureShader.end();\n\n\t\tmaskRTPtr->unbind();\n/*\n\tmaskRTPtr->readBack(img);\n\tsibr::show(img);\n*/\n\t\tmasks.push_back(maskRTPtr);\n\t}\n\n\n\tvoid \tRenderMaskHolder::loadMasks(const sibr::BasicIBRScene::Ptr& ibrScene,  const std::string& maskDir,  \n\t\t\t\tconst std::string& preFileName, const std::string& postFileName, int w, int h)\n\t{\n\t\tif( boost::filesystem::exists(maskDir) ) {\n\n\t\tfor(int i=0; i<(int)ibrScene->cameras()->inputCameras().size(); i++ ) {\n\t\t\t\tsibr::ImageRGB mask;\n\t\t\t\tstd::string filename = maskDir + \"/\" + preFileName + sibr::imageIdToString(i) + postFileName;\n\t\t\t\n\t\t\t\tif( boost::filesystem::exists(filename))  {\n\t\t\t\t\tmask.load(filename,false);\n\t\t\t\t\t// Split the image in its channels and keep only the first.\n\t\t\t\t\tcv::Mat channels[3];\n\t\t\t\t\tcv::split(mask.toOpenCV(), channels);\n\t\t\t\t\tsibr::ImageL8 maskOneChan;\t\t\t\t\t\n\t\t\t\t\tmaskOneChan.fromOpenCV(channels[0]);\n\t\t\t\t\tuploadMaskGPU(maskOneChan, i, _masks, false);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif( ibrScene->cameras()->inputCameras()[i]->isActive() ) \n\t\t\t\t\t\tSIBR_ERR << \"[RenderMaskHolder] couldnt find \" << filename << std::endl;\n\t\t\t\t\telse { /// push back empty mask so array is consistent\n\t\t\t\t\t\t/// \\todo TODO GD -- this is wasteful, should fine better way\n\t\t\t\t\t\tstd::shared_ptr<sibr::RenderTargetLum> maskRTPtr;\n\t\t\t\t\t\tmaskRTPtr.reset(new sibr::RenderTargetLum(w, h));\n\t\t\t\t\t\t_masks.push_back(maskRTPtr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t}\n\n\t}\n\telse\n\t\tSIBR_ERR << \"[RenderMaskHolder] Cant find directory \" << maskDir << std::endl;\n\t}\n\n\tvoid\tRenderMaskHolderArray::setMasks(const MaskArrayPtr& masks)\n\t{\n\t\t_masks = masks;\n\t}\n\n\tconst RenderMaskHolderArray::MaskArrayPtr & RenderMaskHolderArray::getMasks(void) const {\n\t\treturn _masks;\n\t}\n\n\tvoid RenderMaskHolderArray::loadMasks(\n\t\tconst sibr::BasicIBRScene::Ptr& ibrScene,\n\t\tconst std::string& maskDir, const std::string& preFileName,\n\t\tconst std::string& postFileName, int w, int h\n\t) {\n\t\tstd::string maskdir = (maskDir == \"\" ? ibrScene->data()->basePathName() + \"/images/\" : maskDir);\n\n\t\tif (!boost::filesystem::exists(maskdir)) {\n\t\t\tSIBR_ERR << \"[RenderMaskHolder] Cant find directory \" << maskDir << std::endl;\n\t\t} else {\n\t\t\tint numInputImgs = (int)ibrScene->cameras()->inputCameras().size();\n\t\t\tstd::vector<cv::Mat> masks(numInputImgs);\n\t\t\tfor (int i = 0; i < numInputImgs; i++) {\n\t\t\t\tstd::string filename = maskDir + \"/\" + preFileName + sibr::imageIdToString(i) + postFileName;\n\n\t\t\t\tcv::Mat mask = cv::imread(filename);\n\n\t\t\t\tif (mask.empty()) {\n\t\t\t\t\tSIBR_ERR << \"[RenderMaskHolderArray] couldnt find or read \" << filename << std::endl;\n\t\t\t\t}\n\n\t\t\t\tcv::Mat channels[3];\n\t\t\t\tcv::split(mask, channels);\n\t\t\t\tif (w > 0 && h > 0) {\n\t\t\t\t\tcv::resize(channels[0], channels[0], cv::Size(w, h));\n\t\t\t\t}\n\t\t\t\tmasks[i] = channels[0];\n\t\t\t}\n\n\t\t\t_masks = MaskArrayPtr(new MaskArray(masks, SIBR_FLIP_TEXTURE));\n\t\t}\n\t}\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/RenderMaskHolder.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"Config.hpp\"\r\n# include <core/graphics/RenderTarget.hpp>\r\n# include <core/graphics/Shader.hpp>\r\n# include <core/graphics/Texture.hpp>\r\n# include <core/graphics/Image.hpp>\r\n# include <core/scene/BasicIBRScene.hpp>\r\n\r\nnamespace sibr { \r\n\r\n\t/** Store a set of masks associated to a set of images (dataset input images for instance), on the GPU.\r\n\tThis version uses a list of R8 rendertargets.\r\n\t\\note Might want to use textures instead of RTs here.\r\n\t\\ingroup sibr_renderer\r\n\t*/\r\n\tclass SIBR_EXP_RENDERER_EXPORT RenderMaskHolder\r\n\t{\r\n\t\ttypedef\tRenderTargetLum::Ptr\tMaskPtr;\r\n\tpublic:\r\n\r\n\t\t/** Update the masks\r\n\t\t\\param masks the new masks to use\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\t\t\tsetMasks( const std::vector<MaskPtr>& masks );\r\n\r\n\t\t/** \\return the masks rendertargets. */\r\n\t\tconst std::vector<MaskPtr>&\t\tgetMasks( void ) const;\r\n\r\n\t\t/** \\return true if masks are available (non empty list). */\r\n\t\tbool\t\t\t\t\t\t\tuseMasks( void ) const;\r\n\r\n\t\t/** Load masks from black and white images on disk.\r\n\t\t\\param ibrScene the dataset scene associated to the masks\r\n\t\t\\param maskDir the masks directory\r\n\t\t\\param preFileName mask filename prefix\r\n\t\t\\param postFileName mask filename suffix and extension\r\n\t\t\\param w target width\r\n\t\t\\param h target height\r\n\t\t*/\r\n\t\tvoid \t\t\t\t\t\t\tloadMasks(\r\n\t\t\t\t\t\t\t\t\t\t\tconst sibr::BasicIBRScene::Ptr& ibrScene, \r\n\t\t\t\t\t\t\t\t\t\t\tconst std::string& maskDir, const std::string& preFileName, \r\n\t\t\t\t\t\t\t\t\t\t\tconst std::string& postFileName, int w, int h);\r\n\r\n\t\t/** Upload a mask image to the GPU.\r\n\t\t\\param img the mask image to upload\r\n\t\t\\param i the mask index in the list\r\n\t\t\\param masks the uploaded masks list (will be updated)\r\n\t\t\\param invert should the mask be inverted\r\n\t\t**/\r\n\t    void \t\t\t\t\t\t\tuploadMaskGPU(sibr::ImageL8& img, int i, std::vector<RenderTargetLum::Ptr> & masks, bool invert) ;\r\n\r\n\tprivate:\r\n\r\n\t\tstd::vector<MaskPtr>\t_masks; ///< List of masks on the GPU.\r\n\r\n\t};\r\n\r\n\t/** Store a set of masks associated to a set of images (dataset input images for instance), on the GPU.\r\n\tThis version uses a R8 texture array.\r\n\t\\ingroup sibr_renderer\r\n\t*/\r\n\tclass SIBR_EXP_RENDERER_EXPORT RenderMaskHolderArray\r\n\t{\r\n\t\tusing MaskArray = sibr::Texture2DArrayLum;\r\n\t\tusing MaskArrayPtr = MaskArray::Ptr;\r\n\r\n\tpublic:\r\n\r\n\t\t/** Update the masks\r\n\t\t\\param masks the new masks to use\r\n\t\t*/\r\n\t\tvoid\t\t\t\t\t\t\tsetMasks(const MaskArrayPtr& masks);\r\n\r\n\t\t/** \\return the masks texture array. */\r\n\t\tconst MaskArrayPtr &\t\t\t\tgetMasks(void) const;\r\n\r\n\t\t/** Load masks from black and white images on disk.\r\n\t\t\\param ibrScene the dataset scene associated to the masks\r\n\t\t\\param maskDir the masks directory\r\n\t\t\\param preFileName mask filename prefix\r\n\t\t\\param postFileName mask filename suffix and extension\r\n\t\t\\param w target width\r\n\t\t\\param h target height\r\n\t\t*/\r\n\t\tvoid \t\t\t\t\t\t\tloadMasks(\r\n\t\t\tconst sibr::BasicIBRScene::Ptr& ibrScene,\r\n\t\t\tconst std::string& maskDir = \"\", const std::string& preFileName = \"masks\" ,\r\n\t\t\tconst std::string& postFileName = \"\", int w = -1, int h = -1\r\n\t\t);\r\n\r\n\tprotected:\r\n\r\n\t\tMaskArrayPtr _masks; ///< The masks texture array.\r\n\r\n\t};\r\n\r\n} /*namespace sibr*/ \r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/ShadowMapRenderer.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n# include \"ShadowMapRenderer.hpp\"\r\n# include \"core/graphics/RenderUtility.hpp\"\r\n\r\nfloat SUN_APP_DIAM = 0.5358f;\r\n\r\nnamespace sibr\r\n{\r\n\r\n\tShadowMapRenderer::~ShadowMapRenderer() {};\r\n\r\n\tShadowMapRenderer::ShadowMapRenderer(const sibr::InputCamera& depthMapCam, std::shared_ptr<sibr::RenderTargetLum32F> depthMap_RT):_depthMap_RT(depthMap_RT)\r\n\t{\r\n\t\t_shadowMapShader.init(\"ShadowMapShader\",\r\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"shadowMapRenderer.vp\")), \r\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"shadowMapRenderer.fp\")));\r\n\r\n\t\t_shadowMapShader_MVP.init(_shadowMapShader,\"MVP\");\r\n\t\t_depthMap_MVP.init(_shadowMapShader, \"depthMapMVP\");\r\n\t\t_depthMap_MVPinv.init(_shadowMapShader, \"depthMapMVPinv\");\r\n\t\t_depthMap_radius.init(_shadowMapShader, \"depthMapRadius\");\r\n\t\t_lightDir.init(_shadowMapShader, \"lightDir\");\r\n\t\t_bias_control.init(_shadowMapShader, \"biasControl\");\r\n\t\t_sun_app_radius.init(_shadowMapShader, \"sun_app_radius\");\r\n\r\n\t\tsibr::Vector3f toLight = -depthMapCam.dir();\r\n\t\t_shadowMapShader.begin();\r\n\t\t_depthMap_MVP.set(depthMapCam.viewproj());\r\n\t\t_depthMap_MVPinv.set(depthMapCam.invViewproj());\r\n\t\t_depthMap_radius.set(depthMapCam.orthoRight());\r\n\t\t_lightDir.set(toLight);\r\n\t\t_sun_app_radius.set(SUN_APP_DIAM/2.0f);\r\n\t\t_shadowMapShader.end();\r\n\t}\r\n\r\n\tvoid ShadowMapRenderer::render(int w, int h, const sibr::InputCamera& cam, const Mesh& mesh, float bias ) \r\n\t{\r\n\r\n\t\t//sibr::Vector1f cc(1.0);\r\n\t\t//_depth_RT->clear(cc);\r\n\r\n\t\t_shadowMap_RT.reset(new sibr::RenderTargetLum(w, h));\r\n\t\t\r\n\t\tglViewport(0, 0, _shadowMap_RT->w(), _shadowMap_RT->h());\r\n\t\t_shadowMap_RT->bind();\r\n\t\tglClearColor(1.0, 1.0, 1.0, 1.0);\r\n\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r\n\r\n\t\t_shadowMapShader.begin();\r\n\t\t_shadowMapShader_MVP.set(cam.viewproj());\r\n\t\t_bias_control.set(bias);\r\n\r\n\t\tglActiveTexture(GL_TEXTURE0);\r\n\t\tglBindTexture(GL_TEXTURE_2D, _depthMap_RT->texture());\r\n\r\n\t\tmesh.render(true, false, sibr::Mesh::FillRenderMode);\r\n\r\n\t\t_shadowMapShader.end();\r\n\r\n\t}\r\n\r\n} // namespace"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/ShadowMapRenderer.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <core/renderer/Config.hpp>\r\n\r\n# include \"core/assets/InputCamera.hpp\"\r\n# include \"core/graphics/Texture.hpp\"\r\n# include \"core/graphics/Camera.hpp\"\r\n# include \"core/graphics/RenderUtility.hpp\"\r\n# include \"core/assets/Resources.hpp\"\r\n# include \"core/graphics/Shader.hpp\"\r\n# include \"core/graphics/Mesh.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/** Render high quality soft shadows, designed to mimick the sun shadowing.\r\n\t\\note Soft shadowing require a lot of texture fetches that can impact performances.\r\n\t\\ingroup sibr_renderer\r\n\t*/\r\n\tclass SIBR_EXP_RENDERER_EXPORT ShadowMapRenderer\r\n\t{\r\n\r\n\tpublic:\r\n\r\n\t\t/** Constructor.\r\n\t\t\\param depthMapCam the light viewpoint\r\n\t\t\\param depthMap_RT depth map rendered from the light viewpoint\r\n\t\t*/\r\n\t\tShadowMapRenderer(const sibr::InputCamera& depthMapCam, std::shared_ptr<sibr::RenderTargetLum32F> depthMap_RT)  ;\r\n\t\t\r\n\t\t/// Destructor.\r\n\t\t~ShadowMapRenderer();\r\n\r\n\t\t/**\r\n\t\tRender soft sun shadows on the mesh, using the precomputed depth map.\r\n\t\t\\param w the target width\r\n\t\t\\param h the target height\r\n\t\t\\param cam the viewpoint to use\r\n\t\t\\param mesh the mesh to render\r\n\t\t\\param bias shadow acne bias\r\n\t\t*/\r\n\t\tvoid render(int w, int h,const sibr::InputCamera &cam, const Mesh& mesh, float bias= 0.0005f);\r\n\r\n\t\tstd::shared_ptr<sibr::RenderTargetLum> _shadowMap_RT; ///< Result containing the soft shadows.\r\n\r\n\t\tstd::shared_ptr<sibr::RenderTargetLum32F> _depthMap_RT; ///< Depth map rendered from the light viewpoint.\r\n\r\n\t\t\r\n\tprivate:\r\n\r\n\t\tsibr::GLShader\t\t\t\t_shadowMapShader; ///< Shadow rendering.\r\n\t\tsibr::GLParameter\t\t\t_shadowMapShader_MVP; ///< Final MVP uniform.\r\n\t\tsibr::GLParameter\t\t\t_depthMap_MVP; ///< Light MVP uniform.\r\n\t\tsibr::GLParameter\t\t\t_depthMap_MVPinv; ///< Light inverse MVP uniform.\r\n\t\tsibr::GLParameter\t\t\t_depthMap_radius; ///< Depth map radius.\r\n\t\tsibr::GLParameter\t\t\t_lightDir; ///< Light direction uniform.\r\n\t\tsibr::GLParameter\t\t\t_bias_control; ///< Bias uniform.\r\n\t\tsibr::GLParameter\t\t\t_sun_app_radius; ///< Sun radius (for soft shadows).\r\n\t\tstd::shared_ptr<sibr::Texture2DLum32F> _textureDepthMap; ///< Shadow map target (unused).\r\n\r\n\t};\r\n\r\n} // namespace\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/TexturedMeshRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <core/renderer/TexturedMeshRenderer.hpp>\n\nnamespace sibr { \n\tTexturedMeshRenderer::TexturedMeshRenderer( bool flipY )\n\t{\t\n\t\tif(flipY)\n\t\t_shader.init(\"TexturedMesh\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/textured_mesh_flipY.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/textured_mesh.frag\"));\n\t\telse\n\t\t_shader.init(\"TexturedMesh\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/textured_mesh.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/textured_mesh.frag\"));\n\t\t_paramMVP.init(_shader,\"MVP\");\n\t}\n\n\tvoid\tTexturedMeshRenderer::process(const Mesh& mesh, const Camera& eye, uint textureID, IRenderTarget& dst, bool backfaceCull)\n\t{\n\t\tdst.bind();\n\t\t_shader.begin();\n\t\t_paramMVP.set(eye.viewproj());\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID);\n\t\tmesh.render(true, backfaceCull);\n\t\t_shader.end();\n\t\tdst.unbind();\n\n\t}\n\n\tvoid\tTexturedMeshRenderer::process(const Mesh& mesh, const Camera& eye, const sibr::Matrix4f& model, uint textureID, IRenderTarget& dst, bool backfaceCull)\n\t{\n\t\tdst.bind();\n\t\t_shader.begin();\n\t\t_paramMVP.set(sibr::Matrix4f(eye.viewproj() * model));\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID);\n\t\tmesh.render(true, backfaceCull);\n\t\t_shader.end();\n\t\tdst.unbind();\n\n\t}\n\n} /*namespace sibr*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/TexturedMeshRenderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\t/** Render a textured mesh, using per-vertex texture coordinates.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass SIBR_EXP_RENDERER_EXPORT TexturedMeshRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<TexturedMeshRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t\\param flipY if set to true, UV coordinates will be flipped vertically.\n\t\t*/\n\t\tTexturedMeshRenderer(bool flipY = false );\n\n\t\t/** Render the textured mesh.\n\t\t\\param mesh the mesh to render (should have UV attribute)\n\t\t\\param eye the viewpoint to use\n\t\t\\param textureID handle of the texture to use\n\t\t\\param dst destination rendertarget\n\t\t\\param backfaceCull should backface culling be performed\n\t\t*/\n\t\tvoid\tprocess(const Mesh& mesh, const Camera& eye, uint textureID, IRenderTarget& dst, bool backfaceCull = true);\n\n\t\t/** Render the textured mesh.\n\t\t\\param mesh the mesh to render (should have UV attribute)\n\t\t\\param eye the viewpoint to use\n\t\t\\param model additional transformation matrix\n\t\t\\param textureID handle of the texture to use\n\t\t\\param dst destination rendertarget\n\t\t\\param backfaceCull should backface culling be performed\n\t\t*/\n\t\tvoid process(const Mesh & mesh, const Camera & eye, const sibr::Matrix4f & model, uint textureID, IRenderTarget & dst, bool backfaceCull = true);\n\n\tprotected:\n\n\t\tGLShader\t\t\t_shader; ///< The texture mesh shader.\n\t\tGLParameter\t\t\t_paramMVP; ///< MVP uniform.\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/addshadow.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n#define SHADOWPOWER_METHOD_ESTIM \t\t\t1\n#define SHADOWPOWER_METHOD_3D \t\t\t\t2\n#define SHADOWPOWER_METHOD_ESTIM_NONLINEAR \t3\n\n#define SHADOWPOWER_METHOD SHADOWPOWER_METHOD_ESTIM\n\nlayout(binding = 0) uniform sampler2D tex;\nlayout(binding = 1) uniform sampler2D firstPassRT; /// \\todo TODO: use ping pong buffering to update it with the last add\n\nuniform mat4 \tin_inv_proj;\nuniform vec2  \tin_image_size;\n\nlayout(location= 0) out vec4 out_color;\n\nin vec2 tex_coord;\n\n\n\n//const float blurSize = 1.0 / 1000.0;\n\nconst float constFgAdditionalOffset = -0.02; // note it should not be linear because of near/far+proj\nconst float constSmoothShadowDistAtten = 8.0;\n\nvec3 unproject(vec3 xyd, mat4 inv_proj) {\n    vec4 pxl = vec4(xyd,1.0)*vec4(2.0)-vec4(1.0); // [0,1] -> [-1,1]\n    vec4 obj = inv_proj * pxl;                    // unproject\n    return (obj.xyz/obj.w);\n}\n\nfloat \tsmoothShadowDist( float dist01 )\n{\n\tfloat v = 1.0 - max(0.0, min(1.0, dist01) );\n\treturn exp(log(v)*constSmoothShadowDistAtten);\n}\n\nvoid main(void) {\n\n//===========================================================================//\t\n//                                                                           //\n//\t\t\t\t\t\t\t   Adding Shadow                                 //\n//                                                                           //\n//===========================================================================//\t\n{\t\n\tconst float scanSizeX = (1.0 / in_image_size.x)*4.0; /// \\todo TODO: should be split into H and W and use image size\n\tconst float scanSizeY = (1.0 / in_image_size.y)*4.0; /// \\todo TODO: should be split into H and W and use image size\n\t\n\tvec4 bg = texture(firstPassRT,tex_coord); \t// background color\n\tvec4 fg = texture(tex,tex_coord);\t\t\t// foreground color (object to add)\n\tfloat bgDepth = bg.a;\n\tfloat outDepth = bgDepth;\n\tfloat fgDepth = fg.a+constFgAdditionalOffset;\n\n\t// By default set output values using bg\n    out_color = vec4(bg.rgb, 1.0);\n\t//out_color = vec4(bg.a, 0.0, 0.0, 1.0);\n\t//gl_FragDepth = bgDepth;\n    \n\tbool fgIsEmpty = (fg.r == 0 && fg.g == 0 && fg.b == 0);\n\t/// gl_FragDepth = 0;\n\t\n    if (fgIsEmpty == false)\n\t{\n\t\tout_color = vec4(fg.rgb, (bgDepth <= fgDepth)? 0.0 : 1.0);\n\t\t//fgDepth = bgDepth;\n\t\toutDepth = (bgDepth <= fgDepth)? bgDepth : fgDepth;\n\t\t//gl_FragDepth = fgDepth;\n\t\t/// out_color = vec4(fg.a, 0.0, 0.0, 1.0);\n\t}\n\telse\n\t{\t\t\n\t\t// Scan for non-empty pixels for determining the power\n\t\t// of the shadow.\n\t\t// 'non-empty' pixels are FULL black pixels\n\t\t\n\t\tconst int \tscanItCount = 8;\n\t\tconst int \tmaxScanablePixels = (scanItCount*2 + 1)*(scanItCount*2 + 1);\n\t\tconst float maxAxisX = (scanItCount*2 + 1)*scanSizeX;\n\t\tconst float maxAxisY = (scanItCount*2 + 1)*scanSizeY;\n\t\tconst float maxScanDist = maxAxisX*maxAxisX + maxAxisY*maxAxisY; \n\t\t\n\t\tfloat nearestDist = maxScanDist*2.0;//maxScanDist + 1.0;\n\t\tfloat nearestXs = 1.0;\n\t\tfloat nearestYs = 1.0;\n\t\tfloat nearestDepth = 0.0;\n\t\tfloat averageBgDepth = 0.0;\n\t\tfloat nonEmptyPixelFound = 0;\n\t\tfor (int x = -scanItCount; x <= scanItCount; x++)\n\t\t{\n\t\t\tfor (int y = -scanItCount; y <= scanItCount; y++)\n\t\t\t{\n\t\t\t\tfloat xs = x*scanSizeX;\n\t\t\t\tfloat ys = y*scanSizeY;\n\t\t\t\tfloat dist = (xs*xs + ys*ys);\n\t\t\t\tvec4 color = texture(tex, vec2(tex_coord.x+xs, tex_coord.y+ys));\n\t\t\t\tfloat sampleDepth =  color.a+constFgAdditionalOffset;\n\t\t\t\t\n\t\t\t\taverageBgDepth += sampleDepth;\n\t\t\t\t\n\t\t\t\tif ( (color.r == 0 && color.g == 0 && color.b == 0) == false\n//// [A] this one will cause you trouble with object in front of your shadow caster\n//\t\t\t\t)\n//// [B] this one will prevent you from casting shadow in front of your object\n////     but visually, you can make it fly a bit and it look like the under is in front\n\t\t\t&& sampleDepth <= bgDepth )\n//// [C] this one is the best in fidelity but requires a call to texture(...)\n//\t\t\t\t&& sampleDepth <= texture(firstPassRT, vec2(tex_coord.x+xs, tex_coord.y+ys)).a)\n\t\t\t\t{\n\t\t\t\t\tif (dist < nearestDist)\n\t\t\t\t\t{\n\t\t\t\t\t\tnearestXs = xs;\n\t\t\t\t\t\tnearestYs = ys;\n\t\t\t\t\t\t// nearestDist = dist;\n\t\t\t\t\t\t// nearestDepth = sampleDepth;\n\t\t\t\t\t}\n\n\t\t\t\t\t\n\t\t\t\t\t//nearestXs = min(xs, nearestXs);\n\t\t\t\t\t//nearestYs = min(ys, nearestYs); // note that stored nearestXs/Ys might stores an unexisting coordinate pair\n\t\t\t\t\tnearestDist = min(nearestDist, dist);\n\t\t\t\t\tnearestDepth += sampleDepth;//max(nearestDepth, sampleDepth);\n\t\t\t\t\t++nonEmptyPixelFound;\n\t\t\t\t\t\n\t\t\t\t\taverageBgDepth -= sampleDepth; // cancel this in this case\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tnearestDepth = nearestDepth/float(nonEmptyPixelFound);\n\t\taverageBgDepth = averageBgDepth/float(maxScanablePixels-nonEmptyPixelFound);\n\t\t\n\t\t//if (nearestDist > 0)\n\t\t{\n\t\t\t\n\t\t\t// Compute the shadow power\n\t\t\tfloat shadowPower = 1.0;\n\t\t\t\n\t\t\tfloat ratioNonEmptyPixelFound = float(nonEmptyPixelFound) / float(maxScanablePixels);\n\t\t\t// influence of the caster size\n\t\t\tshadowPower *= smoothstep(0.0, 0.5, ratioNonEmptyPixelFound);\n\t\t\t\n\t\t\t//if (nonEmptyPixelFound < 100)\n\t\t\t//\tshadowPower = 0.0;\n\t\t\t\n\t\t\t//shadowPower *= max(0.0, min(1.0, nonEmptyPixelFound/minPixelCaster ));\n\t\t\t\n\t\t\t// Dev Note for improving things\n\t\t\t// There are two way to implement the influence of the shadow caster/receiver distance.\n\t\t\t// [3d solution]\n\t\t\t// - one is to unproject both points and measure their distance in 3d world unit\n\t\t\t// [estim solution]\n\t\t\t// - another one is roughly estimate the effect\n\n#if SHADOWPOWER_METHOD == SHADOWPOWER_METHOD_ESTIM_NONLINEAR\t\t\t\n\t\t\t// [estim solution]\n\t\t\t// influence of the distance to the object that cast this shadow (slightly improve but not enough)\n\t\t\t//shadowPower *= smoothShadowDist(nearestDist/maxScanDist);\n\t\t\t// influence of the depth distance\n\t\t\tconst float maxDiffDepth = 0.04;//0.15; // because it's nonlinear, it will react differently depending on Z (and your dataset clipping planes's near/far)\n\t\t\tfloat diffDepth =  bgDepth-nearestDepth;\n\t\t\tdiffDepth = diffDepth / maxDiffDepth;\n\t\t\tfloat depthFactor = clamp(diffDepth, 0.0, 1.0);\n\t\t\tshadowPower *= (1.0 - depthFactor);\n#endif\t\t\t\n\t\t\t\n#if SHADOWPOWER_METHOD == SHADOWPOWER_METHOD_ESTIM\t\t\t\n\t\t\t// [estim solution]\n\t\t\t// influence of the distance to the object that cast this shadow (slightly improve but not enough)\n\t\t\t//shadowPower *= smoothShadowDist(nearestDist/maxScanDist);\n\t\t\t// influence of the depth distance\n\t\t\tconst float maxDist = 0.4; // in world unit\n\t\t\tvec3 bg2dPos = vec3(tex_coord.xy, bgDepth);\n\t\t\tvec3 bg3dPos = unproject(bg2dPos, in_inv_proj);\n\t\t\tvec3 caster2dPos = vec3(vec2(tex_coord.x+nearestXs, tex_coord.y+nearestYs), nearestDepth);\n\t\t\tvec3 caster3dPos = unproject(caster2dPos, in_inv_proj);\n\t\t\tfloat line = abs(caster3dPos.z-bg3dPos.z);\n\t\t\tfloat factorDist = 1.0 - max(0.0, min(1.0, line/maxDist) );\n\t\t\tshadowPower *= factorDist;\n#endif\t\t\t\n\t\t\t\n#if SHADOWPOWER_METHOD == SHADOWPOWER_METHOD_3D\n\t\t\t// [3d solution]\t\t\t\n\t\t\t// influence of the shadow receiver distance\n\t\t\tconst float maxDist = 0.25; // in world unit\n\t\t\tconst float maxDistSqr = maxDist*maxDist;\n\t\t\tvec3 bg2dPos = vec3(tex_coord.xy, bgDepth);\n\t\t\tvec3 bg3dPos = unproject(bg2dPos, in_inv_proj);\n\t\t\tvec3 caster2dPos = vec3(vec2(tex_coord.x+nearestXs, tex_coord.y+nearestYs), nearestDepth);\n\t\t\tvec3 caster3dPos = unproject(caster2dPos, in_inv_proj);\n\t\t\tvec3 line = caster3dPos-bg3dPos;\n\t\t\tfloat distSqr = line.x*line.x + line.y*line.y + line.z*line.z;\n\t\t\tfloat factorDist = 1.0 - max(0.0, min(1.0, distSqr/maxDistSqr) );\n\t\t\tshadowPower *= factorDist;\n#endif\t\t\t\n\t\t\t\n\t\t\n\t\t\t\n\t\t\tout_color = vec4(0, 0, 0, shadowPower / 1.5);\n\t\t\t\n\t\t\t// //out_color = vec4(fgZ/10.0, 0, 0, 1.0);\n\t\t\t// float nearPlane = 3.23569;\n\t\t\t// float farPlane = 17.1543;\n\t\t\t// //bgZ = (bg2dPos.z * bg2dPos.w - nearPlane) / (farPlane - nearPlane);\n\t\t\t// out_color = vec4(bg2dPos.z/farPlane, 0, 0, 1.0);\n\t\t\t//out_color = vec4(bgDepth, 0, 0, 1.0);\n\t\t\t//gl_FragDepth = nearestDepth;\n\t\t\t\n\t\t\t\n\t\t\t//gl_FragDepth = bgDepth + constFgAdditionalOffset - 1;\n\t\t\tfloat newDepth = bgDepth;// + constFgAdditionalOffset;\n\t\t\toutDepth = (newDepth <= bgDepth)? newDepth : bgDepth;\n\t\t}\n\t\t\n\t\t// if (nearestDist < 1.0)\n\t\t// {\n\t\t\t// out_color = vec4(nearestDepth, 0.0, 0.0, 1.0);\n\t\t\t// gl_FragDepth = 0;\n\t\t// }\t\t\n\t}\n\t\n\t// Simulate BLEND function (GL_ONE_MINUS_SRC_ALPHA)\n\t//out_color = vec4(bg.xyz + out_color.xyz/out_color.a, gl_FragDepth);\n\tout_color = vec4(bg.xyz*(1.0 - out_color.a) + out_color.xyz*out_color.a,  outDepth);\n\t//gl_FragDepth = outDepth;\n\t//out_color = bg;\n}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/blur.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\n\nuniform sampler2D \timage;\nuniform vec2  \t\tin_image_size;\n\n\nin vec2 tex_coord;\n\n\nvoid main(void) {\n\n\tvec4 color = texture(image, tex_coord);\t\n\t\n\tconst float blurPixelStep = 1.25;\n\tconst float blurSizeX = (1.0 / in_image_size.x)*blurPixelStep;\n\tconst float blurSizeY = (1.0 / in_image_size.y)*blurPixelStep;\n\t\n\tconst int nbVisit = 4;\n\tivec2 visit[nbVisit] = ivec2 [](\n\t// cross\n\t// ivec2( 0, -1),\n\t// ivec2( 0,  1),\n\t// ivec2( 1,  0),\n\t// ivec2(-1,  0)\n\t\n\t// corner (better, detect more edge)\n\tivec2(-1, -1),\n\tivec2(-1,  1),\n\tivec2( 1, -1),\n\tivec2( 1,  1)\n\t);\n\t\n\tvec4 avcolor;\n\tvec4 bgColor = vec4(color.xyz, 0.0);\n\tfor (int i = 0; i < nbVisit; ++i)\n\t{\n\t\tvec4 col = texture( image, \n\t\tvec2(tex_coord.x + visit[i].x* blurSizeX, tex_coord.y + visit[i].y * blurSizeY) );\n\t\t\n\t\tbgColor = (col.a > bgColor.a)? col : bgColor;\n\t\tavcolor += col;\n\t}\n\t\n\tavcolor /= float(nbVisit);\n\t\n\tfloat dColor = abs(avcolor.a - color.a);//length(avcolor - color);\n\tconst float maxDColor = 0.015f;\n\tfloat mixFactor = 1.0 - min(1.0, dColor/maxDColor);\n\tvec4 correction = bgColor;//mix(bgColor, avcolor, mixFactor);\n\n\t// [USEFUL FOR DEBUGGING]: show detected edge\n\t//out_color = (dColor > maxDColor)? vec4(1.0, 0.0, 1.0, 1.0) : color;\n\n\tout_color = (dColor > maxDColor)? mix(correction, color, 0.5) : color;\n\n\t// [USEFUL FOR DEBUGGING]: change nothing\n\t//out_color = color;\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/colored_mesh.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nout vec4 out_color;\n\nin vec3 vertColor;\n\nvoid main(void) {\n\tout_color = vec4(vertColor, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/colored_mesh.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 1) in vec3 in_color;\n\nout vec3 vertColor;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\t\n\tvertColor = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/copy.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\n\nuniform sampler2D image;\nuniform bool flip = false;\n\nin vec4 texcoord;\n\nvoid main(void)\n{\n    vec4 color   = texture(image, flip ? vec2(texcoord.x, 1.0 - texcoord.y) : texcoord.xy);\n    out_color    = color;//vec4(color.rgb, 1.0);\n    gl_FragDepth = color.w;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/copy_depth.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\n\nlayout(binding = 0) uniform sampler2D image;\n\nin vec4 texcoord;\n\nvoid main(void)\n{\n    vec4 color   = texture(image, texcoord.xy);\n\tout_color = vec4(vec3(color.r), 1.0);\n    gl_FragDepth = color.r;\n    //gl_FragDepth = color.r == 0? 1.0 : color.r;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/depthRenderer.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nout float out_depth;\n\nvoid main(void) {\n\t\n    out_depth = 2.0*gl_FragCoord.z-1.0;\n    //out_color = fragTexCoord.x*vec4(1.0,0.0,0.0,1.0);\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/depthRenderer.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\n\n\nvoid main(void) {\n\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\t//fragTexCoord = vec2(0.2,0.8);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/emotive_relight.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform vec3 lightDir;\nuniform vec3 cameraEye;\n\nlayout(binding=0) uniform sampler2D tex;\nlayout(binding=1) uniform sampler2D smTex;\n\n\nout vec4 out_color;\n\nin vec2 vertUV;\nin float vertAO;\nin vec3 VtoF_normal;\nin vec3 vertPos;\n\nfloat sRGB2LinF(float inF){\n\tif(inF<0.04045){\n\t\treturn inF/12.92;\n\t}\n\telse{\n\t\treturn pow((inF+0.055)/(1.055),2.4);\n\t}\n}\n\nfloat lin2sRGBF(float inF){\n\t\n\tif(inF<0.0031308){\n\t\treturn 12.92*inF;\n\t}\n\telse{\n\t\treturn 1.055*pow(inF,1.0/2.4)-0.055;\n\t}\n\t\n}\n\nvec4 sRGB2Lin(vec4 inVec){\n\treturn vec4(sRGB2LinF(inVec.x),sRGB2LinF(inVec.y),sRGB2LinF(inVec.z),inVec.w);\n}\n\nvec4 lin2sRGB(vec4 inVec){\n\treturn vec4(lin2sRGBF(inVec.x),lin2sRGBF(inVec.y),lin2sRGBF(inVec.z),inVec.w);\n}\n\nfloat getFogFactor(float d)\n{\n    const float FogMax = 70.0;\n    const float FogMin = 10.0;\n\n    if (d>=FogMax) return 1;\n    if (d<=FogMin) return 0;\n\n    return 1 - (FogMax - d) / (FogMax - FogMin);\n}\n\n\nvoid main(void) {\n\tvec2 uv = vertUV;\n\tuv.y = 1.0 - uv.y; /// \\todo TODO: Why Texture are flipped in y ?\n\n\tvec4 sky_color_lin = vec4(0.7,1.0,1.2,1.0)*sRGB2Lin(texture(tex, uv));\n\n\tfloat shadowVal=texture(smTex,gl_FragCoord.xy/textureSize(smTex,0).xy).x;\n\n\tvec4 sun_color_lin = max(0.0,dot(VtoF_normal,lightDir))*shadowVal*sRGB2Lin(texture(tex, uv))/(vertAO+0.0001);\n\n\tfloat d = distance(cameraEye, vertPos);\n    float alpha = getFogFactor(d);\n\n    vec4 composed_color = lin2sRGB(sun_color_lin+0.1*sky_color_lin);\n\n    out_color = mix(composed_color, vec4(0.5,0.5,0.5,1.0), alpha);\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/emotive_relight.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 1) in vec3 in_ao;\nlayout(location = 2) in vec2 in_uv;\nlayout(location = 3) in vec3 in_normal;\n\nout vec2 vertUV;\nout float vertAO;\nout vec3 VtoF_normal;\nout vec3 vertPos;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\t\n\tvertUV = in_uv;\n\tvertAO = in_ao.x;\n\tVtoF_normal = in_normal;\n\tvertPos = in_vertex;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/hdrEnvMap.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform sampler2D tex;\n\nout vec4 out_color;\n\nin vec2 vertUV;\n\nfloat lin2sRGBF(float inF){\n\t\n\tif(inF<0.0031308){\n\t\treturn 12.92*inF;\n\t}\n\telse{\n\t\treturn 1.055*pow(inF,1.0/2.4)-0.055;\n\t}\n\t\n}\n\nvec4 lin2sRGB(vec4 inVec){\n\treturn vec4(lin2sRGBF(inVec.x),lin2sRGBF(inVec.y),lin2sRGBF(inVec.z),inVec.w);\n}\n\nvoid main(void) {\n\tvec2 uv = vertUV;\n\tuv.y = 1.0 - uv.y; /// \\todo TODO: Why Texture are flipped in y ?\n\tout_color = lin2sRGB(texture(tex, uv));\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/hdrEnvMap.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 2) in vec2 in_uv;\n\nout vec2 vertUV;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\t\n\tvertUV = in_uv;\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/longlat.gp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 450\r\n\r\nlayout(triangles) in;\r\nlayout(triangle_strip, max_vertices=6) out;\r\n\r\nlayout(location = 1) in vec3 colors_tes[];\r\nlayout(location = 2) in vec2 coordsTex_tes[];\r\nlayout(location = 3) in vec3 normals_tes[];\r\n\r\nuniform vec3 pos;                           \r\n\r\nconst float PI = 3.1415926535897932384626433832795;   \r\n\r\nlayout(location = 0) out vec4 position;\r\nlayout(location = 1) out vec3 colors_gs;\r\nlayout(location = 2) out vec2 coordsTex_gs;\r\nlayout(location = 3) out vec3 normals_gs;\r\n\r\nvoid main()\r\n{\t\r\nint i,j;\r\nvec3 toPoint[3];\r\nvec3 d[3];\r\nfloat lat[3];\r\nfloat longt[3];\r\n\r\n\r\nfor(i=0; i<3; i++)\r\n{\r\n  toPoint[i] = gl_in[i].gl_Position.xyz-pos;\r\n  d[i] = normalize(toPoint[i]);                                  \r\n  lat[i] = d[i].z;\r\n  longt[i] = atan(d[i].y,d[i].x);\r\n  if(longt[i]<0)\r\n    longt[i] += 2.0f*PI;\r\n}\r\n\r\n\r\nif((abs(longt[1]-longt[2])<PI && abs(longt[0]-longt[2])<PI && abs(longt[1]-longt[0])<PI)){\r\n\r\n\r\n  float fact=100.0f;\r\n\r\n  for(i=0; i<3; i++)\r\n  {\r\n  gl_Position = vec4(longt[i]/PI-1.0,-lat[i],length(toPoint[i])/fact,1.0f);\r\n  position=gl_Position;\r\n  colors_gs = colors_tes[i];\r\n  coordsTex_gs = coordsTex_tes[i];\r\n  normals_gs = normals_tes[i];\r\n  EmitVertex();\r\n  }\r\n  EndPrimitive();\r\n\r\n}\r\nelse{\r\nfor(i=0; i<3; i++){\r\n  if(abs(longt[i]-longt[(i+1)%3])>PI && abs(longt[i]-longt[(i+2)%3])>PI){\r\n\r\n    float longt_0[3]=longt;\r\n    float longt_1[3]=longt;\r\n\r\n    if(longt[i]>PI){\r\n      longt_0[i]=longt[i]-2.0f*PI;\r\n      longt_1[(i+1)%3]=longt[(i+1)%3]+2.0f*PI;\r\n      longt_1[(i+2)%3]=longt[(i+2)%3]+2.0f*PI;\r\n    }\r\n    else{\r\n      longt_0[i]=longt[i]+2.0f*PI;\r\n      longt_1[(i+1)%3]=longt[(i+1)%3]-2.0f*PI;\r\n      longt_1[(i+2)%3]=longt[(i+2)%3]-2.0f*PI;\r\n    }\r\n\r\n    for(j=0; j<3; j++)\r\n    {\r\n      gl_Position = vec4(longt_0[j]/PI-1.0,-lat[j],length(toPoint[j])/100.0f,1.0f);\r\n      position=gl_Position;\r\n      colors_gs = colors_tes[i];\r\n      coordsTex_gs = coordsTex_tes[i];\r\n      normals_gs = normals_tes[i];\r\n      EmitVertex();\r\n    }\r\n    EndPrimitive();\r\n\r\n    for(j=0; j<3; j++)\r\n    {\r\n      gl_Position = vec4(longt_1[j]/PI-1.0,-lat[j],length(toPoint[j])/100.0f,1.0f);\r\n      position=gl_Position;\r\n      colors_gs = colors_tes[i];\r\n      coordsTex_gs = coordsTex_tes[i];\r\n      normals_gs = normals_tes[i];\r\n      EmitVertex();\r\n    }\r\n    EndPrimitive();\r\n    break;\r\n  }\r\n}\r\n\r\n  \r\n}\r\n\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/longlat.tcs",
    "content": "#version 450 core\r\n\r\nuniform vec3 pos;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\r\nconst float PI = 3.1415926535897932384626433832795;\t\t\r\n\r\nlayout(vertices = 3) out; \r\n\r\nlayout(location = 1) in vec3 vs_colors[];\r\nlayout(location = 2) in vec2 vs_coordsTex[];\r\nlayout(location = 3) in vec3 vs_normals[];\r\n\r\nout vec3 colors_tcs [];\r\nout vec2 coordsTex_tcs [];\r\nout vec3 normals_tcs [];\r\n\r\nvoid main(void) {\r\n\r\ncolors_tcs[gl_InvocationID] = vs_colors[gl_InvocationID];\r\ncoordsTex_tcs[gl_InvocationID] = vs_coordsTex[gl_InvocationID];\r\nnormals_tcs[gl_InvocationID] = vs_normals[gl_InvocationID];\r\n\r\nif (gl_InvocationID == 0) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n    int i;\r\n\tvec3 toPoint[3];\r\n\tvec3 d[3];\r\n\tfloat lat[3];\r\n\tfloat longt[3];\r\n\r\n\tfor(i=0; i<3; i++)\r\n\t{\r\n\t  toPoint[i] = gl_in[i].gl_Position.xyz-pos;\r\n\t  d[i] = normalize(toPoint[i]);                                  \r\n      lat[i] = d[i].z;\r\n      longt[i] = atan(d[i].y,d[i].x);\r\n      if(longt[i]<0)\r\n    \tlongt[i] += 2.0f*PI;\r\n\t}\r\n\r\n\tfloat inner=0;\r\n\tfor(i=0; i<3; i++)\r\n\t{\r\n\t\tfloat level= 512*sqrt(\r\n\t\tpow((longt[(i+1)%3]-longt[(i+2)%3])/PI,2)+\r\n\t\tpow((lat[(i+1)%3]-lat[(i+2)%3]),2)\r\n\t\t);\r\n\t\tgl_TessLevelOuter[i] = level;\r\n\t\tif(level>inner)\r\n\t\t\tinner=level;\r\n\t}\r\n\r\n\tgl_TessLevelInner[0] = inner/2; \r\n\r\n}\r\n\r\ngl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; \r\n\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/longlat.tes",
    "content": "#version 450 core \t\t\t\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(triangles, equal_spacing, cw) in; \t\r\n\r\nlayout(location = 1) in vec3 colors_tcs[];\r\nlayout(location = 2) in vec2 coordsTex_tcs[];\r\nlayout(location = 3) in vec3 normals_tcs[];\r\n\r\nlayout(location = 1) out vec3 colors_tes;\r\nlayout(location = 2) out vec2 coordsTex_tes;\r\nlayout(location = 3) out vec3 normals_tes;\r\n\r\nvoid main(void) {\r\n   colors_tes = colors_tcs[0];\r\n   coordsTex_tes = coordsTex_tcs[0];\r\n   normals_tes = normals_tcs[0];\r\nvec4 inPos = (gl_TessCoord.x*gl_in[0].gl_Position + gl_TessCoord.y*gl_in[1].gl_Position + gl_TessCoord.z*gl_in[2].gl_Position); \r\n\t\t\t\t\t\t\t\t\t\t\r\n   gl_Position = inPos;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/longlat.vp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 450\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 0) in vec3 inPos;\r\nlayout(location = 1) in vec3 inColors;\r\nlayout(location = 2) in vec2 inCoordsTex;\r\nlayout(location = 3) in vec3 inNormals; \r\n\r\nlayout(location = 1) out vec3 vs_colors;\r\nlayout(location = 2) out vec2 vs_coordsTex;\r\nlayout(location = 3) out vec3 vs_normals;\r\n\r\nvoid main()\r\n{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\tvs_colors= inColors;\r\n\tvs_coordsTex = inCoordsTex;\r\n\tvs_normals = inNormals;\r\n\tgl_Position = vec4(inPos,1.0f);\r\n}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/longlatColor.fp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 450\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 0) out vec4 outColor;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 0) in vec4 position;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 1) in vec3 colors_gs;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 2) in vec2 texCoords_gs;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 3) in vec3 normals_gs;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\r\nvoid main()\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n{\t\t\t\r\n\r\n\tif(100.f*position.z<0.001)\r\n\t\tdiscard;\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\t//outColor = vec4(1.f-10.f*position.z, 1.f-10.f*position.z, 1.f-10.f*position.z , 1.0f);\t\r\n\toutColor = vec4(colors_gs.x, colors_gs.y, colors_gs.z , 1.0f);\t\r\n}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/longlatDepth.fp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 450\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 0) out vec4 outColor;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 0) in vec4 position;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 1) in vec3 colors_gs;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 2) in vec2 texCoords_gs;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\nlayout(location = 3) in vec3 normals_gs;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\r\nvoid main()\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n{\t\t\t\r\n\r\n\tif(100.f*position.z<0.001)\r\n\t\tdiscard;\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\toutColor = vec4(1.f-10.f*position.z, 1.f-10.f*position.z, 1.f-10.f*position.z , 1.0f);\t\r\n\t//outColor = vec4(colors_gs.x, colors_gs.y, colors_gs.z , 1.0f);\t\r\n}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/noproj.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/** \\file ibr.vp\n *\n * Vertex shader WITHOUT projection and modelview transformations.\n */\n\n#version 420\n\nlayout(location = 0) in vec4 in_vertex;   /**< Input vertex coordinates */\nlayout(location = 1) in vec4 in_texcoord; /**< Input texture coordinates */\nlayout(location = 2) in vec4 in_color;    /**< Input colour value */\n\nout vec4 texcoord;                        /**< Output texture coordinates */\nout vec4 color;                           /**< Output color value */\n\nvoid main(void) {\n  gl_Position = in_vertex;\n  texcoord    = in_texcoord;\n  color       = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/normalRenderer.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nin vec3 GtoF_normal;\nlayout(location = 0) out vec4 out_color;\n\nvoid main(void) {\n\t\n\tvec3 colorN=(GtoF_normal+1.0)/2.0;\n    out_color = vec4(colorN,1.0);\n    //out_color = fragTexCoord.x*vec4(1.0,0.0,0.0,1.0);\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/normalRenderer.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\nuniform mat4 M;\nuniform mat4 V;\nuniform bool imSpaceNormals;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 3) in vec3 in_normal;\n\nout vec3 GtoF_normal;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\n\tif(imSpaceNormals)\n\t\tGtoF_normal = normalize((inverse(transpose(V*M)) * vec4(in_normal,0.0)).xyz);\n\telse\n\t\tGtoF_normal = normalize((inverse(transpose(M)) * vec4(in_normal,0.0)).xyz);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/normalRendererGen.gp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\nuniform mat4 MVPinv;\r\n\r\nlayout(triangles) in;\r\nlayout (triangle_strip) out;\r\nlayout (max_vertices = 3) out;\r\n\r\nout vec3 GtoF_normal;\r\n\r\nvoid main(void) {\r\n  \r\n  vec3 P0 = (MVPinv *gl_in[0].gl_Position).xyz;\r\n  vec3 P1 = (MVPinv *gl_in[1].gl_Position).xyz;\r\n  vec3 P2 = (MVPinv *gl_in[2].gl_Position).xyz;\r\n  \r\n  vec3 V0 = P0 - P1;\r\n  vec3 V1 = P2 - P1;\r\n  \r\n  vec3 N = normalize( cross(V1, V0) );\r\n\r\n  int i;\r\n\r\n    for (i = 0; i < gl_in.length(); i++)\r\n    {\r\n\t\tGtoF_normal = N; // Specs say we need to set again output values after calling EmitVertex\r\n        gl_Position = gl_in[i].gl_Position;\r\n        EmitVertex();\r\n    }\r\n    EndPrimitive();\r\n\r\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/normalRendererGen.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\t//fragTexCoord = vec2(0.2,0.8);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/poisson_diverg.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D synth;\nlayout(location= 0) out vec4 out_constraint;\n\nvoid main(void) {\n    vec4 I = texelFetch(synth, ivec2(gl_FragCoord.xy), 0);\n\n    // hole - perform Poisson synthesis here\n    if (all(lessThan(I.xyz,vec3(0.01))))\n        out_constraint = vec4(0);\n    else\n        out_constraint = I;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/poisson_interp.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform float scale;\n\nlayout(binding = 0) uniform sampler2D coarse;\nlayout(binding = 1) uniform sampler2D constraint;\nlayout(location= 0) out vec4 out_color;\n\nin vec4 texcoord;\n\n\nvoid main(void) {\n    //  sample color from lower multigrid level by scaling texture coordinates\n    vec4 color = texture(coarse,vec2(gl_FragCoord.xy/scale)/textureSize(coarse,0).xy,0);\n\n    //  sample Dirichlet constraint without texture filtering because pixel without\n    //  Dirichlet constraint are black and texture filtering may break this check\n    vec4 cons  = texelFetch(constraint,ivec2(gl_FragCoord),0);\n\n    //  write color of lower level to output except holes pixels in the constraint\n    if (any(greaterThan(cons.rgb,vec3(0.01))))\n        out_color = cons;\n    else\n        out_color = color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/poisson_jacobi.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform vec4 weights;\nuniform float scale;\n\nlayout(binding = 0) uniform sampler2D curr_tex;\nlayout(location= 0) out vec4 out_color;\n\nvoid main(void) {\n    ivec2 coord = ivec2(gl_FragCoord.xy*scale);\n\n    float w_center = weights.x;\n    float w_edge   = weights.y;\n    float w_corner = weights.z;\n    float w_c_x_inv= weights.w;\n\n    // Solve the Laplace equation, same as Poisson equation with\n    // divergence equal to 0\n    out_color =\n        -w_c_x_inv * (\n                (texelFetch(curr_tex, coord, 0) * w_center +\n                 (texelFetch(curr_tex, coord+ivec2( 0, 1), 0) +\n                  texelFetch(curr_tex, coord+ivec2( 0,-1), 0) +\n                  texelFetch(curr_tex, coord+ivec2( 1, 0), 0) +\n                  texelFetch(curr_tex, coord+ivec2(-1, 0), 0)) * w_edge +\n                 (texelFetch(curr_tex, coord+ivec2(-1,-1), 0) +\n                  texelFetch(curr_tex, coord+ivec2(-1, 1), 0) +\n                  texelFetch(curr_tex, coord+ivec2( 1,-1), 0) +\n                  texelFetch(curr_tex, coord+ivec2( 1, 1), 0)) * w_corner\n                )\n            );\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/poisson_restrict.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform float scale;\n\nlayout(binding = 0) uniform sampler2D cons;\nlayout(location= 0) out vec4 out_constraint;\n\nvoid main(void) {\n    mat3 f = mat3(\n            0.25, 0.50, 0.25,\n            0.50, 1.00, 0.50,\n            0.25, 0.50, 0.25);\n\n    vec4 constr = vec4(0);\n\n    float sum = 0;\n\n    ivec2 coord = ivec2(gl_FragCoord.xy*scale);\n    for (int i=0; i<3; i++) {\n        for (int j=0; j<3; j++) {\n            vec4 c  = texelFetch(cons,coord+ivec2(i-1,j-1),0);\n            float a = float(any(greaterThan(c.rgb, vec3(0.01))));\n            constr += f[i][j] * a * c;\n            sum    += f[i][j] * a;\n        }\n    }\n    out_constraint = constr / sum;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/positionReflectedDirRenderer.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nin vec3 position;\nin vec3 normal;\n\nuniform vec3 cameraPos;\n\nlayout(location = 0) out vec3 outPosition;\nlayout(location = 1) out vec3 outDirection;\n\nvoid main(void) {\n\toutPosition = position;\n\toutDirection = reflect(normalize(position - cameraPos), normal);\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/positionReflectedDirRenderer.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 3) in vec3 in_normal;\n\nout vec3 position;\nout vec3 normal;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n    position = in_vertex;\n    normal = in_normal;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/positionRenderer.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nout vec3 out_position;\n\nin vec3 position;\n\nvoid main(void) {\n\tout_position = position;\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/positionRenderer.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\n\nout vec3 position;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n    position = in_vertex;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/shadowMapRenderer.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\nconst float PI = 3.1415926535897932384626433832795;\n\nvec2 poissonDisk[64] = vec2[](\nvec2(-0.613392, 0.617481),\nvec2(0.170019, -0.040254),\nvec2(-0.299417, 0.791925),\nvec2(0.645680, 0.493210),\nvec2(-0.651784, 0.717887),\nvec2(0.421003, 0.027070),\nvec2(-0.817194, -0.271096),\nvec2(-0.705374, -0.668203),\nvec2(0.977050, -0.108615),\nvec2(0.063326, 0.142369),\nvec2(0.203528, 0.214331),\nvec2(-0.667531, 0.326090),\nvec2(-0.098422, -0.295755),\nvec2(-0.885922, 0.215369),\nvec2(0.566637, 0.605213),\nvec2(0.039766, -0.396100),\nvec2(0.751946, 0.453352),\nvec2(0.078707, -0.715323),\nvec2(-0.075838, -0.529344),\nvec2(0.724479, -0.580798),\nvec2(0.222999, -0.215125),\nvec2(-0.467574, -0.405438),\nvec2(-0.248268, -0.814753),\nvec2(0.354411, -0.887570),\nvec2(0.175817, 0.382366),\nvec2(0.487472, -0.063082),\nvec2(-0.084078, 0.898312),\nvec2(0.488876, -0.783441),\nvec2(0.470016, 0.217933),\nvec2(-0.696890, -0.549791),\nvec2(-0.149693, 0.605762),\nvec2(0.034211, 0.979980),\nvec2(0.503098, -0.308878),\nvec2(-0.016205, -0.872921),\nvec2(0.385784, -0.393902),\nvec2(-0.146886, -0.859249),\nvec2(0.643361, 0.164098),\nvec2(0.634388, -0.049471),\nvec2(-0.688894, 0.007843),\nvec2(0.464034, -0.188818),\nvec2(-0.440840, 0.137486),\nvec2(0.364483, 0.511704),\nvec2(0.034028, 0.325968),\nvec2(0.099094, -0.308023),\nvec2(0.693960, -0.366253),\nvec2(0.678884, -0.204688),\nvec2(0.001801, 0.780328),\nvec2(0.145177, -0.898984),\nvec2(0.062655, -0.611866),\nvec2(0.315226, -0.604297),\nvec2(-0.780145, 0.486251),\nvec2(-0.371868, 0.882138),\nvec2(0.200476, 0.494430),\nvec2(-0.494552, -0.711051),\nvec2(0.612476, 0.705252),\nvec2(-0.578845, -0.768792),\nvec2(-0.772454, -0.090976),\nvec2(0.504440, 0.372295),\nvec2(0.155736, 0.065157),\nvec2(0.391522, 0.849605),\nvec2(-0.620106, -0.328104),\nvec2(0.789239, -0.419965),\nvec2(-0.545396, 0.538133),\nvec2(-0.178564, -0.596057)\n);\n\nuniform vec3 lightDir;\nuniform float sun_app_radius;\nuniform mat4 depthMapMVPinv;\nuniform float depthMapRadius;\nuniform float biasControl;\n\nlayout(binding=0) uniform sampler2D depthMap;\n\nin vec4 depthMapProj;\nin vec3 VtoF_normal;\nin vec3 VtoF_pos;\n\nout float out_val;\n\nvoid main(void) {\n\t\n\tvec2 texc = (depthMapProj.xy + 1.0) / 2.0;\n\n\tfloat depthImSpace = depthMapProj.z;\n\n\tfloat cosTheta = clamp(dot(VtoF_normal,lightDir),0.0,1.0);\n\tfloat bias = biasControl*tan(acos(cosTheta));\n\tbias = clamp(bias, 0.0, 5*biasControl);\n\n\tint textureWidth = textureSize(depthMap,0).x;\n\n\t// Compute the size of the shadow transition\n\n\t// The 2 account for the fact that we are treating the radius.\n\n\tint r_blocker = int(ceil(0.5*tan(sun_app_radius*PI/180.0)*textureWidth));\n\tfloat mean_blocker_val = 0.0;\n\tfloat blocker_num_val = 0.0;\n\tfloat sum_weight = 0.0;\n\n\tfor(int i = 0; i<7 ; i++){\n\n\t\tfloat theta_rot=i;\n\t\tmat2 rotation_poisson =mat2(cos(theta_rot), sin(theta_rot), -sin(theta_rot), cos(theta_rot));\n\n\t\tfor(int k = 0; k <64 ; k++){\n\n\t\t\tfloat pixDist = length(r_blocker*poissonDisk[k]/textureWidth);\n\t\t\tif(pixDist<=r_blocker){\n\t\t\t\tfloat depthMapVal = texture(depthMap, texc + r_blocker*rotation_poisson*poissonDisk[k]/textureWidth).x;\n\n\t\t\t\tfloat bias_with_dist = bias*(pixDist+1.0);\n\n\t\t\t\tif( depthImSpace-bias_with_dist > depthMapVal ){\n\n\t\t\t\t\tvec3 blocker_pos=(depthMapMVPinv*vec4(depthMapProj.x,depthMapProj.y,depthMapVal,1.0)).xyz;\n\n\t\t\t\t\tfloat weight = 0.01+exp(-pow(pixDist/(0.02*r_blocker),2.0));\n\t\t\t\t\tmean_blocker_val += weight*length(VtoF_pos-blocker_pos);\n\t\t\t\t\tsum_weight += weight;\n\t\t\t\t\tblocker_num_val += 1.0;\n\t\t\t    }\n\t\t\t}\t\t\n\t\t}\n\t}\n\n\tif( dot(VtoF_normal,lightDir) <= -0.017 ){\n\t\tout_val = 0.0;\n\t}\n\telse if(blocker_num_val < 1.0){\n\t\tout_val = 1.0;\n\t}\n\telse{\n\n\t\tfloat d_blocker_receiver = mean_blocker_val/sum_weight;\n\n\t\tfloat wShadow=2*d_blocker_receiver*tan(sun_app_radius*PI/180.0);\n\t\t//float angleCompensation= dot(normalize(VtoF_normal),normalize(lightDir));\n\t\t//float wShadowAngle = wShadow/angleCompensation;\n\t\tfloat ratioShadowTexture = wShadow/(2.0*depthMapRadius);\n\t\tfloat pixelShadowTexture = ratioShadowTexture*textureWidth;\n\n\t\tconst int r = int(ceil(pixelShadowTexture));\n\n\t\tfloat num_val = 0.0;\n\t\tfloat sum_val = 0.0;\n\n\t\tfor(int i = 0; i<14 ; i++){\n\n\t\t\tfloat theta_rot=float(i);\n\t\t\tmat2 rotation_poisson =mat2(cos(theta_rot), sin(theta_rot), -sin(theta_rot), cos(theta_rot));\n\n\t\t\tfor(int k = 0; k <64 ; k++){\n\n\t\t\t\tfloat depthMapVal = texture(depthMap, texc + r*rotation_poisson*poissonDisk[k]/textureWidth).x;\n\t\t\t\tfloat pixDist = length(r*poissonDisk[k]/textureWidth);\n\t\t\t\tfloat bias_with_dist = bias*(pixDist+1.0);\n\n\t\t\t\tif( depthImSpace-bias_with_dist > depthMapVal ){\n\t\t\t    \tsum_val += 0.0;\n\t\t\t    }\n\t\t\t    else{\n\t\t\t    \tsum_val += 1.0;\n\t\t\t    }\n\t\t\t    num_val +=1.0;\n\t\t\t}\n\t\t}\n\n\t\tout_val = sum_val/num_val;\n\t}\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/shadowMapRenderer.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\nuniform mat4 depthMapMVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 3) in vec3 in_normal;\n\nout vec4 depthMapProj;\nout vec3 VtoF_normal;\nout vec3 VtoF_pos;\n\nvoid main(void) {\n\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\n\tdepthMapProj = depthMapMVP * vec4(in_vertex,1.0);\n\n\tVtoF_normal = in_normal;\n\tVtoF_pos = in_vertex;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/texture-invert.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D tex;\nlayout(location= 0) out vec4 out_color;\n\nin vec2 tex_coord;\n\nvoid main(void) {\n    vec2 texcoord = tex_coord ;\n    out_color = vec4(1, 1, 1, 1) - texture(tex,texcoord);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/texture.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D tex;\nlayout(location= 0) out vec4 out_color;\n\nin vec2 tex_coord;\n\nvoid main(void) {\n    vec2 texcoord = tex_coord ;\n    out_color = texture(tex,texcoord);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/texture.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n#define VERTICAL_FLIP (0)\n\nlayout(location = 0) in vec2 in_vertex;\nlayout(location = 1) in vec2 in_texcoord;\n\n\nout vec2 tex_coord;\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex, 0.0, 1.0);\n\ttex_coord.x = in_texcoord.x;\n\tif (VERTICAL_FLIP==1) {\n\t\ttex_coord.y = 1.0 - in_texcoord.y;\n\t}\n\telse {\n\t\ttex_coord.y = in_texcoord.y;\n\t}\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/textured_mesh.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform sampler2D tex;\n\nout vec4 out_color;\n\nin vec2 vertUV;\n\nvoid main(void) {\n\tvec2 uv = vertUV;\n\tif(uv.x==0.0 && uv.y==0.0){\n\tout_color = vec4(1.0,1.0,1.0,1.0);\n\t}\n\telse{\n\tuv.y = 1.0 - uv.y; /// \\todo TODO: Why Texture are flipped in y ?\n\tout_color = texture(tex, uv);\n\t}\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/textured_mesh.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 2) in vec2 in_uv;\n\nout vec2 vertUV;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\t\n\tvertUV = in_uv;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/shaders/textured_mesh_flipY.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 2) in vec2 in_uv;\n\nout vec2 vertUV;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\tvec2 uv = in_uv;\n\tuv.y = 1.0 - uv.y ;\n\tvertUV = uv;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/renderer/sibr_renderer.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n\t\\defgroup sibr_renderer sibr_renderer\n\n\t\\brief Renderer utilities.\n\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/BasicIBRScene.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"BasicIBRScene.hpp\"\n#include <iostream>\n#include <string>\n\n#include \"core/scene/CalibratedCameras.hpp\"\n#include \"core/scene/ParseData.hpp\"\n#include \"core/scene/ProxyMesh.hpp\"\n#include \"core/scene/InputImages.hpp\"\n\nnamespace sibr\n{\n\t\n\tBasicIBRScene::BasicIBRScene() {\n\t\t_data.reset(new ParseData());\n\t\t_cams.reset(new CalibratedCameras());\n\t\t_imgs.reset(new InputImages());\n\t\t_proxies.reset(new ProxyMesh());\n\t\t_renderTargets.reset(new RenderTargetTextures());\n\t}\n\n\tBasicIBRScene::BasicIBRScene(const BasicIBRAppArgs & myArgs, bool noRTs, bool noMesh)\n\t{\n\n\t\tBasicIBRScene();\n\t\t// parse metadata file\n\t\t_data.reset(new ParseData());\n\t\t_currentOpts.renderTargets = !noRTs;\n\t\t_currentOpts.mesh = !noMesh;\n\n\t\t_data->getParsedData(myArgs);\n\t\tstd::cout << \"Number of input Images to read: \" << _data->imgInfos().size() << std::endl;\n\n\t\tif (_data->imgInfos().size() != _data->numCameras())\n\t\t\tSIBR_ERR << \"List Image file size do not match number of input cameras in Bundle file!\" << std::endl;\n\n\t\tif (_data->datasetType() != IParseData::Type::EMPTY) {\n\t\t\tcreateFromData(myArgs.texture_width);\n\t\t}\n\t}\n\n\tBasicIBRScene::BasicIBRScene(const BasicIBRAppArgs& myArgs, SceneOptions myOpts)\n\t{\n\t\tBasicIBRScene();\n\t\t_currentOpts = myOpts;\n\n\t\t// parse metadata file\n\t\t_data.reset(new ParseData());\n\n\n\t\t_data->getParsedData(myArgs);\n\t\tstd::cout << \"Number of input Images to read: \" << _data->imgInfos().size() << std::endl;\n\n\t\tif (_data->imgInfos().size() != _data->numCameras())\n\t\t\tSIBR_ERR << \"List Image file size do not match number of input cameras in Bundle file!\" << std::endl;\n\n\t\tif (_data->datasetType() != IParseData::Type::EMPTY) {\n\t\t\tcreateFromData(myArgs.texture_width);\n\t\t}\n\t}\n\n\tvoid BasicIBRScene::createFromCustomData(const IParseData::Ptr & data, const uint width, BasicIBRScene::SceneOptions myOpts)\n\t{\n\t\t_data = data;\n\t\t_currentOpts = myOpts;\n\t\tcreateFromData(width);\n\t}\n\n\n\tvoid BasicIBRScene::createRenderTargets()\n\t{\n\t\t_renderTargets->initializeDefaultRenderTargets(_cams, _imgs, _proxies);\n\t}\n\n\tBasicIBRScene::BasicIBRScene(BasicIBRScene & scene)\n\t{\n\t\t_data = scene.data();\n\t\t_cams = scene.cameras();\n\t\t_imgs = scene.images();\n\t\t_proxies = scene.proxies();\n\t\t_renderTargets = scene.renderTargets();\n\t}\n\n\tvoid BasicIBRScene::createFromData(const uint width)\n\t{\n\t\t_cams.reset(new CalibratedCameras());\n\t\t_imgs.reset(new InputImages());\n\t\t_proxies.reset(new ProxyMesh());\n\n\t\t// setup calibrated cameras\n\t\tif (_currentOpts.cameras) {\n\t\t\t\n\t\t\t_cams->setupFromData(_data);\n\n\t\t\tstd::cout << \"Number of Cameras set up: \" << _cams->inputCameras().size() << std::endl;\n\t\t}\n\n\t\t// load input images\n\n\t\tuint mwidth = width;\n\t\tif (_currentOpts.images) {\n\t\t\t_imgs->loadFromData(_data);\n\t\t\tstd::cout << \"Number of Images loaded: \" << _imgs->inputImages().size() << std::endl;\n\n\t\t\tif (width == 0) {// default\n\t\t\t\tif (_imgs->inputImages()[0]->w() > 1920) {\n\t\t\t\t\tSIBR_LOG << \"Limiting width to 1920 for performance; use --texture-width to override\" << std::endl;\n\t\t\t\t\tmwidth = 1920;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_renderTargets.reset(new RenderTargetTextures(mwidth));\n\n\t\tif (_currentOpts.mesh) {\n\t\t\t// load proxy\n\t\t\t_proxies->loadFromData(_data);\n\n\n\t\t\tstd::vector<InputCamera::Ptr> inCams = _cams->inputCameras();\n\t\t\tfloat eps = 0.1f;\n\t\t\tif (inCams.size() > 0 && (abs(inCams[0]->znear() - 0.1) < eps || abs(inCams[0]->zfar() - 1000.0) < eps || abs(inCams[0]->zfar() - 100.0) < eps) && _proxies->proxy().triangles().size() > 0) {\n\t\t\t\tstd::vector<sibr::Vector2f>    nearsFars;\n\t\t\t\tCameraRaycaster::computeClippingPlanes(_proxies->proxy(), inCams, nearsFars);\n\t\t\t\t_cams->updateNearsFars(nearsFars);\n\t\t\t}\n\n\t\t\t//// Load the texture.\n\t\t\tsibr::ImageRGB inputTextureImg;\n\n\t\t\tstd::string texturePath, textureImageFileName;\n\n\t\t\t// Assumes that the texture is stored next to the mesh in the same directory\n\t\t\t// This information comes from Assimp and the mtl file if available\n\t\t\tif ((textureImageFileName = _proxies->proxy().getTextureImageFileName()) != \"\") {\n\t\t\t\ttexturePath = sibr::parentDirectory(_data->meshPath()) + \"/\" + textureImageFileName;\n\t\t\t\t// check if full path given \n\t\t\t\tif (!sibr::fileExists(texturePath) && sibr::fileExists(textureImageFileName)) \n\t\t\t\t\ttexturePath = textureImageFileName;\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttexturePath = sibr::parentDirectory(_data->meshPath()) + \"/mesh_u1_v1.png\";\n\t\t\t\tif (sibr::fileExists(texturePath)) {\n\t\t\t\t\ttexturePath = sibr::parentDirectory(_data->meshPath()) + \"/textured_u1_v1.png\";\n\t\t\t\t\tif (!sibr::fileExists(texturePath)) \n\t\t\t\t\t\ttexturePath = sibr::parentDirectory(_data->meshPath()) + \"/texture.png\";\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (_currentOpts.texture && sibr::fileExists(texturePath)) {\n\t\t\t\tinputTextureImg.load(texturePath);\n\t\t\t\t_inputMeshTexture.reset(new sibr::Texture2DRGB(inputTextureImg, SIBR_GPU_LINEAR_SAMPLING));\n\t\t\t}\n\n\t\t}\n\n\t\tif (_currentOpts.renderTargets) {\n\t\t\tcreateRenderTargets();\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/BasicIBRScene.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <core/scene/IIBRScene.hpp>\n\nnamespace sibr {\n\n\t/**\n\t* Class used to define a basic IBR Scene \n\t* containing multiple components required to define a scene.\n\t* \n\t* \\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT BasicIBRScene: public IIBRScene\n\t{\n\t\t\n\tpublic:\n\n\t\t/**\n\t\t* \\brief Pointer to the instance of class sibr::BasicIBRScene.\n\t\t*/\n\t\tSIBR_CLASS_PTR(BasicIBRScene);\n\n\t\t/**\n\t\t * \\brief Default constructor to create a BasicIBRScene.\n\t\t */\n\t\tBasicIBRScene();\n\n\t\t/**\n\t\t * \\brief Constructor to create a BasicIBRScene given command line arguments.\n\t\t * The scene may be created using either dataset path, or explicitly specifying individual componenets.\n\t\t * \\param myArgs to provide all command line arguments containing path to specific components.\n\t\t * \\param noRTs to specify whether to initialize render target textures or not.\n\t\t * \\param noMesh skip loading the mesh\n\t\t */\n\t\tBasicIBRScene(const BasicIBRAppArgs & myArgs, bool noRTs, bool noMesh = false);\n\n\t\t/**\n\t\t * \\brief Constructor to create a BasicIBRScene given command line arguments.\n\t\t * The scene may be created using either dataset path, or explicitly specifying individual componenets.\n\t\t * \\param myArgs to provide all command line arguments containing path to specific components.\n\t\t * \\param myOpts to specify initialization paramters for the scene.\n\t\t */\n\t\tBasicIBRScene(const BasicIBRAppArgs& myArgs, SceneOptions myOpts = SceneOptions());\n\n\n\t\t/** Destructor. */\n\t\t~BasicIBRScene() {};\n\n\t\t/**\n\t\t* \\brief Creates a BasicIBRScene given custom data argument.\n\t\t* The scene will be created using the custom data (cameras/images/proxies/textures etc.) provided.\n\t\t* \\param data to provide data instance holding customized components.\n\t\t* \\param width the constrained width for GPU texture data.\n\t\t* \\param myOpts to specify whether to initialize specific parts of the scene (RTs, geometry,...)\n\t\t*/\n\t\tvoid createFromCustomData(const IParseData::Ptr & data, const uint width = 0, SceneOptions myOpts = SceneOptions()) override;\n\t\t\n\t\t/**\n\t\t * \\brief Function to create a scene directly using the dataset path specified in command-line.\n\t\t */\n\t\tvoid createFromDatasetPath() {};\n\n\t\t/**\n\t\t* \\brief Function to generate render targets using the _data (regarding cameras, images, proxies ) parsed from metadata file.\n\t\t*/\n\t\tvoid createRenderTargets() override;\n\t\t\n\t\t\t\t/**\n\t\t * \\brief Getter for the pointer holding the data related to the scene.\n\t\t * \n\t\t */\n\t\tconst IParseData::Ptr\t\t\t\t\t\tdata(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the pointer holding the data related to the scene for scene creation.\n\t\t* \\param data the setup data\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\tdata(const sibr::IParseData::Ptr & data) override;\n\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding cameras related to each input iamge of the scene.\n\t\t *\n\t\t */\n\t\tconst ICalibratedCameras::Ptr\t\t\t\tcameras(void) const override;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the input images to the scene.\n\t\t *\n\t\t */\n\t\tconst IInputImages::Ptr\t\t\t\t\t\timages(void) const override;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the proxies required by the scene.\n\t\t *\n\t\t */\n\t\tconst IProxyMesh::Ptr\t\t\t\t\t\tproxies(void) const override;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the render targets textures related to the scene.\n\t\t *\n\t\t */\n\t\tconst RenderTargetTextures::Ptr\t&\t\trenderTargets(void) const override;\n\t\t\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the render targets textures related to the scene.\n\t\t *\n\t\t */\n\t\tRenderTargetTextures::Ptr &\t\t\t\trenderTargets(void) override;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the mesh textures related to the mesh loaded for the scene.\n\t\t *\n\t\t */\n\t\tTexture2DRGB::Ptr &\t\t\t\t\t\tinputMeshTextures(void) override;\n\n\tprotected:\n\t\tBasicIBRScene(BasicIBRScene & scene);\n\t\tBasicIBRScene& operator =(const BasicIBRScene&) = delete;\n\n\t\tIParseData::Ptr\t\t\t\t_data;\n\t\tICalibratedCameras::Ptr\t\t_cams;\n\t\tIInputImages::Ptr\t\t\t_imgs;\n\t\tIProxyMesh::Ptr\t\t\t\t_proxies;\n\t\tTexture2DRGB::Ptr\t\t\t_inputMeshTexture;\n\t\tRenderTargetTextures::Ptr\t_renderTargets;\n\t\tSceneOptions\t\t\t\t_currentOpts;\n\n\t\t/**\n\t\t* \\brief Creates a BasicIBRScene from the internal stored data component in the scene.\n\t\t* The data could be populated either from dataset path or customized by the user externally.\n\t\t* \\param width the constrained width for GPU texture data.\n\t\t*/\n\t\tvoid createFromData(const uint width = 0);\n\n\t\t\n\t};\n\n\t///// INLINE DEFINITIONS /////\n\n\tinline const IParseData::Ptr\t\t\tBasicIBRScene::data(void) const\n\t{\n\t\treturn _data;\n\t}\n\n\tinline void BasicIBRScene::data(const IParseData::Ptr & data) \n\t{\n\t\t_data = data;\n\t}\n\n\tinline const ICalibratedCameras::Ptr BasicIBRScene::cameras(void) const\n\t{\n\t\treturn _cams;\n\t}\n\n\tinline const IInputImages::Ptr BasicIBRScene::images(void) const\n\t{\n\t\treturn _imgs;\n\t}\n\n\tinline const IProxyMesh::Ptr BasicIBRScene::proxies(void) const\n\t{\n\t\treturn _proxies;\n\t}\n\n\tinline const RenderTargetTextures::Ptr & BasicIBRScene::renderTargets(void) const\n\t{\n\t\treturn _renderTargets;\n\t}\n\n\tinline RenderTargetTextures::Ptr & BasicIBRScene::renderTargets(void)\n\t{\n\t\treturn _renderTargets;\n\t}\n\n\tinline Texture2DRGB::Ptr & BasicIBRScene::inputMeshTextures(void)\n\t{\n\t\treturn _inputMeshTexture;\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nproject(sibr_scene)\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\r\nsource_group(\"Source Files\" FILES ${SOURCES})\r\n\r\n\r\n## Specify target rules\r\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\r\n\r\ninclude_directories(${Boost_INCLUDE_DIRS})\r\ntarget_link_libraries(${PROJECT_NAME}\r\n    ${Boost_LIBRARIES}\r\n    ${ASSIMP_LIBRARIES}\r\n    ${GLEW_LIBRARIES}\r\n    ${OPENGL_LIBRARIES}\r\n    ${OpenCV_LIBRARIES}\r\n\tOpenMP::OpenMP_CXX\r\n    sibr_assets\r\n    sibr_graphics\r\n    sibr_raycaster\r\n)\r\n\r\nadd_definitions( -DSIBR_SCENE_EXPORTS -DBOOST_ALL_DYN_LINK  )\r\n\r\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\r\n\r\n## High level macro to install in an homogen way all our ibr targets\r\ninclude(install_runtime)\r\nibr_install_target(${PROJECT_NAME}\r\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\r\n)\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/CalibratedCameras.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"CalibratedCameras.hpp\"\n\nnamespace sibr {\n\tvoid CalibratedCameras::setupFromData(const sibr::IParseData::Ptr& data)\n\t{\n\n\n\t\t_inputCameras.resize(data->numCameras());\n\t\t_inputCameras = data->cameras();\n\t\treturn;\n\t}\n\n\tvoid CalibratedCameras::debugFlagCameraAsUsed(const std::vector<uint>& selectedCameras)\n\t{\n\t\t// Used for Debugging -- Display colored cameras in TopView\n\t\tstd::vector<bool> cameraUsed(inputCameras().size(), false);\n\t\tfor (uint usedID : selectedCameras)\n\t\t\tcameraUsed[usedID] = true;\n\t\tusedCameraForRendering(cameraUsed);\n\n\t}\n\n\tconst void CalibratedCameras::updateNearsFars(std::vector<Vector2f> & nearsFars) \n\t{\n\t\tif (_inputCameras.size() != nearsFars.size())\n\t\t\tSIBR_ERR << \"InputCamera size does not match Clipping Planes size!\" << std::endl;\n\n\t\tfor (int c = 0; c < _inputCameras.size(); c++){\n\t\t\t_inputCameras[c]->znear(nearsFars[c].x());\n\t\t\t_inputCameras[c]->zfar(nearsFars[c].y());\n\t\t}\n\t}\n\n\t\n\n\t\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/CalibratedCameras.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n# include \"core/scene/ICalibratedCameras.hpp\"\n#include \"core/scene/Config.hpp\"\n\nnamespace sibr\n{\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT CalibratedCameras : public ICalibratedCameras {\n\tpublic:\n\n\t\t/**\n\t\t* \\brief Pointer to the instance of class sibr::CalibratedCameras.\n\t\t*/\n\t\ttypedef std::shared_ptr<CalibratedCameras>\tPtr;\n\n\t\t// load from a path on disk in a predefined format (or could detect from file extension)\n\n\t\t/**\n\t\t* \\brief Creates the calibrated cameras for a scene given data parsed from dataset path.\n\t\t* \n\t\t* \\param data Holds all information required to created a set of calibrated cameras\n\t\t*/\n\t\tvoid\tsetupFromData(const IParseData::Ptr & data) override;\n\n\t\t/**\n\t\t* \\brief Assigns the calibrated cameras for a scene to a list of cameras passed as parameter.\n\t\t*\n\t\t* \\param cams Vector of type sibr::InputCamera to which the scene inputCameras will be set\n\t\t*/\n\t\tvoid\tsetupCamerasFromExisting(const std::vector<InputCamera::Ptr> & cams) override;\n\t\t\n\t\t/**\n\t\t* \\brief Function to set a camera as active.\n\t\t* \n\t\t* \\param camId Integer ID of the camera to be set active\n\t\t*/\n\t\tvoid\tactivateCamera(uint camId) override;\n\n\t\t/**\n\t\t* \\brief Function to set a camera as inactive.\n\t\t*\n\t\t* \\param camId Integer ID of the camera to be set inactive\n\t\t*/\n\t\tvoid\tdeactivateCamera(uint camId) override;\n\n\t\t/**\n\t\t* \\brief Function to mark the cameras used for rendering.\n\t\t* Generally used for debugging purposes\n\t\t* \\param selectedCameras list of camera IDs that are used for rendering\n\t\t*/\n\t\tvoid\tdebugFlagCameraAsUsed(const std::vector<uint>& selectedCameras) override;\n\n\t\t/**\n\t\t* \\brief Function to check if the camera is used for rendering.\n\t\t*\n\t\t* \\param camId Integer ID of the cameras to be checked if it is being used for rendering\n\t\t* \\return true if used for rendering\n\t\t*/\n\t\tbool\tisCameraUsedForRendering(size_t camId) const override;\n\t\t\n\t\t/**\n\t\t* \\brief Function to set the cameras used for rendering.\n\t\t*\n\t\t* \\param usedCamera Vector to specify which cameras are used for rendering \n\t\t*/\n\t\tvoid\tusedCameraForRendering(const std::vector<bool> usedCamera) override;\n\n\t\t/**\n\t\t* \\brief Getter to the vector of input cameras used to create the scene\n\t\t*\n\t\t*/\n\t\tconst std::vector<InputCamera::Ptr>& inputCameras(void) const override;\n\n\t\tconst void updateNearsFars(std::vector<sibr::Vector2f> & nearsFars) override;\n\n\tprotected:\n\t\tstd::vector<InputCamera::Ptr>\t\t\t\t_inputCameras; \n\t\tstd::vector<bool>\t\t\t\t\t\t\t_usedCameraFlag;\n\n\t};\n\t\n\t///// INLINE DEFINITIONS /////\n\n\tinline void CalibratedCameras::setupCamerasFromExisting(const std::vector<InputCamera::Ptr>& cams)\n\t{\n\t\t_inputCameras = cams;\n\t}\n\n\tinline const std::vector<InputCamera::Ptr>& CalibratedCameras::inputCameras( void ) const {\n\t\treturn _inputCameras;\n\t}\n\n\tinline void CalibratedCameras::activateCamera(uint camId)\n\t{\n\t\t_inputCameras[camId]->setActive(true);\n\t}\n\n\tinline void CalibratedCameras::deactivateCamera(uint camId)\n\t{\n\t\t_inputCameras[camId]->setActive(false);\n\t}\n\n\tinline bool CalibratedCameras::isCameraUsedForRendering(size_t camId) const\n\t{\n\t\treturn (_usedCameraFlag.empty()) ? false : _usedCameraFlag[camId];\n\t}\n\n\tinline void CalibratedCameras::usedCameraForRendering(const std::vector<bool> usedCamera)\n\t{\n\t\t_usedCameraFlag = usedCamera;\n\t}\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/Config.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n# include <iomanip>\r\n\r\n\r\n#ifdef SIBR_OS_WINDOWS\r\n//// Export Macro (used for creating DLLs) ////\r\n# ifdef SIBR_STATIC_DEFINE\r\n#   define SIBR_EXPORT\r\n#   define SIBR_NO_EXPORT\r\n# else\r\n#   ifndef SIBR_SCENE_EXPORT\r\n#     ifdef SIBR_SCENE_EXPORTS\r\n         /* We are building this library */\r\n#       define SIBR_SCENE_EXPORT __declspec(dllexport)\r\n#     else\r\n         /* We are using this library */\r\n#       define SIBR_SCENE_EXPORT __declspec(dllimport)\r\n#     endif\r\n#   endif\r\n#   ifndef SIBR_NO_EXPORT\r\n#     define SIBR_NO_EXPORT\r\n#   endif\r\n# endif\r\n# else\r\n#  define SIBR_SCENE_EXPORT\r\n# endif\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/ICalibratedCameras.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n# include \"core/scene/IParseData.hpp\"\n#include \"core/scene/Config.hpp\"\n\nnamespace sibr\n{\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT ICalibratedCameras {\n\tpublic:\n\n\t\t/**\n\t\t* \\brief Pointer to the instance of class sibr::CalibratedCameras.\n\t\t*/\n\t\ttypedef std::shared_ptr<ICalibratedCameras>\tPtr;\n\n\t\t// load from a path on disk in a predefined format (or could detect from file extension)\n\n\t\t/**\n\t\t* \\brief Creates the calibrated cameras for a scene given data parsed from dataset path.\n\t\t* \n\t\t* \\param data Holds all information required to created a set of calibrated cameras\n\t\t*/\n\t\tvirtual void\tsetupFromData(const IParseData::Ptr & data) = 0;\n\n\t\t/**\n\t\t* \\brief Assigns the calibrated cameras for a scene to a list of cameras passed as parameter.\n\t\t*\n\t\t* \\param cams Vector of type sibr::InputCamera to which the scene inputCameras will be set\n\t\t*/\n\t\tvirtual void\tsetupCamerasFromExisting(const std::vector<InputCamera::Ptr> & cams) = 0;\n\t\t\n\t\t/**\n\t\t* \\brief Function to set a camera as active.\n\t\t* \n\t\t* \\param camId Integer ID of the camera to be set active\n\t\t*/\n\t\tvirtual void\tactivateCamera(uint camId) = 0;\n\n\t\t/**\n\t\t* \\brief Function to set a camera as inactive.\n\t\t*\n\t\t* \\param camId Integer ID of the camera to be set inactive\n\t\t*/\n\t\tvirtual void\tdeactivateCamera(uint camId) = 0;\n\n\t\t/**\n\t\t* \\brief Function to mark the cameras used for rendering.\n\t\t* Generally used for debugging purposes\n\t\t* \\param selectedCameras list of camera IDs that are used for rendering\n\t\t*/\n\t\tvirtual void\tdebugFlagCameraAsUsed(const std::vector<uint>& selectedCameras) = 0;\n\n\t\t/**\n\t\t* \\brief Function to check if the camera is used for rendering.\n\t\t*\n\t\t* \\param camId Integer ID of the cameras to be checked if it is being used for rendering\n\t\t* \\return true if used for rendering\n\t\t*/\n\t\tvirtual bool\tisCameraUsedForRendering(size_t camId) const = 0;\n\t\t\n\t\t/**\n\t\t* \\brief Function to set the cameras used for rendering.\n\t\t*\n\t\t* \\param usedCamera Vector to specify which cameras are used for rendering \n\t\t*/\n\t\tvirtual void\tusedCameraForRendering(const std::vector<bool> usedCamera) = 0;\n\n\t\t/**\n\t\t* \\brief Getter to the vector of input cameras used to create the scene\n\t\t*\n\t\t*/\n\t\tvirtual const std::vector<InputCamera::Ptr>&\t\t\t\tinputCameras(void) const = 0;\n\n\n\t\tvirtual const void\t\t\t\t\t\t\tupdateNearsFars(std::vector<sibr::Vector2f> & nearsFars) = 0;\n\n\t};\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/IIBRScene.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <core/system/CommandLineArgs.hpp>\n#include \"core/raycaster/CameraRaycaster.hpp\"\n#include \"core/scene/ICalibratedCameras.hpp\"\n#include \"core/scene/IParseData.hpp\"\n#include \"core/scene/IProxyMesh.hpp\"\n#include \"core/scene/IInputImages.hpp\"\n#include \"core/scene/RenderTargetTextures.hpp\"\n#include \"core/scene/Config.hpp\"\n#include \"core/system/String.hpp\"\n\nnamespace sibr {\n\n\t/**\n\t* Interface used to define how an IBR Scene is shaped\n\t* containing multiple components required to define a scene.\n\t*\n\t* Members:\n\t* - ICalibratedCameras\n\t* - IInputImages\n\t* - IProxyMesh\n\t* - RenderTargetTextures\n\t* \n\t* \\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT IIBRScene\n\t{\n\n\t\t/**\n\t\t* \\brief Pointer to the instance of class sibr::IIBRScene.\n\t\t*/\n\t\tSIBR_CLASS_PTR(IIBRScene);\n\t\t\n\tpublic:\n\t\t/** Scene initialization infos. */\n\t\tstruct SceneOptions\n\t\t{\n\t\t\tbool\t\trenderTargets = true; ///< Load rendertargets?\n\t\t\tbool\t\tmesh = true; ///< Load mesh?\n\t\t\tbool\t\timages = true; ///< Load images?\n\t\t\tbool\t\tcameras = true; ///< Load cameras?\n\t\t\tbool        texture = true; ///< Load texture ?\n\n\t\t\tSceneOptions() {}\n\t\t};\n\n\t\t/**\n\t\t* \\brief Creates a BasicIBRScene given custom data argument.\n\t\t* The scene will be created using the custom data (cameras/images/proxies/textures etc.) provided.\n\t\t* \\param data to provide data instance holding customized components.\n\t\t* \\param width the constrained width for GPU texture data.\n\t\t* \\param myOpts to specify whether to initialize specific parts of the scene (RTs, geometry,...)\n\t\t*/\n\t\tvirtual void createFromCustomData(const IParseData::Ptr & data, const uint width = 0, SceneOptions myOpts = SceneOptions()) = 0;\n\t\t\n\t\t/**\n\t\t * \\brief Function to create a scene directly using the dataset path specified in command-line.\n\t\t */\n\t\tvirtual void\tcreateFromDatasetPath() = 0;\n\n\t\t/**\n\t\t* \\brief Function to generate render targets using the _data (regarding cameras, images, proxies ) parsed from metadata file.\n\t\t*/\n\t\tvirtual void\tcreateRenderTargets() = 0;\n\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the data related to the scene.\n\t\t * \n\t\t */\n\t\tvirtual const IParseData::Ptr\t\t\t\t\t\tdata(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the pointer holding the data related to the scene for scene creation.\n\t\t* \\param data the setup data\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\tdata(const sibr::IParseData::Ptr & data) = 0;\n\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding cameras related to each input iamge of the scene.\n\t\t *\n\t\t */\n\t\tvirtual const ICalibratedCameras::Ptr\t\t\t\tcameras(void) const = 0;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the input images to the scene.\n\t\t *\n\t\t */\n\t\tvirtual const IInputImages::Ptr\t\t\t\t\t\timages(void) const = 0;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the proxies required by the scene.\n\t\t *\n\t\t */\n\t\tvirtual const IProxyMesh::Ptr\t\t\t\t\t\tproxies(void) const = 0;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the render targets textures related to the scene.\n\t\t *\n\t\t */\n\t\tvirtual const RenderTargetTextures::Ptr\t&\t\t\trenderTargets(void) const = 0;\n\t\t\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the render targets textures related to the scene.\n\t\t *\n\t\t */\n\t\tvirtual RenderTargetTextures::Ptr &\t\t\t\t\trenderTargets(void) = 0;\n\n\t\t/**\n\t\t * \\brief Getter for the pointer holding the mesh textures related to the mesh loaded for the scene.\n\t\t *\n\t\t */\n\t\tvirtual Texture2DRGB::Ptr &\t\t\t\t\t\t\tinputMeshTextures(void) = 0;\n\t\t\n\t};\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/IInputImages.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/scene/Config.hpp\"\n#include \"core/scene/IParseData.hpp\"\n\nnamespace sibr\n{\n\t/** \n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT IInputImages {\n\t\tSIBR_DISALLOW_COPY(IInputImages);\n\tpublic:\n\n\n\t\ttypedef std::shared_ptr<IInputImages>\t\t\t\tPtr;\n\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\tloadFromData(const IParseData::Ptr & data) = 0;\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\tloadFromExisting(const std::vector<sibr::ImageRGB::Ptr> & imgs) = 0;\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\tloadFromExisting(const std::vector<sibr::ImageRGB> & imgs) = 0;\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\tloadFromPath(const IParseData::Ptr & data, const std::string & prefix, const std::string & postfix) = 0;\n\n\t\t// Alpha blend and modify input images -- for fences\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\talphaBlendInputImages(const std::vector<sibr::ImageRGB>& back, std::vector<sibr::ImageRGB>& alphas) = 0;\n\n\t\tvirtual const std::vector<sibr::ImageRGB::Ptr>&\t\tinputImages(void) const = 0;\n\t\tvirtual const\tsibr::ImageRGB& \t\t\t\t\t\timage(uint i) = 0;\n\n\tprotected:\n\t\tIInputImages() {};\n\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/IParseData.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n#include \"picojson/picojson.hpp\"\n#include \"core/scene/Config.hpp\"\n#include \"core/system/CommandLineArgs.hpp\"\n#\n#include \"core/system/Matrix.hpp\"\n#include \"core/assets/ImageListFile.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n\n\n#include <iostream>\n#include <vector>\n#include <string>\n\n\nnamespace sibr{\n\n\t/**\n\t* Interface used to store the data required for defining an IBR Scene\n\t* \n\t*\n\t* Members:\n\t* - _basePathName: Base dataset directory path.\n\t* - _camInfos: Vector of sibr::InputCamera holding all data attached with the scene cameras.\n\t* - _meshPath: Filepath of the mesh associated to the scene.\n\t* - _imgInfos: Vector of sibr::ImageListFile::Infos holding filename, width, height, and id of the input images.\n\t* - _imgPath: Path to the calibrated images directory.\n\t* - _activeImages: Vector of bools storing active state of the camera.\n\t* - _numCameras: Number of cameras associated with the dataset\n\t* - _datasetType: Type if dataset being used. Currently supported: COLMAP, SIBR_BUNDLER, NVM, MESHROOM\n\t*\n\t* \\ingroup sibr_scene\n\t*/\n\n\tclass SIBR_SCENE_EXPORT IParseData {\n\t\t\n\tpublic:\n\n\t\t/**\n\t\t * \\brief Denotes the type of dataset represented by a IParseData object.\n\t\t* \\ingroup sibr_scene\n\t\t*/\n\t\tenum class Type {\n\t\t\tEMPTY, GAUSSIAN, BLENDER, SIBR, COLMAP_CAPREAL, COLMAP, COLMAP2, NVM, MESHROOM, CHUNKED, EXTERNAL\n\t\t};\n\n\t\t/**\n\t\t* \\brief Pointer to the instance of class sibr::IParseData.\n\t\t*/\n\t\ttypedef std::shared_ptr<IParseData>\t\t\t\tPtr;\n\n\t\t/**\n\t\t* \\brief Function to parse data from a dataset path. Will automatically determine the type of dataset based on the files present.\n\t\t* \\param myArgs Arguments containing the dataset path and other infos\n\t\t* \\param customPath additional data path\n\t\t*/\n\t\tvirtual void  getParsedData(const BasicIBRAppArgs & myArgs, const std::string & customPath = \"\") = 0;\n\n\t\t/**\n\t\t* \\brief Getter for the information regarding the input images.\n\t\t*\n\t\t*/\n\t\tvirtual const std::vector<sibr::ImageListFile::Infos>&\timgInfos(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the information regarding the input images.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\timgInfos(std::vector<sibr::ImageListFile::Infos>& infos) = 0;\n\n\t\t/**\n\t\t* \\brief Getter to the number of cameras defined in the bundle file.\n\t\t*\n\t\t*/\n\t\tvirtual const int\t\t\t\t\t\t\t\t\t\tnumCameras(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter to the number of cameras defined in the bundle file.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\tnumCameras(int numCams) = 0;\n\n\t\t/**\n\t\t* \\brief Getter for the list of active cameras/images.\n\t\t*\n\t\t*/\n\t\tvirtual const std::vector<bool>&\t\t\t\t\t\tactiveImages(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the list of active cameras/images.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\tactiveImages(std::vector<bool>& activeCams) = 0;\n\n\t\t/**\n\t\t* \\brief Getter for the base path name where the dataset is located.\n\t\t*\n\t\t*/\n\t\tvirtual const std::string&\t\t\t\t\t\t\t\tbasePathName(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the base path name where the dataset is located.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\tbasePathName(std::string & path)  = 0;\n\t\t\n\t\t/**\n\t\t* \\brief Getter for the mesh path where the dataset is located.\n\t\t*\n\t\t*/\n\t\tvirtual const std::string&\t\t\t\t\t\t\t\tmeshPath(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the mesh path where the dataset is located.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\tmeshPath(std::string & path)  = 0;\n\n\t\t/**\n\t\t* \\brief Getter for the dataset type.\n\t\t*\n\t\t*/\n\t\tvirtual const IParseData::Type&\t\t\t\t\t\t\tdatasetType(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the dataset type.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\tdatasetType(IParseData::Type dataType) = 0;\n\n\t\t/**\n\t\t* \\brief Getter for the camera infos.\n\t\t*\n\t\t*/\n\t\tvirtual const std::vector<InputCamera::Ptr>\tcameras(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the camera infos.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\tcameras(std::vector<InputCamera::Ptr>& cams) = 0;\n\n\t\t/**\n\t\t* \\brief Getter for the image path.\n\t\t*\n\t\t*/\n\t\tvirtual const std::string\t\t\t\t\t\t\t\timgPath(void) const = 0;\n\n\t\t/**\n\t\t* \\brief Setter for the image path.\n\t\t*\n\t\t*/\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\timgPath(std::string& imPath) = 0;\n\t\t\n\t};\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/IProxyMesh.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/scene/Config.hpp\"\n#include \"core/scene/IParseData.hpp\"\n#include \"core/graphics/Mesh.hpp\"\n\nnamespace sibr {\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT IProxyMesh {\n\t\tSIBR_DISALLOW_COPY(IProxyMesh);\n\tpublic:\n\t\ttypedef std::shared_ptr<IProxyMesh>\t\t\t\t\tPtr;\n\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\t\tloadFromData(const IParseData::Ptr & data) = 0;\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\t\treplaceProxy(Mesh::Ptr newProxy) = 0;\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\t\t\treplaceProxyPtr(Mesh::Ptr newProxy) = 0;\n\t\tvirtual bool\t\t\t\t\t\t\t\t\t\t\t\thasProxy(void) const = 0;\n\t\tvirtual const Mesh&\t\t\t\t\t\t\t\t\t\t\tproxy(void) const = 0;\n\t\tvirtual const Mesh::Ptr\t\t\t\t\t\t\t\t\t\tproxyPtr(void) const = 0;\n\n\tprotected:\n\t\tIProxyMesh() {};\n\n\t};\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/InputImages.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"InputImages.hpp\"\n\n\nnamespace sibr\n{\n\tvoid InputImages::loadFromData(const IParseData::Ptr & data)\n\t{\n\t\t//InputImages out;\n\t\t_inputImages.resize(data->imgInfos().size());\n\n\t\tif (data->imgInfos().empty() == false)\n\t\t{\n//\t\t\t#pragma omp parallel for\n\t\t\tfor (int i = 0; i < data->imgInfos().size(); ++i) {\n\t\t\t\tif (data->activeImages()[i]) {\n\t\t\t\t\t_inputImages[i] = std::make_shared<ImageRGB>();\n\t\t\t\t\t_inputImages[i]->load(data->imgPath() + \"/\" + data->imgInfos().at(i).filename, false);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_inputImages[i] = std::make_shared<ImageRGB>(16,16, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"cannot load images (ImageListFile is empty. Did you use ImageListFile::load(...) before ?\";\n\n\t\tstd::cout << std::endl;\n\t\t\n\t\treturn;\n\t}\n\n\tvoid InputImages::loadFromExisting(const std::vector<sibr::ImageRGB> & imgs)\n\t{\n\t\t_inputImages.resize(imgs.size());\n\t\tfor (size_t i = 0; i < imgs.size(); ++i) {\n\t\t\t_inputImages[i].reset(new ImageRGB(imgs[i].clone()));\n\t\t}\n\t}\n\n\t/// \\todo UN-TESTED code!!!!\n\tvoid InputImages::loadFromPath(const IParseData::Ptr & data, const std::string & prefix, const std::string & postfix)\n\t{\n\t\t_inputImages.resize(data->imgInfos().size());\n\n\t\t#pragma omp parallel for\n\t\tfor (int i = 0; i < data->imgInfos().size(); ++i) {\n\t\t\tif (data->activeImages()[i]) {\n\t\t\t\tstd::string imgPath = data->basePathName()+ \"/images/\" + prefix + sibr::imageIdToString(i) + postfix;\n\t\t\t\tif (!_inputImages[i]->load(imgPath, false)) {\n\t\t\t\t\tSIBR_WRG << \"could not load input image : \" << imgPath << std::endl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\tvoid InputImages::alphaBlendInputImages(const std::vector<sibr::ImageRGB>& back, std::vector<sibr::ImageRGB>& alphas)\n\t{\n\t\tfor (uint i = 0; i< _inputImages.size(); i++) {\n\t\t\t// check size\n\t\t\tif (_inputImages[i]->w() != alphas[i].w() ||\n\t\t\t\t_inputImages[i]->h() != alphas[i].h())\n\t\t\t\talphas[i] = alphas[i].resized(_inputImages[i]->w(), _inputImages[i]->h());\n\t\t\tfor (uint x = 0; x<_inputImages[i]->w(); x++)\n\t\t\t\tfor (uint y = 0; y<_inputImages[i]->h(); y++) {\n\t\t\t\t\tImageRGB::Pixel p = _inputImages[i](x, y);\n\t\t\t\t\tImageRGB::Pixel bp = back[i](x, y);\n\t\t\t\t\tImageRGB::Pixel a = alphas[i](x, y);\n\t\t\t\t\tVector3f alpha(float(a[0] / 255.), float(a[1] / 255.), float(a[2] / 255.));\n\t\t\t\t\tfloat al = alpha[0]; // assume grey for now\n\t\t\t\t\tVector3f val = Vector3f(float(p[0]), float(p[1]), float(p[2]));\n\t\t\t\t\tVector3f bval = Vector3f(float(bp[0]), float(bp[1]), float(bp[2]));\n\t\t\t\t\tVector3f out;\n\t\t\t\t\tif (alpha[0] > 0.4) {\n\t\t\t\t\t\tout = (val - bval + al*bval) / al; // foreground solving for a\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   // clamp\n#define NZ(x)\t\t((x)<0.f?0.f:x)\n#define UP(x)\t\t((x)>255.f?255.f:x)\n\t\t\t\t\t\tout[0] = UP(NZ(out[0]));\n\t\t\t\t\t\tout[1] = UP(NZ(out[1]));\n\t\t\t\t\t\tout[2] = UP(NZ(out[2]));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tout = Vector3f(0, 0, 0);\n\n\t\t\t\t\t_inputImages[i](x, y) = ImageRGB::Pixel((uint)out[0], (uint)out[1], (uint)out[2]);\n\t\t\t\t}\n\t\t}\n\t}\n}\n\t\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/InputImages.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/scene/IInputImages.hpp\"\n#include \"core/scene/Config.hpp\"\n\nnamespace sibr\n{\n\t/** \n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT InputImages : public IInputImages {\n\t\tSIBR_DISALLOW_COPY(InputImages);\n\tpublic:\n\n\n\t\ttypedef std::shared_ptr<InputImages>\t\t\t\tPtr;\n\n\t\tInputImages(){};\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\tloadFromData(const IParseData::Ptr & data) override;\n\t\tvirtual void\t\t\t\t\t\t\t\t\t\tloadFromExisting(const std::vector<sibr::ImageRGB::Ptr> & imgs) override;\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\tloadFromExisting(const std::vector<sibr::ImageRGB> & imgs) override;\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\tloadFromPath(const IParseData::Ptr & data, const std::string & prefix, const std::string & postfix) override;\n\n\t\t// Alpha blend and modify input images -- for fences\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\talphaBlendInputImages(const std::vector<sibr::ImageRGB>& back, std::vector<sibr::ImageRGB>& alphas) override;\n\n\t\tconst std::vector<sibr::ImageRGB::Ptr>&\t\t\t\tinputImages(void) const override;\n\t\tconst sibr::ImageRGB& \t\t\t\t\t\t\t\timage(uint i) override\t{\treturn *_inputImages[i]; }\n\n\t\t~InputImages(){};\n\n\tprotected:\n\n\t\tstd::vector<sibr::ImageRGB::Ptr>\t\t\t\t\t\t\t_inputImages;\n\n\t};\n\n\tinline void InputImages::loadFromExisting(const std::vector<sibr::ImageRGB::Ptr>& imgs)\n\t{\n\t\t_inputImages = imgs;\n\t}\n\n\tinline const std::vector<sibr::ImageRGB::Ptr>& InputImages::inputImages(void) const {\n\t\treturn _inputImages;\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/ParseData.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"ParseData.hpp\"\n\n#include <fstream>\n#include <sstream>\n#include <algorithm>\n\n#include <boost/algorithm/string/split.hpp>\n#include <boost/algorithm/string/classification.hpp>\n#include <boost/algorithm/string/case_conv.hpp>\n#include <map>\n#include \"core/system/String.hpp\"\n#include \"core/graphics/Mesh.hpp\"\n#include \"core/system/Utils.hpp\"\n#include <algorithm>\n\nusing namespace boost::algorithm;\nnamespace sibr {\n\n\n\tbool ParseData::parseBundlerFile(const std::string & bundler_file_path)\n\t{\n\t\t// check bundler file\n\t\tstd::ifstream bundle_file(bundler_file_path);\n\t\tif (!bundle_file.is_open()) {\n\t\t\tSIBR_ERR << \"Bundler file does not exist at \" + bundler_file_path << std::endl;\n\t\t}\n\n\t\t// read number of images\n\t\tstd::string line;\n\t\tsafeGetline(bundle_file, line);\t// ignore first line - contains version\n\n\t\tbundle_file >> _numCameras;\t// read first value (number of images)\n\t\tsafeGetline(bundle_file, line);\t// ignore the rest of the line\n\n\t\t//_outputCamsMatrix.resize(_numCameras);\n\t\t_camInfos.resize(_numCameras);\n\t\tfor (int i = 0; i < _numCameras; i++) {\n\t\t\tconst sibr::ImageListFile::Infos& infos = _imgInfos[i];\n\n\t\t\t//Matrix4f &m = _outputCamsMatrix[i];\n\t\t\tMatrix4f m;\n\t\t\tbundle_file >> m(0) >> m(1) >> m(2) >> m(3) >> m(4);\n\t\t\tbundle_file >> m(5) >> m(6) >> m(7) >> m(8) >> m(9);\n\t\t\tbundle_file >> m(10) >> m(11) >> m(12) >> m(13) >> m(14);\n\n\t\t\t_camInfos[i] = InputCamera::Ptr(new InputCamera(infos.camId, infos.width, infos.height, m, _activeImages[i]));\n\t\t\t_camInfos[i]->name(infos.filename);\n\t\t\t_camInfos[i]->znear(0.001f);\n\t\t\t_camInfos[i]->zfar(1000.0f);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tvoid ParseData::populateFromCamInfos()\n\t{\n\t\t_numCameras = _camInfos.size();\n\t\t_imgInfos.resize(_numCameras);\n\t\t_activeImages.resize(_numCameras);\n\t\tfor (uint id = 0; id < _numCameras; id++) {\n\t\t\t_imgInfos[id].camId = _camInfos[id]->id();\n\t\t\t_imgInfos[id].filename = _camInfos[id]->name();\n\t\t\t_imgInfos[id].height = _camInfos[id]->h();\n\t\t\t_imgInfos[id].width = _camInfos[id]->w();\n\n\t\t\t_activeImages[id] = _camInfos[id]->isActive();\n\t\t}\n\t}\n\n\tbool ParseData::parseSceneMetadata(const std::string& scene_metadata_path)\n\t{\n\n\t\tstd::string line;\n\t\tstd::vector<std::string> splitS;\n\t\tstd::ifstream scene_metadata(scene_metadata_path);\n\t\tif (!scene_metadata.is_open()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tuint camId = 0;\n\n\t\twhile (safeGetline(scene_metadata, line))\n\n\t\t{\n\t\t\tif (line.compare(\"[list_images]\") == 0 )\n\t\t\t{\n\t\t\t\tsafeGetline(scene_metadata, line);\t// ignore template specification line\n\t\t\t\tImageListFile::Infos infos;\n\t\t\t\tint id;\n\t\t\t\twhile (safeGetline(scene_metadata, line))\n\t\t\t\t{\n//\t\t\t\t\tstd::cerr << line << std::endl;\n\t\t\t\t\tsplit(splitS, line, is_any_of(\" \"));\n//\t\t\t\t\tstd::cerr << splitS.size() << std::endl;\n\t\t\t\t\tif (splitS.size() > 1) {\n\t\t\t\t\t\tinfos.filename = splitS[0];\n\t\t\t\t\t\tinfos.width = stoi(splitS[1]);\n\t\t\t\t\t\tinfos.height = stoi(splitS[2]);\n\t\t\t\t\t\tinfos.camId = camId;\n\n\t\t\t\t\t\t//infos.filename.erase(infos.filename.find_last_of(\".\"), std::string::npos);\n\t\t\t\t\t\tid = atoi(infos.filename.c_str());\n\n\t\t\t\t\t\tInputCamera::Z nearFar(100.0f, 0.1f);\n\n\t\t\t\t\t\tif (splitS.size() > 3) {\n\t\t\t\t\t\t\tnearFar.near = stof(splitS[3]);\n\t\t\t\t\t\t\tnearFar.far = stof(splitS[4]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_imgInfos.push_back(infos);\n\n\t\t\t\t\t\t++camId;\n\t\t\t\t\t\tinfos.filename.clear();\n\t\t\t\t\t\tsplitS.clear();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (line.compare(\"[active_images]\") == 0) {\n\n\t\t\t\tsafeGetline(scene_metadata, line);\t// ignore template specification line\n\n\t\t\t\t_activeImages.resize(_imgInfos.size());\n\n\t\t\t\tfor (int i = 0; i < _imgInfos.size(); i++)\n\t\t\t\t\t_activeImages[i] = false;\n\n\t\t\t\twhile (safeGetline(scene_metadata, line))\n\t\t\t\t{\n\t\t\t\t\tsplit(splitS, line, is_any_of(\" \"));\n\t\t\t\t\t//std::cout << splitS.size() << std::endl;\n\t\t\t\t\tif (splitS.size() >= 1) {\n\t\t\t\t\t\tfor (auto& s : splitS)\n\t\t\t\t\t\t\tif (!s.empty())\n\t\t\t\t\t\t\t\t_activeImages[stoi(s)] = true;\n\t\t\t\t\t\tsplitS.clear();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (line.compare(\"[exclude_images]\") == 0) {\n\n\t\t\t\tsafeGetline(scene_metadata, line);\t// ignore template specification line\n\n\t\t\t\t_activeImages.resize(_imgInfos.size());\n\n\t\t\t\tfor (int i = 0; i < _imgInfos.size(); i++)\n\t\t\t\t\t_activeImages[i] = true;\n\n\t\t\t\twhile (safeGetline(scene_metadata, line))\n\t\t\t\t{\n\t\t\t\t\tsplit(splitS, line, is_any_of(\" \"));\n\t\t\t\t\tif (splitS.size() >= 1) {\n\t\t\t\t\t\tfor (auto& s : splitS)\n\t\t\t\t\t\t\tif (!s.empty())\n\t\t\t\t\t\t\t\t_activeImages[stoi(s)] = false;\n\t\t\t\t\t\tsplitS.clear();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (line == \"[proxy]\") {\n\t\t\t\t// Read the relative path of the mesh to load.\n\t\t\t\tsafeGetline(scene_metadata, line);\n\n\t\t\t\t_meshPath = _basePathName + \"/\" + line;\n\t\t\t}\n\t\t}\n\n\t\tif (_activeImages.empty()) {\n\t\t\t_activeImages.resize(_imgInfos.size());\n\t\t\tfor (int i = 0; i < _imgInfos.size(); i++) {\n\t\t\t\t_activeImages[i] = true;\n\t\t\t}\n\t\t}\n\n\n\n\t\tscene_metadata.close();\n\n\t\treturn true;\n\t}\n\n\tvoid ParseData::getParsedBundlerData(const std::string & dataset_path, const std::string & customPath, const std::string & scene_metadata_filename)\n\t{\n\t\t_basePathName = dataset_path + customPath;\n\t\t/*std::cout << scene_metadata_filename << std::endl;*/\n\t\tif (!parseSceneMetadata(_basePathName + \"/\" + scene_metadata_filename)) {\n\t\t\tSIBR_ERR << \"Scene Metadata file does not exist at /\" + _basePathName + \"/.\" << std::endl;\n\t\t}\n\n\t\tif (!parseBundlerFile(_basePathName + \"/cameras/bundle.out\")) {\n\t\t\tSIBR_ERR << \"Bundle file does not exist at /\" + _basePathName + \"/cameras/.\" << std::endl;\n\t\t}\n\n\t\t_imgPath = _basePathName + \"/images/\";\n\n\t\t// Default mesh path if none found in the metadata file.\n\t\tif (_meshPath.empty()) {\n\t\t\t_meshPath = _basePathName + \"/meshes/recon.obj\";\n\t\t\t_meshPath = (sibr::fileExists(_meshPath)) ? _meshPath : _basePathName + \"/meshes/recon.ply\";\n\t\t}\n\n\t}\n\n\tvoid ParseData::getParsedMeshroomData(const std::string & dataset_path, const std::string & customPath)\n\t{\t\t\n\t\t_basePathName = dataset_path;\n\n\t\tstd::string meshRoomCachePath = sibr::listSubdirectories(_basePathName + \"/StructureFromMotion/\")[0];\n\n\t\t_camInfos = sibr::InputCamera::loadMeshroom(_basePathName + \"/StructureFromMotion/\" + meshRoomCachePath);\n\n\t\tif (_camInfos.empty()) {\n\t\t\tSIBR_ERR << \"Could not load Meshroom sfm file at /\" + _basePathName + \"/StructureFromMotion/\"<< meshRoomCachePath << std::endl;\n\t\t}\n\n\t\t_imgPath = _basePathName + \"/PrepareDenseScene/\" + sibr::listSubdirectories(_basePathName + \"/PrepareDenseScene/\")[0];\n\n\t\tpopulateFromCamInfos();\n\n\t\t_meshPath = _basePathName + \"/Texturing/\" + sibr::listSubdirectories(_basePathName + \"/Texturing/\")[0] + \"/texturedMesh.obj\";\n\t}\n\n\tvoid ParseData::getParsedBlenderData(const std::string& dataset_path)\n\t{\n\t\t_camInfos = InputCamera::loadTransform(dataset_path + \"/transforms_test.json\", 800, 800, \"png\", 0.01f, 1000.0f);\n\t\tauto testInfos = InputCamera::loadTransform(dataset_path + \"/transforms_train.json\", 800, 800, \"png\", 0.01f, 1000.0f, _camInfos.size());\n\t\t_camInfos.insert(_camInfos.end(), testInfos.begin(), testInfos.end());\n\n\t\t_basePathName = dataset_path;\n\n\t\tif (_camInfos.empty()) {\n\t\t\tSIBR_ERR << \"Colmap camera calibration file does not exist at /\" + _basePathName + \"/sparse/.\" << std::endl;\n\t\t}\n\n\t\t_imgPath = dataset_path;\n\n\t\tpopulateFromCamInfos();\n\n\t\t_meshPath = dataset_path;\n\t}\n\n\tvoid ParseData::getParsedGaussianData(const std::string& dataset_path)\n\t{\n\t\t_camInfos = InputCamera::loadJSON(dataset_path + \"/cameras.json\");\n\t\t_meshPath = dataset_path + \"/input.ply\";\n\n\t\t_basePathName = dataset_path;\n\n\t\t_imgPath = \".\";\n\n\t\tpopulateFromCamInfos();\n\n\t\t_meshPath = dataset_path + \"/input.ply\";\n\t}\n\n\tvoid ParseData::getParsedColmap2Data(const std::string& dataset_path, const int fovXfovY_flag, const bool capreal_flag)\n\t{\n\t\t_basePathName = dataset_path + \"/sparse/0/\";\n\n\t\t_camInfos = sibr::InputCamera::loadColmapBin(_basePathName, 0.01f, 1000.0f, fovXfovY_flag);\n\n\t\tif (_camInfos.empty()) {\n\t\t\t_camInfos = sibr::InputCamera::loadColmap(_basePathName, 0.01f, 1000.0f, fovXfovY_flag);\n\t\t}\n\n\t\tif (_camInfos.empty()) {\n\t\t\tSIBR_ERR << \"Colmap camera calibration file does not exist at /\" + _basePathName + \"/sparse/.\" << std::endl;\n\t\t}\n\n\t\t_imgPath = dataset_path + \"/images/\";\n\n\t\tpopulateFromCamInfos();\n\n\t\t_meshPath = dataset_path + \"/sparse/0/points3d.bin\";\n\n\t\tif (!std::ifstream(_meshPath).good())\n\t\t\t_meshPath = dataset_path + \"/sparse/0/points3d.txt\";\n\t}\n\n\tvoid colmapSave(const std::string& filename, const std::vector<InputCamera::Ptr>& xformPath, float scale) {\n\t\t// save as colmap images.txt file\n\t\tsibr::Matrix3f converter;\n\t\tconverter << 1, 0, 0,\n\t\t\t0, -1, 0,\n\t\t\t0, 0, -1;\n\n\t\tstd::ofstream outputColmapPath, outputColmapPathCams;\n\t\tstd::string colmapPathCams = parentDirectory(filename) + std::string(\"/cameras.txt\");\n\n\t\tstd::cerr << std::endl;\n\t\tstd::cerr << std::endl;\n\t\tstd::cerr << \"Writing colmap path to \" << parentDirectory(filename) << std::endl;\n\n\t\toutputColmapPath.open(filename);\n\t\tif (!outputColmapPath.good())\n\t\t\tSIBR_ERR << \"Cant open output file \" << filename << std::endl;\n\t\toutputColmapPathCams.open(colmapPathCams);\n\n\t\toutputColmapPathCams << \"# Camera list with one line of data per camera:\" << std::endl;\n\t\toutputColmapPathCams << \"#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]\" << std::endl;\n\t\toutputColmapPathCams << \"# Number of cameras: \" << xformPath.size() << std::endl;\n\n\t\tSIBR_WRG << \"No focal x given making it equal to focaly * aspect ratio; use result at own risk. Should have a colmap dataset as input\" << std::endl;\n\n\t\tfor (int i = 0; i < xformPath.size(); i++) {\n\t\t\tfloat focalx = xformPath[i]->focal() * xformPath[i]->aspect(); // use aspect ratio\n\t\t\toutputColmapPathCams << i + 1 << \" PINHOLE \" << xformPath[i]->w() * scale << \" \" << xformPath[i]->h() * scale\n\t\t\t\t<< \" \" << xformPath[i]->focal() * scale << \" \" << focalx * scale\n\t\t\t\t<< \" \" << xformPath[i]->w() * scale * 0.5 << \" \" << xformPath[i]->h() * scale * 0.5 << std::endl;\n\t\t}\n\n\n\t\toutputColmapPath << \"# Image list with two lines of data per image:\" << std::endl;\n\t\toutputColmapPath << \"#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME\" << std::endl;\n\t\toutputColmapPath << \"#   POINTS2D[] as (X, Y, POINT3D_ID)\" << std::endl;\n\t\tfor (int i = 0; i < xformPath.size(); i++) {\n\t\t\tsibr::Matrix3f tmp = xformPath[i]->rotation().toRotationMatrix() * converter;\n\t\t\tsibr::Matrix3f Qinv = tmp.transpose();\n\t\t\tsibr::Quaternionf q = quatFromMatrix(Qinv);\n\t\t\tsibr::Vector3f t = -Qinv * xformPath[i]->position();\n\n\t\t\toutputColmapPath << (i+1) << \" \" << q.w() << \" \" << -q.x() << \" \" << -q.y() << \" \" << -q.z() << \" \" <<\n\t\t\t\tt.x() << \" \" << t.y() << \" \" << t.z() << \" \" << (i+1) << \" \" << xformPath[i]->name() << std::endl;\n\t\t\toutputColmapPath << std::endl; // empty line, no points\n\t\t}\n\t\toutputColmapPath.close();\n\t\toutputColmapPathCams.close();\n\t}\n\n\tvoid ParseData::getParsedChunkedData(const std::string& dataset_path)\n\t{\n\t\t_basePathName = sibr::parentDirectory(sibr::parentDirectory(dataset_path));;\n\n\t\tauto test = sibr::getFileName(dataset_path);\n\t\tstd::replace(test.begin(), test.end(), '_', ' ');\n\t\tstd::stringstream ss(test);\n\t\tint x, y;\n\t\tss >> x >> y;\n\t\tx = 0;\n\t\ty = 0;\n\n\t\t_imgPath = _basePathName + \"/cameras/\";\n\n\t\tauto camdirs = sibr::listSubdirectories(_imgPath);\n\n\t\tfor (int i = 0; i < camdirs.size(); i++)\n\t\t{\n\t\t\tauto cam = std::make_shared<InputCamera>(0, 0, 0, 0, 0, 0, _camInfos.size());\n\t\t\tcam->loadFromBinary(_imgPath + camdirs[i] + \"/incam.bin\");\n\n\t\t\tauto quat = cam->transform().rotation();\n\t\t\tauto mat = sibr::matFromQuat(quat);\n\n\t\t\tif (mat(2, 2) > 0.9 || cam->position().x() < (x) * 100.9 || cam->position().x() > (x+1) * 100.9 || cam->position().y() < y * 100.9 || cam->position().y() > (y + 1) * 100.9)\n\t\t\t\tcontinue;\n\n\t\t\tcam->name(camdirs[i] + \".png\");\n\t\t\t_camInfos.push_back(cam);\n\t\t}\n\n\t\tpopulateFromCamInfos();\n\n\t\tcolmapSave(_basePathName + \"/sparse/images.txt\", _camInfos, 1.0f);\n\n\t\t_meshPath = dataset_path + \"/mesh.ply\";\n\t}\n\n\n\tvoid ParseData::getParsedColmapData(const std::string & dataset_path, const int fovXfovY_flag, const bool capreal_flag)\n\t{\n\t\t_basePathName = dataset_path + \"/colmap/stereo\";\n\n\t\t_camInfos = sibr::InputCamera::loadColmap(_basePathName + \"/sparse\", 0.01f, 1000.0f, fovXfovY_flag);\n\n\t\tif (_camInfos.empty()) {\n\t\t\tSIBR_ERR << \"Colmap camera calibration file does not exist at /\" + _basePathName + \"/sparse/.\" << std::endl;\n\t\t}\n\n\t\t_imgPath = _basePathName + \"/images/\";\n\n\t\tstd::string blackListFile = dataset_path + \"/colmap/database.blacklist\";\n\n\t\tif (sibr::fileExists(blackListFile)) {\n\t\t\tstd::string line;\n\t\t\tstd::vector<std::string> splitS;\n\t\t\tstd::ifstream blackListFileF(blackListFile);\n\t\t\tif (blackListFileF.is_open()) {\n\t\t\t\twhile (safeGetline(blackListFileF, line)) {\n\n\t\t\t\t\tsplit(splitS, line, is_any_of(\" \"));\n\t\t\t\t\t//std::cout << splitS.size() << std::endl;\n\t\t\t\t\tif (splitS.size() > 0) {\n\t\t\t\t\t\tfor (uint cam_id = 0; cam_id < _camInfos.size(); cam_id++) {\n\t\t\t\t\t\t\tif (find_any(splitS, _camInfos[cam_id]->name())) {\n\t\t\t\t\t\t\t\t_camInfos[cam_id]->setActive(false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsplitS.clear();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpopulateFromCamInfos();\n\n\t\tif(capreal_flag) {\n\t\t\t_meshPath = dataset_path + \"/capreal/mesh.obj\";\n\t\t\t_meshPath = (sibr::fileExists(_meshPath)) ? _meshPath : dataset_path + \"/capreal/mesh.ply\";\n\t\t}\n\t\telse {\n\t\t\t_meshPath = dataset_path + \"/colmap/stereo/meshed-delaunay.ply\";\n\t\t}\n\n\t}\n\n\tvoid ParseData::getParsedNVMData(const std::string & dataset_path, const std::string & customPath, const std::string & nvm_path)\n\t{\n\t\t_basePathName = dataset_path + customPath + nvm_path;\n\n\t\t_camInfos = sibr::InputCamera::loadNVM(_basePathName + \"/scene.nvm\", 0.001f, 1000.0f);\n\t\tif (_camInfos.empty()) {\n\t\t\tSIBR_ERR << \"Error reading NVM dataset at /\" + _basePathName << std::endl;\n\t\t}\n\n\t\t_imgPath = _basePathName;\n\n\t\tpopulateFromCamInfos();\n\n\t\t_meshPath = dataset_path + \"/capreal/mesh.obj\";\n\t\t_meshPath = (sibr::fileExists(_meshPath)) ? _meshPath : dataset_path + \"/capreal/mesh.ply\";\n\t}\n\n\tvoid ParseData::getParsedData(const BasicIBRAppArgs & myArgs, const std::string & customPath)\n\t{\n\t\tstd::string datasetTypeStr = myArgs.dataset_type.get();\n\t\t\n\t\tboost::algorithm::to_lower(datasetTypeStr);\n\n\t\tstd::string bundler = myArgs.dataset_path.get() + customPath + \"/cameras/bundle.out\";\n\t\tstd::string colmap = myArgs.dataset_path.get() + \"/colmap/stereo/sparse/images.txt\";\n\t\tstd::string colmap_2 = myArgs.dataset_path.get() + \"/sparse/0/images.bin\";\n\t\tstd::string caprealobj = myArgs.dataset_path.get() + \"/capreal/mesh.obj\";\n\t\tstd::string caprealply = myArgs.dataset_path.get() + \"/capreal/mesh.ply\";\n\t\tstd::string nvmscene = myArgs.dataset_path.get() + customPath + \"/nvm/scene.nvm\";\n\t\tstd::string meshroom = myArgs.dataset_path.get() + \"/../../StructureFromMotion/\";\n\t\tstd::string meshroom_sibr = myArgs.dataset_path.get() + \"/StructureFromMotion/\";\n\t\tstd::string chunked = myArgs.dataset_path.get() + \"/chunk.dat\";\n\t\tstd::string blender = myArgs.dataset_path.get() + \"/transforms_train.json\";\n\t\tstd::string gaussian = myArgs.dataset_path.get() + \"/cameras.json\";\n\n\t\tif(datasetTypeStr == \"sibr\") {\n\t\t\tif (!sibr::fileExists(bundler))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t\t\t << \"Reason : bundler folder (\" << bundler << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::SIBR;\n\t\t}\n\t\telse if (datasetTypeStr == \"colmap_capreal\") {\n\t\t\tif (!sibr::fileExists(colmap))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t\t\t << \"Reason : colmap folder (\" << colmap << \") does not exist\" << std::endl;\n\t\t\t\n\t\t\tif (!(sibr::fileExists(caprealobj) || sibr::fileExists(caprealply)))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t\t\t << \"Reason : capreal mesh (\" << caprealobj << \", \" << caprealply << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::COLMAP_CAPREAL;\n\t\t}\n\t\telse if (datasetTypeStr == \"colmap\") {\n\t\t\tif (!sibr::fileExists(colmap))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t\t\t << \"Reason : colmap folder (\" << colmap << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::COLMAP;\n\t\t}\n\t\telse if (datasetTypeStr == \"nvm\") {\n\t\t\tif (!sibr::fileExists(nvmscene))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t\t\t << \"Reason : nvmscene folder (\" << nvmscene << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::NVM;\n\t\t}\n\t\telse if (datasetTypeStr == \"meshroom\") {\n\t\t\tif (!(sibr::directoryExists(meshroom) || sibr::directoryExists(meshroom_sibr)))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t\t\t << \"Reason : meshroom folder (\" << meshroom << \", \" << meshroom_sibr << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::MESHROOM;\n\t\t}\n\t\telse if (datasetTypeStr == \"blender\")\n\t\t{\n\t\t\tif (!sibr::fileExists(blender))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t<< \"Reason : blender transform (\" << blender << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::BLENDER;\n\t\t}\n\t\telse if (datasetTypeStr == \"gaussian\")\n\t\t{\n\t\t\tif (!sibr::fileExists(gaussian))\n\t\t\t\tSIBR_ERR << \"Cannot use dataset_type \" + myArgs.dataset_type.get() + \" at /\" + myArgs.dataset_path.get() + \".\" << std::endl\n\t\t\t\t<< \"Reason : Gaussian transform (\" << blender << \") does not exist\" << std::endl;\n\n\t\t\t_datasetType = Type::BLENDER;\n\t\t}\n\t\telse {\n\t\t\tif (sibr::fileExists(bundler)) {\n\t\t\t\t_datasetType = Type::SIBR;\n\t\t\t}\n\t\t\telse if (sibr::fileExists(gaussian))\n\t\t\t{\n\t\t\t\t_datasetType = Type::GAUSSIAN;\n\t\t\t}\n\t\t\telse if (sibr::fileExists(colmap) && (sibr::fileExists(caprealobj) || sibr::fileExists(caprealply))) {\n\t\t\t\t_datasetType = Type::COLMAP_CAPREAL;\n\t\t\t}\n\t\t\telse if (sibr::fileExists(colmap)) {\n\t\t\t\t_datasetType = Type::COLMAP;\n\t\t\t}\n\t\t\telse if (sibr::fileExists(nvmscene)) {\n\t\t\t\t_datasetType = Type::NVM;\n\t\t\t}\n\t\t\telse if (sibr::directoryExists(meshroom) || sibr::directoryExists(meshroom_sibr)) {\n\t\t\t\t_datasetType = Type::MESHROOM;\n\t\t\t}\n\t\t\telse if (sibr::fileExists(colmap_2))\n\t\t\t\t_datasetType = Type::COLMAP2;\n\n\t\t\telse if (sibr::fileExists(chunked))\n\t\t\t{\n\t\t\t\t_datasetType = Type::CHUNKED;\n\t\t\t}\n\t\t\telse if (sibr::fileExists(blender))\n\t\t\t{\n\t\t\t\t_datasetType = Type::BLENDER;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tSIBR_ERR << \"Cannot determine type of dataset at /\" + myArgs.dataset_path.get() + customPath << std::endl;\n\t\t\t}\n\t\t}\n\n\t\tswitch(_datasetType) {\n\t\t\tcase Type::GAUSSIAN:\t\t\tgetParsedGaussianData(myArgs.dataset_path); break;\n\t\t\tcase Type::BLENDER:\t\t\tgetParsedBlenderData(myArgs.dataset_path); break;\n\t\t\tcase Type::SIBR : \t\t\tgetParsedBundlerData(myArgs.dataset_path, customPath, myArgs.scene_metadata_filename); break;\n\t\t\tcase Type::COLMAP_CAPREAL : getParsedColmapData(myArgs.dataset_path, myArgs.colmap_fovXfovY_flag, true); break;\n\t\t\tcase Type::COLMAP : \t\tgetParsedColmapData(myArgs.dataset_path, myArgs.colmap_fovXfovY_flag, false); break;\n\t\t\tcase Type::COLMAP2 : \t\tgetParsedColmap2Data(myArgs.dataset_path, myArgs.colmap_fovXfovY_flag, false); break;\n\t\t\tcase Type::CHUNKED:\t\t\tgetParsedChunkedData(myArgs.dataset_path); break;\n\t\t\tcase Type::NVM : \t\t\tgetParsedNVMData(myArgs.dataset_path, customPath, \"/nvm/\"); break;\n\t\t\tcase Type::MESHROOM : \t\tif (sibr::directoryExists(meshroom)) getParsedMeshroomData(myArgs.dataset_path.get() + \"/../../\");\n\t\t\t\t\t\t\t\t\t\telse if (sibr::directoryExists(meshroom_sibr)) getParsedMeshroomData(myArgs.dataset_path); break;\n\t\t}\n\t\t\n\t\t// What happens if multiple are present?\n\t\t// Ans: Priority --> SIBR > COLMAP > NVM\n\n\t\t// Subtract minCAMID from all\n\t\tuint minCamID = UINT_MAX;\n\t\tfor (const auto& cam : _camInfos)\n\t\t\tminCamID = std::min(minCamID, cam->id());\n\t\tfor (auto& cam : _camInfos)\n\t\t\tcam->_id -= minCamID;\n\t\tfor (auto& img : _imgInfos)\n\t\t\timg.camId -= minCamID;\n\n\t\t// Find max cam ID and check present image IDs\n\t\tint maxId = 0;\n\t\tstd::vector<bool> presentIDs;\n\t\t\n\t\tpresentIDs.resize(_numCameras);\n\n\t\tfor (int c = 0; c < _numCameras; c++) {\n\t\t\tmaxId = (maxId > int(_imgInfos[c].camId)) ? maxId : int(_imgInfos[c].camId);\n\t\t\tif (_imgInfos[c].camId >= presentIDs.size())\n\t\t\t{\n\t\t\t\t//SIBR_ERR << \"Incorrect Camera IDs \" << std::endl;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\tpresentIDs[_imgInfos[c].camId] = true;\n\t\t\t}\n\t\t\tcatch (const std::exception&)\n\t\t\t{\n\t\t\t\tSIBR_ERR << \"Incorrect Camera IDs \" << std::endl;\n\t\t\t}\n\t\t}\n\n\t\t// Check if max cam ID matches max number of cams\n\t\t// If not find the missing IDs \n\t\tstd::vector<int> missingIDs;\n\t\tint curid;\n\t\tint j, pos;\n\t\tif (maxId >= _numCameras) {\n\t\t\tfor (int i = 0; i < _numCameras; i++) {\n\t\t\t\tif (!presentIDs[i]) { missingIDs.push_back(i); }\n\t\t\t}\n\n\t\t\t// Now, shift the imgInfo IDs to adjust max Cam IDs\n\t\t\tfor (int k = 0; k < _numCameras; k++) {\n\t\t\t\tcurid = _imgInfos[k].camId;\n\t\t\t\tpos = -1;\n\t\t\t\tfor (j = 0; j < missingIDs.size(); j++) {\n\t\t\t\t\tif (curid > missingIDs[j]) { pos = j; }\n\t\t\t\t\telse { break; }\n\t\t\t\t}\n\n\t\t\t\t_imgInfos[k].camId = _imgInfos[k].camId - (pos + 1);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/ParseData.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/scene/IParseData.hpp\"\n\n\nnamespace sibr{\n\n\t/**\n\t* Class used to store the data required for defining an IBR Scene\n\t* \n\t*\n\t* Members:\n\t* - _basePathName: Base dataset directory path.\n\t* - _camInfos: Vector of sibr::InputCamera holding all data attached with the scene cameras.\n\t* - _meshPath: Filepath of the mesh associated to the scene.\n\t* - _imgInfos: Vector of sibr::ImageListFile::Infos holding filename, width, height, and id of the input images.\n\t* - _imgPath: Path to the calibrated images directory.\n\t* - _activeImages: Vector of bools storing active state of the camera.\n\t* - _numCameras: Number of cameras associated with the dataset\n\t* - _datasetType: Type if dataset being used. Currently supported: COLMAP, SIBR_BUNDLER, NVM, MESHROOM\n\t*\n\t* \\ingroup sibr_scene\n\t*/\n\n\tclass SIBR_SCENE_EXPORT ParseData: public IParseData {\n\t\t\n\tpublic:\n\n\t\t/**\n\t\t* \\brief Pointer to the instance of class sibr::ParseData.\n\t\t*/\n\t\ttypedef std::shared_ptr<ParseData>\t\t\t\tPtr;\n\n\t\t/**\n\t\t* \\brief Function to parse data from a template dataset path.\n\t\t* \\param dataset_path Path to the folder containing data\n\t\t* \\param customPath Path to algorithm specific data\n\t\t* \\param scene_metadata_filename Specify the filename of the Scene Metadata file to load specific scene\n\t\t*/\n\t\tvoid  getParsedBundlerData(const std::string & dataset_path, const std::string & customPath, const std::string & scene_metadata_filename);\n\n\t\t/**\n\t\t* \\brief Function to parse data from a template dataset path.\n\t\t* \\param dataset_path Path to the folder containing data\n\t\t* \\param customPath Path to algorithm specific data\n\t\t*/\n\t\tvoid  getParsedMeshroomData(const std::string & dataset_path, const std::string & customPath = \"\");\n\n\n\t\t/**\n\t\t* \\brief Function to parse data from a colmap dataset path.\n\t\t*\n\t\t* The function takes in a colmap dataset folder path and populates ParseData members with data.\n\t\t* This function can be used for direct compatibility with colmap data in SIBR.\n\t\t* The function automatically computes the intrinsic and extrinsic parameters of the camera, input images filename, widht and height etc.\n\t\t* Colmap uses LHS coordinate system while SIBR uses RHS coordinate system. The function applies appropriate transformation to handle this case.\n\t\t* \n\t\t* For further compatibility with FrIBR, which enforces a Y-up RHS coordinate system, we need to apply an extra conversion to the rotation matrix, to 'flip back' from y-down to y-up.\n\t\t* \\note Note: when applying the above mentioned conversion, the mesh needs to be converted by the same converter matrix\n\t\t* \\param dataset_path Path to the folder containing data\n\t\t* \\param fovXfovY_flag activate two dimensional fov parameters\n\t\t* \\param capreal_flag use capreal data\n\t\t*/\n\t\tvoid  getParsedColmapData(const std::string & dataset_path, const int fovXfovY_flag, const bool capreal_flag = true);\n\n\n\t\tvoid  getParsedColmap2Data(const std::string & dataset_path, const int fovXfovY_flag, const bool capreal_flag = true);\n\n\t\tvoid getParsedGaussianData(const std::string& dataset_path);\n\n\t\tvoid getParsedBlenderData(const std::string& dataset_path);\n\n\t\tvoid getParsedChunkedData(const std::string& dataset_path);\n\n\t\t/**\n\t\t* \\brief Function to parse data from a template dataset path.\n\t\t* \\param dataset_path Path to the folder containing data\n\t\t* \\param customPath Path to algorithm specific data\n\t\t* \\param nvm_path Specify the filename of the NVM path.\n\t\t*/\n\t\tvoid  getParsedNVMData(const std::string & dataset_path, const std::string & customPath, const std::string & nvm_path);\n\n\t\t/**\n\t\t* \\brief Function to parse data from a dataset path. Will automatically determine the type of dataset based on the files present.\n\t\t* \\param myArgs Arguments containing the dataset path and other infos\n\t\t* \\param customPath additional data path\n\t\t*/\n\t\tvoid  getParsedData(const BasicIBRAppArgs & myArgs, const std::string & customPath = \"\") override;\n\n\t\t/**\n\t\t* \\brief Getter for the information regarding the input images.\n\t\t*\n\t\t*/\n\t\tconst std::vector<sibr::ImageListFile::Infos>&\timgInfos(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the information regarding the input images.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\timgInfos(std::vector<sibr::ImageListFile::Infos>& infos) override;\n\n\t\t/**\n\t\t* \\brief Getter to the number of cameras defined in the bundle file.\n\t\t*\n\t\t*/\n\t\tconst int\t\t\t\t\t\t\t\t\t\tnumCameras(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter to the number of cameras defined in the bundle file.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\tnumCameras(int numCams) override;\n\n\t\t/**\n\t\t* \\brief Getter for the list of active cameras/images.\n\t\t*\n\t\t*/\n\t\tconst std::vector<bool>&\t\t\t\t\t\tactiveImages(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the list of active cameras/images.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\tactiveImages(std::vector<bool>& activeCams) override;\n\n\t\t/**\n\t\t* \\brief Getter for the base path name where the dataset is located.\n\t\t*\n\t\t*/\n\t\tconst std::string&\t\t\t\t\t\t\t\tbasePathName(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the base path name where the dataset is located.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\tbasePathName(std::string & path)  override;\n\t\t\n\t\t/**\n\t\t* \\brief Getter for the mesh path where the dataset is located.\n\t\t*\n\t\t*/\n\t\tconst std::string&\t\t\t\t\t\t\t\tmeshPath(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the mesh path where the dataset is located.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\tmeshPath(std::string & path)  override;\n\n\t\t/**\n\t\t* \\brief Getter for the dataset type.\n\t\t*\n\t\t*/\n\t\tconst IParseData::Type&\t\t\t\t\t\t\tdatasetType(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the dataset type.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\tdatasetType(IParseData::Type dataType) override;\n\n\t\t/**\n\t\t* \\brief Getter for the camera infos.\n\t\t*\n\t\t*/\n\t\tconst std::vector <InputCamera::Ptr>\t\t\tcameras(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the camera infos.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\tcameras(std::vector<InputCamera::Ptr>& cams) override;\n\n\t\t/**\n\t\t* \\brief Getter for the image path.\n\t\t*\n\t\t*/\n\t\tconst std::string\t\t\t\t\t\t\t\timgPath(void) const override;\n\n\t\t/**\n\t\t* \\brief Setter for the image path.\n\t\t*\n\t\t*/\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\timgPath(std::string& imPath) override;\n\n\t\t/**\n\t\t* \\brief Function to parse the scene metadata file to read image data.\n\t\t*\n\t\t*/\n\t\tvirtual bool\t\t\t\t\t\t\t\t\tparseSceneMetadata(const std::string & scene_metadata_path);\n\t\n\tprotected:\n\t\t\n\t\t/**\n\t\t* \\brief Function to parse the camera calibration files to read camera properties (camera matrix etc.).\n\t\t*\n\t\t*/\n\t\tbool parseBundlerFile(const std::string & bundler_file_path);\n\n\t\t\n\t\t/**\n\t\t* \\brief Function to populate scene info from camera infos to appropriate location.\n\t\t*\n\t\t*/\n\t\tvoid populateFromCamInfos();\n\n\t\tstd::vector<InputCamera::Ptr>\t\t\t\t_camInfos;\n\t\tstd::string\t\t\t\t\t\t\t\t\t_basePathName;\n\t\tstd::string\t\t\t\t\t\t\t\t\t_meshPath;\n\t\tstd::vector<sibr::ImageListFile::Infos>\t\t_imgInfos;\n\t\tstd::string\t\t\t\t\t\t\t\t\t_imgPath = \"\";\n\t\tstd::vector<bool>\t\t\t\t\t\t\t_activeImages;\n\t\tint\t\t\t\t\t\t\t\t\t\t\t_numCameras;\n\t\tType\t\t\t\t\t\t\t\t\t\t_datasetType = Type::EMPTY;\n\t\t\n\t};\n\n\n\t///// INLINE DEFINITIONS /////\n\t\n\tinline const std::vector<sibr::ImageListFile::Infos>&\tParseData::imgInfos(void) const {\n\t\treturn _imgInfos;\n\t}\n\n\tinline void ParseData::imgInfos(std::vector<sibr::ImageListFile::Infos>& infos)\n\t{\n\t\t_imgInfos = infos;\n\t}\n\n\tinline const int ParseData::numCameras( void ) const {\t\t\n\t\treturn _numCameras;\t\t\n\t}\n\n\tinline void ParseData::numCameras(int numCams)\n\t{\n\t\t_numCameras = numCams;\n\t}\n\t\n\tinline const std::vector<bool>& ParseData::activeImages(void) const {\n\t\treturn _activeImages;\n\t}\n\n\tinline void ParseData::activeImages(std::vector<bool>& activeCams)\n\t{\n\t\t_activeImages = activeCams;\n\t}\n\n\tinline const std::string & ParseData::basePathName(void) const\n\t{\n\t\treturn _basePathName;\n\t}\n\n\tinline void ParseData::basePathName(std::string& path)\n\t{\n\t\t_basePathName = path;\n\t}\n\n\tinline const std::string & ParseData::meshPath(void) const\n\t{\n\t\treturn _meshPath;\n\t}\n\n\tinline void ParseData::meshPath(std::string& path)\n\t{\n\t\t_meshPath = path;\n\t}\n\n\tinline void\t\tParseData::datasetType(IParseData::Type dataType) {\n\t\t_datasetType = dataType;\n\t}\n\n\tinline const std::vector<InputCamera::Ptr> ParseData::cameras(void) const\n\t{\n\t\treturn _camInfos;\n\t}\n\n\tinline void ParseData::cameras(std::vector<InputCamera::Ptr>& cams)\n\t{\n\t\t_camInfos = cams;\n\t}\n\n\tinline const std::string ParseData::imgPath(void) const\n\t{\n\t\treturn _imgPath;\n\t}\n\n\tinline void ParseData::imgPath(std::string& imPath)\n\t{\n\t\t_imgPath = imPath;\n\t}\n\n\tinline const ParseData::Type & ParseData::datasetType(void) const\n\t{\n\t\treturn _datasetType;\n\t}\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/ProxyMesh.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"ProxyMesh.hpp\"\n\n\nnamespace sibr {\n\n\tvoid ProxyMesh::loadFromData(const IParseData::Ptr & data)\n\t{\n\t\t_proxy.reset(new Mesh());\n\t\t// GD HACK\n\t\tif (boost::filesystem::extension(data->meshPath()) == \".bin\") {\n\t\t\tif (!_proxy->loadSfM(data->meshPath(), data->basePathName())) {\n\t\t\t\tSIBR_WRG << \"proxy model not found at \" << data->meshPath() << std::endl;\n\t\t\t}\n\t\t}\n\t\telse if (!_proxy->load(data->meshPath(), data->basePathName()) && !_proxy->load(removeExtension(data->meshPath()) + \".ply\") && !_proxy->load(removeExtension(data->meshPath()) + \".obj\")) {\n\t\t\tif (!_proxy->loadSfM(data->meshPath(), data->basePathName())) {\n\t\t\t\tSIBR_WRG << \"proxy model not found at \" << data->meshPath() << std::endl;\n\t\t\t}\n\t\t}\n\t\tif (!_proxy->hasNormals()) {\n\t\t\t_proxy->generateNormals();\n\t\t}\n\t}\n\n\tvoid ProxyMesh::replaceProxy(Mesh::Ptr newProxy)\n\t{\n\t\t_proxy.reset(new Mesh());\n\t\t_proxy->vertices(newProxy->vertices());\n\t\t_proxy->normals(newProxy->normals());\n\t\t_proxy->colors(newProxy->colors());\n\t\t_proxy->triangles(newProxy->triangles());\n\t\t_proxy->texCoords(newProxy->texCoords());\n\n\t\t// Used by inputImageRT init() and debug rendering\n\t\tif (!_proxy->hasNormals())\n\t\t{\n\t\t\t_proxy->generateNormals();\n\t\t}\n\n\t}\n\n\tvoid ProxyMesh::replaceProxyPtr(Mesh::Ptr newProxy)\n\t{\n\t\t_proxy = newProxy;\n\t}\n\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/ProxyMesh.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/scene/IProxyMesh.hpp\"\n\nnamespace sibr {\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT ProxyMesh : public IProxyMesh {\n\t\tSIBR_DISALLOW_COPY(ProxyMesh);\n\tpublic:\n\t\ttypedef std::shared_ptr<ProxyMesh>\t\t\t\t\tPtr;\n\n\t\tProxyMesh() {};\n\t\t~ProxyMesh() {};\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\tloadFromData(const IParseData::Ptr & data) override;\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\treplaceProxy(Mesh::Ptr newProxy) override;\n\t\tvoid\t\t\t\t\t\t\t\t\t\t\t\treplaceProxyPtr(Mesh::Ptr newProxy) override;\n\t\tbool\t\t\t\t\t\t\t\t\t\t\t\thasProxy(void) const;\n\t\tconst Mesh&\t\t\t\t\t\t\t\t\t\t\tproxy(void) const;\n\t\tconst Mesh::Ptr\t\t\t\t\t\t\t\t\t\tproxyPtr(void) const;\n\n\tprotected:\n\n\t\tMesh::Ptr\t\t\t\t\t\t\t\t\t\t\t_proxy;\n\n\t};\n\n\tinline bool\t\t\t\t\t\t\t\t\t\t\t\tsibr::ProxyMesh::hasProxy(void) const\n\t{\n\t\treturn _proxy && !_proxy->vertices().empty();\n\t}\n\n\tinline const Mesh& ProxyMesh::proxy(void) const\n\t{\n\t\treturn *_proxy;\n\t}\n\n\tinline const Mesh::Ptr\t\t\t\t\t\t\t\t\tProxyMesh::proxyPtr(void) const\n\t{\n\t\treturn _proxy;\n\t}\n\n};\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/RenderTargetTextures.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"RenderTargetTextures.hpp\"\n\nnamespace sibr {\n\n\tvoid RTTextureSize::initSize(uint w, uint h, bool force_aspect_ratio)\n\t{\n\t\t\n\t\tstd::cerr << \"RTTextureSize::initSize NEW FORCE ASPECT \" << force_aspect_ratio << \" : \" << w << \"x\" << h << \" \" << std::endl;\n\n\t\tfloat aspect;\n\t\tif (_width == 0) { // use full resolution\n\t\t\t_width = w;\n\t\t\t_height = h;\n\t\t} else if (!force_aspect_ratio) { // use constrained resolution\n\t\t\t\n\t\t\tif (w >= h) {\n\t\t\t\taspect = float(w) / float(h);\n\t\t\t\t_height = uint(floor(float(_width) / aspect));\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_height = _width;\n\t\t\t\taspect = float(w) / float(h);\n\t\t\t\t_width = uint(floor(float(_height) * aspect));\n\t\t\t}\n\t\t\t\n\t\t}\n\t\telse {\n\t\t\tif (w >= h) _height = w, _width = h;\n\t\t\telse _width = w, _height = h;\n\t\t}\n\n\t\tSIBR_LOG << \"Rendering resolution: (\" << _width << \",\" << _height << \")\" << std::endl;\n\t\t_isInit = true;\n\t}\n\n\tbool RTTextureSize::isInit() const\n\t{\n\t\treturn _isInit;\n\t}\n\n\tconst std::vector<RenderTargetRGBA32F::Ptr>& RGBDInputTextures::inputImagesRT() const\n\t{\n\t\treturn _inputRGBARenderTextures;\n\t}\n\n\tvoid RGBDInputTextures::initializeImageRenderTargets(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs)\n\t{\n\t\tSIBR_LOG << \"Initializing input image RTs \" << std::endl;\n\n\t\tif (!isInit()) {\n\t\t\tinitSize(cams->inputCameras()[_initActiveCam]->w(), cams->inputCameras()[_initActiveCam]->h());\n\t\t}\n\t\t\n\t\t_inputRGBARenderTextures.resize(imgs->inputImages().size());\n\n\t\tGLShader textureShader;\n\t\ttextureShader.init(\"Texture\",\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"texture.vp\")),\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"texture.fp\")));\n\t\tuint interpFlag = (SIBR_SCENE_LINEAR_SAMPLING & SIBR_SCENE_LINEAR_SAMPLING) ? SIBR_GPU_LINEAR_SAMPLING : 0; // LINEAR_SAMPLING Set to default\n\n\t\tfor (uint i = 0; i < imgs->inputImages().size(); i++) {\n\t\t\tif (cams->inputCameras()[i]->isActive()) {\n\t\t\t\tstd::cerr << \".\" ;\n\t\t\t\tImageRGB img = std::move(imgs->inputImages()[i]->clone());\n\t\t\t\timg.flipH();\n\n\t\t\t\tstd::shared_ptr<Texture2DRGB> rawInputImage(new Texture2DRGB(img, interpFlag));\n\n\t\t\t\tglViewport(0, 0, _width, _height);\n\t\t\t\t_inputRGBARenderTextures[i].reset(new RenderTargetRGBA32F(_width, _height, interpFlag));\n\t\t\t\t_inputRGBARenderTextures[i]->clear();\n\t\t\t\t_inputRGBARenderTextures[i]->bind();\n\n\t\t\t\tglActiveTexture(GL_TEXTURE0);\n\t\t\t\tglBindTexture(GL_TEXTURE_2D, rawInputImage->handle());\n\n\t\t\t\tglDisable(GL_DEPTH_TEST);\n\t\t\t\ttextureShader.begin();\n\t\t\t\tRenderUtility::renderScreenQuad();\n\t\t\t\ttextureShader.end();\n\t\t\t\t_inputRGBARenderTextures[i]->unbind();\n\t\t\t}\n\t\t}\n\t\tstd::cerr << std::endl; \n\t}\n\n\tvoid RGBDInputTextures::initializeDepthRenderTargets(ICalibratedCameras::Ptr cams, IProxyMesh::Ptr proxies, bool facecull)\n\t{\n\t\tif (!isInit()) {\n\t\t\tinitSize(cams->inputCameras()[_initActiveCam]->w(), cams->inputCameras()[_initActiveCam]->h());\n\t\t}\n\n\t\tGLParameter size;\n\t\tGLParameter proj;\n\n\t\tGLShader depthShader;\n\t\tdepthShader.init(\"Depth\",\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"depth.vp\")),\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"depth.fp\")));\n\n\t\tproj.init(depthShader, \"proj\"); // [SP]: ??\n\t\tsize.init(depthShader, \"size\"); // [SP]: ??\n\t\tfor (uint i = 0; i < cams->inputCameras().size(); i++) {\n\t\t\tif (cams->inputCameras()[i]->isActive()) {\n\t\t\t\t_inputRGBARenderTextures[i]->bind();\n\t\t\t\tglEnable(GL_DEPTH_TEST);\n\t\t\t\tglClear(GL_DEPTH_BUFFER_BIT);\n\t\t\t\tglDepthMask(GL_TRUE);\n\t\t\t\tglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);\n\n\t\t\t\tif (!proxies->proxy().triangles().empty())\n\t\t\t\t{\n\n\t\t\t\t\tconst uint w = _inputRGBARenderTextures[i]->w();\n\t\t\t\t\tconst uint h = _inputRGBARenderTextures[i]->h();\n\n\t\t\t\t\tdepthShader.begin();\n\t\t\t\t\tsize.set((float)w, (float)h);\n\t\t\t\t\tproj.set(cams->inputCameras()[i]->viewproj());\n\t\t\t\t\tproxies->proxy().render(true, facecull);\n\n\t\t\t\t\tdepthShader.end();\n\t\t\t\t}\n\t\t\t\t_inputRGBARenderTextures[i]->unbind();\n\t\t\t}\n\t\t}\n\t\tglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);\n\t}\n\n\tvoid DepthInputTextureArray::initDepthTextureArrays(ICalibratedCameras::Ptr cams, IProxyMesh::Ptr proxies, bool facecull, int flags)\n\t{\n\n\t\tif (!isInit()) {\n\t\t\tinitSize(cams->inputCameras()[_initActiveCam]->w(), cams->inputCameras()[_initActiveCam]->h());\n\t\t}\n\n\t\tif (!proxies->hasProxy()) {\n\t\t\tSIBR_WRG << \" Cannot init DepthTextureArrays without proxy.\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tSIBR_LOG << \"Depth vertex shader location: \" << Resources::Instance()->getResourceFilePathName(\"depthonly.vp\") << std::endl;\n\t\tSIBR_LOG << \"Depth fragment shader location: \" << Resources::Instance()->getResourceFilePathName(\"depthonly.fp\") << std::endl;\n\n\t\tGLShader depthOnlyShader;\n\t\tdepthOnlyShader.init(\"DepthOnly\",\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"depthonly.vp\")),\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"depthonly.fp\")));\n\n\t\tconst uint interpFlag = (flags & SIBR_SCENE_LINEAR_SAMPLING) ? SIBR_GPU_LINEAR_SAMPLING : 0;\n\n\t\tRenderTargetLum32F depthRT(_width, _height, interpFlag);\n\n\t\tGLParameter proj;\n\t\tproj.init(depthOnlyShader, \"proj\");\n\n\n\t\tconst uint numCams = (uint)cams->inputCameras().size();\n\t\t_inputDepthMapArrayPtr.reset(new Texture2DArrayLum32F(_width, _height, numCams, flags));\n\n\t\tfor (uint i = 0; i < numCams; i++) {\n\t\t\tglViewport(0, 0, _width, _height);\n\n\t\t\tdepthRT.bind();\n\t\t\tglEnable(GL_DEPTH_TEST);\n\t\t\tglClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n\t\t\tglDepthMask(GL_TRUE);\n\n\t\t\tdepthOnlyShader.begin();\n\t\t\tproj.set(cams->inputCameras()[i]->viewproj());\n\t\t\tproxies->proxy().render(true, facecull);\n\t\t\tdepthOnlyShader.end();\n\n\t\t\tdepthRT.unbind();\n\n\t\t\tglCopyImageSubData(\n\t\t\t\tdepthRT.handle(), GL_TEXTURE_2D, 0, 0, 0, 0,\n\t\t\t\t_inputDepthMapArrayPtr->handle(), GL_TEXTURE_2D_ARRAY, 0, 0, 0, i,\n\t\t\t\t_width, _height, 1);\n\t\t\tCHECK_GL_ERROR;\n\t\t}\n\t\tCHECK_GL_ERROR;\n\t}\n\n\tconst Texture2DArrayLum32F::Ptr & DepthInputTextureArray::getInputDepthMapArrayPtr() const\n\t{\n\t\treturn _inputDepthMapArrayPtr;\n\t}\n\n\tvoid RGBInputTextureArray::initRGBTextureArrays(IInputImages::Ptr imgs, int flags, bool force_aspect_ratio)\n\t{\n\t\tif (!isInit()) {\n\t\t\tstd::cerr << \"RGBInputTextureArray::initRGBTextureArrays NEW FORCE ASPECT \" << force_aspect_ratio << std::endl;\n\t\t\tinitSize(imgs->inputImages()[_initActiveCam]->w(), imgs->inputImages()[_initActiveCam]->h(), force_aspect_ratio);\n\t\t}\n\n\t\t_inputRGBArrayPtr.reset(new Texture2DArrayRGB(imgs->inputImages(), _width, _height, flags));\n\t}\n\n\tconst Texture2DArrayRGB::Ptr & RGBInputTextureArray::getInputRGBTextureArrayPtr() const\n\t{\n\t\treturn _inputRGBArrayPtr;\n\t}\n\n\tvoid RenderTargetTextures::initializeDefaultRenderTargets(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies)\n\t{\n\t\tif (!isInit()) {\n\t\t\tinitRenderTargetRes(cams);\n\t\t\t\n\t\t}\n\t\tinitializeImageRenderTargets(cams, imgs);\n\t\tinitializeDepthRenderTargets(cams, proxies, true);\n\t}\n\n\tvoid RenderTargetTextures::initRenderTargetRes(ICalibratedCameras::Ptr cams)\n\t{\n\t\t// Find the first active camera and use it's reolution to init Rendertargets\n\t\tfor (int i = 0; i < cams->inputCameras().size(); i++) {\n\t\t\tif (cams->inputCameras()[i]->isActive()) {\n\t\t\t\t_initActiveCam = i;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tSIBR_ERR << \"No cameras active! Fail to initialize RenderTarget!!\" << std::endl;\n\t}\n\n\tvoid RenderTargetTextures::initRGBandDepthTextureArrays(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies, int textureFlags, int texture_width, bool faceCull, bool force_aspect_ratio)\n\t{\n\t\t_width = texture_width;\n\t\tinitRGBandDepthTextureArrays(cams, imgs, proxies, textureFlags, faceCull, force_aspect_ratio);\n\t}\n\n\tvoid RenderTargetTextures::initRGBandDepthTextureArrays(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies, int textureFlags,  unsigned int width, unsigned int height, bool faceCull)\n\t{\n\t\tinitSize(width, height, true);\n\t\tinitRGBTextureArrays(imgs, textureFlags, true);\n\t\tinitDepthTextureArrays(cams, proxies, faceCull);\n\t}\n\n\tvoid RenderTargetTextures::initRGBandDepthTextureArrays(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies, int textureFlags, bool faceCull, bool force_aspect_ratio)\n\t{\n\t\tif (!isInit()) {\n\t\t\tinitRenderTargetRes(cams);\n\t\t}\n\t\tinitRGBTextureArrays(imgs, textureFlags, force_aspect_ratio);\n\t\tinitDepthTextureArrays(cams, proxies, faceCull);\n\t}\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/RenderTargetTextures.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/graphics/Texture.hpp\"\n#include \"core/scene/ICalibratedCameras.hpp\"\n#include \"core/scene/IInputImages.hpp\"\n#include \"core/scene/IProxyMesh.hpp\"\n#include \"core/assets/Resources.hpp\"\n# include \"core/graphics/Shader.hpp\"\n#include \"core/graphics/Utils.hpp\"\n#include \"core/scene/Config.hpp\"\n\n\n# define SIBR_SCENE_LINEAR_SAMPLING\t\t\t4\n\n\nnamespace sibr{\n\n\t/** \n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT RTTextureSize {\n\n\tpublic:\n\t\tRTTextureSize(uint w = 0) : _width(w) {}\n\n\t\tvoid initSize(uint w, uint h, bool force_aspect_ratio = false);\n\n\t\tbool isInit() const;\n\n\tprotected:\n\t\tuint\t\t_width = 0; //constrained width provided by the command line args, defaults to 0\n\t\tuint\t\t_height = 0; //associated height, computed in initSize\n\t\tbool\t\t_isInit = false;\n\t\tint\t\t\t_initActiveCam = 0;\n\n\t};\n\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT RGBDInputTextures : public virtual RTTextureSize {\n\t\tSIBR_CLASS_PTR(RGBDInputTextures)\n\tpublic:\n\t\tconst std::vector<RenderTargetRGBA32F::Ptr> & inputImagesRT() const;\n\n\t\tvirtual void initializeImageRenderTargets(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs);\n\t\tvirtual void initializeDepthRenderTargets(ICalibratedCameras::Ptr cams, IProxyMesh::Ptr proxies, bool facecull);\n\n\tprotected:\n\t\tstd::vector<RenderTargetRGBA32F::Ptr> _inputRGBARenderTextures;\n\n\t};\n\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT DepthInputTextureArray : public virtual RTTextureSize {\n\t\tSIBR_CLASS_PTR(DepthInputTextureArray)\n\tpublic:\n\t\tvirtual void initDepthTextureArrays(ICalibratedCameras::Ptr cams, IProxyMesh::Ptr proxies, bool facecull, int flags = SIBR_GPU_LINEAR_SAMPLING);\n\t\tconst Texture2DArrayLum32F::Ptr &  getInputDepthMapArrayPtr() const;\n\n\tprotected:\n\t\tTexture2DArrayLum32F::Ptr _inputDepthMapArrayPtr;\n\n\t};\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT RGBInputTextureArray : public virtual RTTextureSize {\n\n\t\tSIBR_CLASS_PTR(RGBInputTextureArray)\n\n\tpublic:\n\t\tvirtual void initRGBTextureArrays(IInputImages::Ptr imgs, int flags = 0, bool force_aspect_ratio=false);\n\t\tconst Texture2DArrayRGB::Ptr & getInputRGBTextureArrayPtr() const;\n\n\tprotected:\n\t\tTexture2DArrayRGB::Ptr _inputRGBArrayPtr;\n\n\t};\n\n\t/**\n\t\\ingroup sibr_scene\n\t*/\n\tclass SIBR_SCENE_EXPORT RenderTargetTextures :\n\t\tpublic virtual RGBDInputTextures,\n\t\tpublic virtual DepthInputTextureArray,\n\t\tpublic virtual RGBInputTextureArray \n\t{\n\t\t\n\tpublic:\n\t\tSIBR_CLASS_PTR(RenderTargetTextures)\n\t\t\n\t\tRenderTargetTextures(uint w = 0) : RTTextureSize(w) {}\n\n\t\tvirtual void initRGBandDepthTextureArrays(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies, int textureFlags, unsigned int w, unsigned int h, bool faceCull = true);\n\t\t// TODO: remove this, not needed\n\t\tvirtual void initRGBandDepthTextureArrays(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies, int textureFlags, int texture_width, bool faceCull = true, bool force_aspect_ratio = false);\n\t\tvirtual void initRGBandDepthTextureArrays(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies, int textureFlags, bool faceCull = true, bool force_aspect_ratio=false);\n\t\tvirtual void initializeDefaultRenderTargets(ICalibratedCameras::Ptr cams, IInputImages::Ptr imgs, IProxyMesh::Ptr proxies);\n\n\tprotected:\n\t\tvoid initRenderTargetRes(ICalibratedCameras::Ptr cams);\n\n\t};\n\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/scene/sibr_scene.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_scene sibr_scene\n\n\t\\brief IBR Scene components.\n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Array2d.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <vector>\n#include <core/system/Vector.hpp>\n\nnamespace sibr\n{\n\ttemplate <typename T> class Array2d;\n\n\n\t///\n\t/// Used to store custom data on a map using pixel position \n\t/// (2d unsigned pos).\n\t/// Internally, this simply use a one dimensional std::vector. \n\t/// This class abstract convert operation from 2d to 1d for \n\t/// making code easy to read.\n\t/// \\ingroup sibr_system\n\t///\n\ttemplate <typename T>\n\tclass Array2d\n\t{\n\tpublic:\n\t\t// We use 'reference' defined by std::vector (and STL) and they are\n\t\t// a bit special. For example, I suppose that some compiler should\n\t\t// be able to replace a heave 'const bool&' by a simple 'bool' (in\n\t\t// this case a copy is far more cheaper). And what about 'bool&' ?\n\t\ttypedef typename std::vector<T>::const_reference\tconst_reference;\n\t\ttypedef typename std::vector<T>::reference\t\t\treference;\n\n\t\t/// Build from the given size.\n\t\t/// Note you can't resize your Array2d (just create a new one \n\t\t/// if you need).\n\t\tArray2d( uint width=0, uint height=0 );\n\t\t/// Build from the given size and using the given default value.\n\t\t/// Note you can't resize your Array2d (just create a new one \n\t\t/// if you need).\n\t\tArray2d( uint width, uint height, const_reference defaultValue );\n\n\t\t/// Destructor.\n\t\t~Array2d( );\n\n\t\t/// Return the width of this Array2d\n\t\tuint\twidth( void ) const;\n\t\t/// Return the height of this Array2d\n\t\tuint\theight( void ) const;\n\t\t/// Return the width of this Array2d\n\t\tuint\tw(void) const;\n\t\t/// Return the height of this Array2d\n\t\tuint\th(void) const;\n\n\t\t/// Return TRUE if is empty\n\t\tbool\tempty( void ) const;\n\n\t\t/// Return data about a pixel at given coordinates\n\t\tconst_reference\t\toperator ()( uint x, uint y ) const;\n\t\t/// Access data about a pixel at given coordinates\n\t\treference\t\t\toperator ()( uint x, uint y );\n\n\t\t/// Return data about a pixel at given coordinates\n\t\tconst_reference\t\toperator ()(const sibr::Vector2i & coords) const;\n\t\t/// Access data about a pixel at given coordinates\n\t\treference\t\t\toperator ()(const sibr::Vector2i & coords);\n\n\t\t/// Return data about a pixel at given index\n\t\tconst_reference\t\toperator []( size_t i ) const;\n\t\t/// Access data about a pixel at given index\n\t\treference\t\t\toperator []( size_t i );\n\n\t\t/// Return the total size of the one dimensional array\n\t\tsize_t\t\t\t\tsize( void ) const;\n\n\t\t/// Return data accessible in a one array form\n\t\t/// \\deprecated Use Array2d<T>::vector( void ) instead.\n\t\tconst std::vector<T>&\toperator () ( void ) const;\n\t\tstd::vector<T>&\t\t\toperator () ( void );\n\n\t\t/// Return the internally used std::vector (so you\n\t\t/// can use STL algos).\n\t\tconst std::vector<T>&\tvector( void ) const;\n\t\tstd::vector<T>&\t\t\tvector( void );\n\n\t\t/// Return a pointer to the first byte a stored\n\t\t/// data.\n\t\tvoid*\t\t\t\t\tdata( void ) const;\n\t\tvoid*\t\t\t\t\tdata( void );\n\n\n\n\t\t/// Return the element index for the given coordinates\n\t\tinline uint\t\tindex( uint x, uint y ) const;\n\n\t\t/// Return FALSE if x,y are out of range. (DON'T print error)\n\t\tinline bool\t\tinRange( uint x, uint y ) const;\n\t\tinline bool\t\tisInRange( uint x, uint y) const;\n\n\n\tprotected:\n\t\t/// Return FALSE if x,y are out of range. (print error)\n\t\tbool\tcheckSizeFor( uint x, uint y ) const;\n\n\t\tuint\t\t\t\t_width;\t\t///< Width of the pixel map\n\t\tuint\t\t\t\t_height;\t///< Height of the pixel map\n\t\tstd::vector<T>\t\t_data;\t\t///< data of the pixel map\n\t};\n\t\n\n\t///// DEFINITION /////\n\t\n\ttemplate<typename T>\n\tArray2d<T>::Array2d( uint width, uint height )\n\t: _width(width), _height(height), _data(_width*_height) {\n\t}\n\n\ttemplate<typename T>\n\tArray2d<T>::Array2d( uint width, uint height, const_reference defaultValue )\n\t: _width(width), _height(height), _data(_width*_height, defaultValue) {\n\t}\n\n\ttemplate<typename T>\n\tArray2d<T>::~Array2d( )\n\t{\n\t\t_data.clear();\n\t}\n\n\ttemplate<typename T>\n\tuint\tArray2d<T>::width( void ) const {\n\t\treturn _width;\n\t}\n\n\ttemplate<typename T>\n\tuint\tArray2d<T>::height( void ) const {\n\t\treturn _height;\n\t}\n\n\ttemplate<typename T>\n\tuint\tArray2d<T>::w(void) const {\n\t\treturn _width;\n\t}\n\n\ttemplate<typename T>\n\tuint\tArray2d<T>::h(void) const {\n\t\treturn _height;\n\t}\n\n\n\ttemplate<typename T>\n\ttypename Array2d<T>::const_reference\t\tArray2d<T>::operator ()( uint x, uint y ) const {\n\t\tcheckSizeFor(x, y);\n\t\treturn _data.at(index(x, y));\n\t}\n\ttemplate<typename T>\n\ttypename Array2d<T>::reference\t\t\tArray2d<T>::operator ()( uint x, uint y) {\n\t\tcheckSizeFor(x, y);\n\t\treturn _data[index(x, y)];\n\t}\n\n\ttemplate<typename T>\n\ttypename Array2d<T>::const_reference\t\tArray2d<T>::operator ()(const sibr::Vector2i & coords) const {\n\t\treturn _data[index(coords[0], coords[1])];\n\t}\n\ttemplate<typename T>\n\ttypename Array2d<T>::reference\t\t\tArray2d<T>::operator ()(const sibr::Vector2i & coords) {\n\t\treturn _data[index(coords[0], coords[1])];\n\t}\n\n\ttemplate<typename T>\n\tconst std::vector<T>&\tArray2d<T>::operator () ( void ) const {\n\t\treturn _data;\n\t}\n\ttemplate<typename T>\n\tstd::vector<T>&\tArray2d<T>::operator () ( void ) {\n\t\treturn _data;\n\t}\n\n\n\ttemplate<typename T>\n\tconst std::vector<T>&\tArray2d<T>::vector( void ) const {\n\t\treturn _data;\n\t}\n\ttemplate<typename T>\n\tstd::vector<T>&\tArray2d<T>::vector( void ) {\n\t\treturn _data;\n\t}\n\t\n\ttemplate<typename T>\n\tvoid*\t\t\t\t\tArray2d<T>::data( void ) const {\n\t\treturn vector().empty()? nullptr : &vector()[0];\n\t}\n\ttemplate<typename T>\n\tvoid*\t\t\t\t\tArray2d<T>::data( void ) {\n\t\treturn vector().empty()? nullptr : &vector()[0];\n\t}\n\n\ttemplate<typename T>\n\tbool\tArray2d<T>::empty( void ) const {\n\t\treturn vector().empty();\n\t}\n\n\t\n\ttemplate<typename T>\n\ttypename Array2d<T>::const_reference\t\tArray2d<T>::operator []( size_t i ) const {\n\t\treturn _data.at(i);\n\t}\n\n\ttemplate<typename T>\n\ttypename Array2d<T>::reference\t\t\tArray2d<T>::operator []( size_t i ) {\n\t\treturn _data[i];\n\t}\n\n\ttemplate<typename T>\n\tsize_t\t\t\t\tArray2d<T>::size( void ) const {\n\t\treturn _data.size();\n\t}\n\n\n\ttemplate<typename T>\n\tuint\t\tArray2d<T>::index( uint x, uint y ) const {\n\t\treturn y*_width + x;\n\t}\n\n\ttemplate<typename T>\n\tbool\tArray2d<T>::inRange( uint x, uint y ) const {\n\t\treturn (x < _width && y < _height);\n\t}\n\n\ttemplate<typename T>\n\tbool\tArray2d<T>::isInRange(uint x, uint y) const {\n\t\treturn (x < _width && y < _height);\n\t}\n\n\ttemplate<typename T>\n\tbool\tArray2d<T>::checkSizeFor( uint x, uint y ) const {\n\t\tif (inRange(x, y))\n\t\t\treturn true;\n\t\t//else\n\t\tSIBR_ERR << \"invalid pixelmap range at \" << x << \", \" << y\n\t\t\t<< \"(current size: \" << _width << \", \" << _height << \"). \"\n\t\t\t<< std::endl;\n\n\t\tassert(false);// else it will crash because of std::vector\n\t\treturn false;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/ByteStream.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <vector>\n#include <iomanip>\n#include <fstream>\n\n#ifndef SIBR_OS_WINDOWS\n# include <string.h>\n#endif\n\n#include \"core/system/ByteStream.hpp\"\n\n\nnamespace sibr\n{\n\tvoid\t\t\tByteStream::memoryDump( void ) const \n\t{\n\t\tconst unsigned char* data = reinterpret_cast<const unsigned char*>(&_buffer[0]);\n\t\tstd::cout << \"Readable size: \" << readableSize() << std::endl;\n\t\tstd::cout << \"Real size: \" << bufferSize() << std::endl;\n\t\tstd::cout << std::hex << std::setfill('0') << std::setw(2);\n\t\tfor (unsigned i = 0; i < _buffer.size(); ++i)\n\t\t{\n\t\t\tconst int blocksize = 2;\n\t\t\tfor (unsigned j = 0; i < _buffer.size() && j < blocksize; ++j, ++i)\n\t\t\t\tstd::cout << uint(data[i]);\n\t\t\tstd::cout << ' ';\n\t\t\tfor (unsigned j = 0; i < _buffer.size() && j < blocksize; ++j, ++i)\n\t\t\t\tstd::cout << uint(data[i]);\n\t\t\tstd::cout << ' ';\n\t\t\tfor (unsigned j = 0; i < _buffer.size() && j < blocksize; ++j, ++i)\n\t\t\t\tstd::cout << uint(data[i]);\n\t\t\tstd::cout << ' ';\n\t\t\tfor (unsigned j = 0; i < _buffer.size() && j < blocksize; ++j, ++i)\n\t\t\t\tstd::cout << uint(data[i]);\n\t\t\tstd::cout << ' ';\n\t\t\tstd::cout << std::endl;\n\t\t}\n\t\tstd::cout << std::dec << std::setw(0);\n\t}\n\n\tuint64\tByteStream::htonll(uint64 n)\n\t{\n\t\tif (ByteStream::systemIsBigEndian())\n\t\t\treturn n;\n\t\t// Else we are on a little endian system\n\t\tuint32 out = 0;\n\t\tout |= (n & 0xFF00000000000000) >> 56;\n\t\tout |= (n & 0x00FF000000000000) >> 40;\n\t\tout |= (n & 0x0000FF0000000000) >> 24;\n\t\tout |= (n & 0x000000FF00000000) >> 8;\n\t\tout |= (n & 0x00000000FF000000) << 8;\n\t\tout |= (n & 0x0000000000FF0000) << 24;\n\t\tout |= (n & 0x000000000000FF00) << 40;\n\t\tout |= (n & 0x00000000000000FF) << 56;\n\t\treturn out;\n\t}\n\n\tuint32\tByteStream::htonl( uint32 n ) \n\t{\n\t\tif (ByteStream::systemIsBigEndian())\n\t\t\treturn n;\n\t\t// Else we are on a little endian system\n\t\tuint32 out = 0;\n\n\t\tout |= (n & 0xFF000000) >> 24;\n\t\tout |= (n & 0x00FF0000) >> 8;\n\t\tout |= (n & 0x0000FF00) << 8;\n\t\tout |= (n & 0x000000FF) << 24;\n\t\treturn out;\n\t}\n\n\tuint16\tByteStream::htons( uint16 n ) \n\t{\n\t\tif (ByteStream::systemIsBigEndian())\n\t\t\treturn n;\n\t\t// Else we are on a little endian system\n\t\tuint16 out = 0;\n\n\t\tout |= (n & 0xFF00) >> 8;\n\t\tout |= (n & 0x00FF) << 8;\n\t\treturn out;\n\t}\n\n\tbool ByteStream::systemIsBigEndian( void ) \n\t{\n\t\tstatic int16 isBigEndian = -1;\n\t\tif (isBigEndian == -1)\n\t\t{\n\t\t\t/// \\todo TODO: check the compiler is not too smart and that it does not\n\t\t\t/// optimize this at compile-time with a const.\n\t\t\tint16 word = 0x0001;\n\t\t\tchar *b = (char *) &word;\n\t\t\tisBigEndian = (b[0]? 0 /*little endian*/ : 1 /*big endian*/);\n\t\t}\n\t\treturn isBigEndian != 0;\n\t}\n\n\tbool\tByteStream::load( const std::string& filename ) \n\t{\n\t\tstd::ifstream file(filename.c_str(), std::ios::in | std::ios::binary);\n\n\t\tif (file)\n\t\t{\n\t\t\tfile.seekg(0, file.end);\n\t\t\tauto len = file.tellg();\n\t\t\tfile.seekg(0, file.beg);\n\n\t\t\t_buffer.resize(len);\n\t\t\tfile.read(reinterpret_cast<char*>(&_buffer[0]), len);\n\n\t\t\tfile.close();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\tSIBR_WRG << \"cannot load ByteStream from file '\" << filename << \"'.\" << std::endl;\n\t\treturn false;\n\t}\n\n\tvoid\tByteStream::saveToFile( const std::string& filename ) \n\t{\n\t\tif (bufferSize() == 0)\n\t\t\treturn;\n\n\t\tstd::ofstream\tfile(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);\n\n\t\tif (file)\n\t\t{\n\t\t\tfile.write((char*)&_buffer[0], _buffer.size() * sizeof(_buffer[0]));\n\t\t\tfile.close();\n\t\t}\n\t\telse\n\t\t\tSIBR_LOG << \"ERROR: cannot write to the file '\" << filename << \"'\" << std::endl;\n\t}\n\n\tvoid ByteStream::push(const void* data, uint size) \n\t{\n\t\tassert(data != nullptr && size > 0);\n\n\t\tsize_t curpos = _buffer.size();\n\t\t_buffer.resize(curpos + size);\n\t\tmemcpy(&_buffer[curpos], data, size);\n\t}\n\n\n\tByteStream& ByteStream::operator <<( bool b ) \n\t{\n\t\treturn ByteStream::operator << (static_cast<uint8>(b));\n\t}\n\n\tByteStream& ByteStream::operator <<( int8 i ) \n\t{\n\t\tpush(&i, sizeof(i));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( int16 i ) \n\t{\n\t\tint16 netorder = htons(i);\n\t\tpush(&netorder, sizeof(netorder));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( int32 i ) \n\t{\n\t\tint32 netorder = htonl(i);\n\t\tpush(&netorder, sizeof(netorder));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( int64 i )\n\t{\n\t\tint64 netorder = htonll(i);\n\t\tpush(&netorder, sizeof(netorder));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( uint8 i ) \n\t{\n\t\tpush(&i, sizeof(i));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( uint16 i ) \n\t{\n\t\tuint16 netorder = htons(i);\n\t\tpush(&netorder, sizeof(netorder));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( uint32 i ) \n\t{\n\t\tuint32 netorder = htonl(i);\n\t\tpush(&netorder, sizeof(netorder));\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<(uint64 i)\n\t{\n\t\tuint64 netorder = htonll(i);\n\t\tpush(&netorder, sizeof(netorder));\n\t\treturn *this;\n\t}\n\t\n\tByteStream& ByteStream::operator <<( const std::string& str )\n\t{\n\t\tuint32 size = static_cast<uint32_t>(str.size());\n\t\toperator << (size);\n\t\tpush(str.data(), sizeof(char)*size);\n\t\treturn *this;\n\t}\n\n\tByteStream& ByteStream::operator <<( float f )\n\t{\n\t\tint32* p = (int32*)&f;\n\t\treturn ByteStream:: operator<<( *p );\n\t}\n\n\tByteStream& ByteStream::operator <<(double d)\n\t{\n\t\tint64* p = (int64*)&d;\n\t\treturn ByteStream:: operator<<(*p);\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/ByteStream.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <vector>\r\n# include <iomanip>\r\n# include \"core/system/Config.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\t/// Be sure to use STL objects from client's dll version by exporting this declaration (see warning C4251)\r\n\t//template class SIBR_EXPORT std::vector<uint8>;\r\n\r\n\t/**\r\n\t Used to manipulate stream of bytes.\r\n\t \\note This ByteStream stores integer using the network byte order (which is big endian).\r\n\t \\ingroup sibr_system\r\n\t*/\r\n\tclass SIBR_SYSTEM_EXPORT ByteStream\r\n\t{\r\n\tpublic:\r\n\r\n\t\t/** Stream endianness, always default to BigEndian. */\r\n\t\tenum Endianness\r\n\t\t{\r\n\t\t\tBigEndian = 0, // always default\r\n\t\t\tLittleEndian\r\n\t\t};\r\n\r\n\tpublic:\r\n\t\ttypedef std::vector<uint8>\tbytes;\t///< type used for storing bytes\r\n\r\n\t\t/// Constructor\r\n\t\tByteStream( void ) : _readPos(0), _valid(true) /*,_endianness(BigEndian)*/ { }\r\n\r\n\t\t/** Load all bytes from a file using the given filename\r\n\t\t* \\param filename the filename\r\n\t\t* \\return success boolean\r\n\t\t* */ \r\n\t\tbool load( const std::string& filename );\r\n\r\n\t\t/** Save all bytes to a file using the given filename\r\n\t\t *\\param filename file apth\r\n\t\t **/\r\n\t\tvoid\tsaveToFile( const std::string& filename );\r\n\r\n\t\t/** Append data to the current buffer\r\n\t\t *\\param data pointer to the data\r\n\t\t *\\param size size in bytes\r\n\t\t **/\r\n\t\tvoid push(const void* data, uint size);\r\n\r\n\t\t/** \\return true if the stream is opened and valid. */\r\n\t\toperator bool( void ) const { return _valid; }\r\n\r\n\t\t/** Write a bool to the stream.\r\n\t\t *\\param b input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( bool b );\r\n\r\n\t\t/** Write an 8bits-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( int8 i );\r\n\r\n\t\t/** Write a 16bits-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( int16 i );\r\n\r\n\t\t/** Write a 32bits-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( int32 i );\r\n\r\n\t\t/** Write a 64bits-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( int64 i );\r\n\r\n\t\t/** Write an 8bits-unsigned-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( uint8 i );\r\n\r\n\t\t/** Write a 16bits-unsigned-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( uint16 i );\r\n\r\n\t\t/** Write a 32bits-unsigned-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( uint32 i );\r\n\r\n\t\t/** Write a 64bits-unsigned-integer to the stream.\r\n\t\t *\\param i input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( uint64 i );\r\n\r\n\t\t/** Write a string to the stream.\r\n\t\t *\\param str input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<( const std::string& str );\r\n\r\n\t\t/** Write a float to the stream.\r\n\t\t *\\param f input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t *\\note Use the same endianness as for integers (not specified in IEEE 754).\r\n\t\t **/\r\n\t\tByteStream& operator <<( float f );\r\n\r\n\t\t/** Write a double to the stream.\r\n\t\t *\\param d input value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tByteStream& operator <<(double d);\r\n\r\n\t\t/** Read a bool from the stream.\r\n\t\t *\\param b output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( bool & b );\r\n\r\n\t\t/** Read an 8bits-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( int8& i );\r\n\r\n\t\t/** Read a 16bits-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( int16& i );\r\n\r\n\t\t/** Read a 32bits-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( int32& i );\r\n\r\n\t\t/** Read a 64bits-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( int64& i );\r\n\r\n\t\t/** Read an 8bits-unsigned-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( uint8& i );\r\n\r\n\t\t/** Read a 16bits-unsigned-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( uint16& i );\r\n\r\n\t\t/** Read a 32bits-unsigned-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( uint32& i );\r\n\r\n\t\t/** Read a 64bits-unsigned-integer from the stream.\r\n\t\t *\\param i output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( uint64 & i );\r\n\r\n\t\t/** Read a string from the stream.\r\n\t\t *\\param str output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( std::string& str );\r\n\r\n\t\t/** Read a float from the stream.\r\n\t\t *\\param f output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t *\\note Use the same endianness as for integers (not specified in IEEE 754).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>( float& f );\r\n\r\n\t\t/** Read a double from the stream.\r\n\t\t *\\param d output value\r\n\t\t *\\return the stream (for chaining).\r\n\t\t **/\r\n\t\tinline ByteStream& operator >>(double& d);\r\n\r\n\t\t/** \\return the number of bytes that have not been read yet.*/\r\n\t\tinline size_t\treadableSize( void ) const;\r\n\t\t/** \\return the total number of bytes in the buffer used by the stream*/\r\n\t\tinline size_t\tbufferSize( void ) const;\r\n\t\t/** \\return a pointer to the buffer */\r\n\t\tinline const uint8*\tbuffer( void ) const { return &_buffer[0]; }\r\n\r\n\t\t// We don't want to include network-related libs (and all their stuffs), so we use a custom implementation of htonl/htons, ntohl/ntohs.\r\n\r\n\t\t/** Convert an uint32 from host to network byte order (which is big endian)\r\n\t\t *\\param n host order value\r\n\t\t *\\return network order value\r\n\t\t **/\r\n\t\tstatic uint64\thtonll(uint64 n);\r\n\r\n\t\t/** Convert an uint32 from host to network byte order (which is big endian)\r\n\t\t *\\param n host order value\r\n\t\t *\\return network order value\r\n\t\t **/\r\n\t\tstatic uint32\thtonl( uint32 n );\r\n\r\n\t\t/** Convert an uint16 from host to network byte order (which is big endian)\r\n\t\t *\\param n host order value\r\n\t\t *\\return network order value\r\n\t\t **/\r\n\t\tstatic uint16\thtons( uint16 n );\r\n\r\n\t\t/** Convert an uint32 from network to host byte order\r\n\t\t *\\param n network order value\r\n\t\t *\\return host order value\r\n\t\t **/\r\n\t\tinline static uint64\tntohll(uint64 n);\r\n\r\n\t\t/** Convert an uint32 from network to host byte order\r\n\t\t *\\param n network order value\r\n\t\t *\\return host order value\r\n\t\t **/\r\n\t\tinline static uint32\tntohl( uint32 n );\r\n\r\n\t\t/** Convert an uint16 from network to host byte order\r\n\t\t *\\param n network order value\r\n\t\t *\\return host order value\r\n\t\t **/\r\n\t\tinline static uint16\tntohs( uint16 n );\r\n\r\n\t\t/** \\return true if the current system runs using Big Endian **/\r\n\t\tstatic bool systemIsBigEndian( void );\r\n\r\n\t\t// (Not used for now: future features to change how are stored float)\r\n\t\t// void\t\tsetEndianness( Endianness e );\r\n\t\t// Endianness\tgetEndianness( void );\r\n\r\n\t\t/** Dump the buffer contents to stdout. (used for debugging purposes)\r\n\t\t **/\r\n\t\tvoid\t\t\tmemoryDump( void ) const;\r\n\r\n\tprivate:\r\n\t\t/** Test if we can read n bytes in the buffer.\r\n\t\t *\\param n the number of bytes to check\r\n\t\t *\\return false if it fails (and set valid flag to false).\r\n\t\t **/\r\n\t\tinline bool\t\ttestSize( uint n );\r\n\r\n\t\tbytes\t\t_buffer;\t///< the whole stream\r\n\t\tuint32\t\t_readPos;   ///< Current position in the buffer when reading.\r\n\t\tbool\t\t_valid;\t\t///< tells if no error occured when reading\r\n\t\t// Endianness\t_endianness;\r\n\r\n\t};\r\n\r\n\t\t///// INLINE FUNCTIONS /////\r\n\t\tsize_t\tByteStream::readableSize( void ) const {\r\n\t\t\treturn bufferSize() - _readPos;\r\n\t\t}\r\n\t\tsize_t\tByteStream::bufferSize( void ) const {\r\n\t\t\treturn _buffer.size();\r\n\t\t}\r\n\r\n\t\tuint64\tByteStream::ntohll(uint64 n) {\r\n\t\t\treturn htonll(n);\r\n\t\t}\r\n\t\tuint32\tByteStream::ntohl( uint32 n ) {\r\n\t\t\treturn htonl(n);\r\n\t\t}\r\n\t\tuint16\tByteStream::ntohs( uint16 n ) {\r\n\t\t\treturn htons(n);\r\n\t\t}\r\n\t\tbool\t\tByteStream::testSize( uint n ) {\r\n\t\t\treturn (_valid = (_valid && (readableSize() >= n)));\r\n\t\t}\r\n\r\n\t\tByteStream& ByteStream::operator >>( bool& b ) {\r\n\t\t\tuint8 i;\r\n\t\t\tByteStream::operator >>(i);\r\n\t\t\tb = (i != 0);\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( int8& i ) {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = *reinterpret_cast<int8*>(&_buffer[_readPos]);\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( int16& i ) {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = ntohs(*reinterpret_cast<int16*>(&_buffer[_readPos]));\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\t\tByteStream& ByteStream::operator >>( int32& i )  {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = ntohl(*reinterpret_cast<int32*>(&_buffer[_readPos]));\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>(int64& i) {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = ntohll(*reinterpret_cast<int64*>(&_buffer[_readPos]));\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\t\tByteStream& ByteStream::operator >>( uint8& i )  {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = *reinterpret_cast<uint8*>(&_buffer[_readPos]);\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( uint16& i ) {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = ntohs(*reinterpret_cast<uint16*>(&_buffer[_readPos]));\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( uint32& i ) {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = ntohl(*reinterpret_cast<uint32*>(&_buffer[_readPos]));\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>(uint64& i) {\r\n\t\t\tif (testSize(sizeof(i)))\r\n\t\t\t{\r\n\t\t\t\ti = ntohll(*reinterpret_cast<uint64*>(&_buffer[_readPos]));\r\n\t\t\t\t_readPos += sizeof(i);\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( std::string& str ) {\r\n\t\t\tuint32 size;\r\n\t\t\toperator >> (size);\r\n\t\t\tstr.resize(size);\r\n\r\n\t\t\tif (testSize(sizeof(char)*size))\r\n\t\t\t{\r\n\t\t\t\tstr.insert(str.begin(), \r\n\t\t\t\t\treinterpret_cast<char*>(&_buffer[_readPos]), \r\n\t\t\t\t\treinterpret_cast<char*>(&_buffer[_readPos + size]));\r\n\t\t\t\t_readPos += sizeof(char)*size;\r\n\t\t\t}\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( float& f ) {\r\n\t\t\tint32 p;\r\n\t\t\tByteStream::operator>> (p);\r\n\t\t\tchar* ptr = (char*)&p;\r\n\t\t\tfloat *fptr = (float*)ptr;\r\n\t\t\tif (_valid)\r\n\t\t\t\tf = *fptr;\r\n\t\t\treturn *this;\r\n\t\t}\r\n\t\tByteStream& ByteStream::operator >>( double& d ) {\r\n\t\t\tint64 p;\r\n\t\t\tByteStream::operator>> (p);\r\n\t\t\tchar* ptr = (char*)&p;\r\n\t\t\tdouble *fptr = (double*)ptr;\r\n\t\t\tif (_valid)\r\n\t\t\t\td = *fptr;\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\t\t// Function used to test this class (might be still useful to test future improvement)\r\n\t\t/*\r\n\t\tstatic void unitTestByteStream( void )\r\n\t\t{\r\n\t\t\tSIBR_LOG << \"[work in progress] - testing ByteStream\" << std::endl;\r\n\r\n\t\t\tstruct SomeData {\r\n\t\t\t\tbool b;\r\n\t\t\t\tint8 s8;\r\n\t\t\t\tuint8 u8;\r\n\t\t\t\tint16 s16;\r\n\t\t\t\tuint16 u16;\r\n\t\t\t\tint32 s32;\r\n\t\t\t\tuint32 u32;\r\n\t\t\t\tfloat f;\r\n\r\n\t\t\t\tvoid dump( void ) {\r\n\t\t\t\t\tSIBR_DEBUG(b);\r\n\t\t\t\t\tSIBR_DEBUG(s8);\r\n\t\t\t\t\tSIBR_DEBUG(u8);\r\n\t\t\t\t\tSIBR_DEBUG(s16);\r\n\t\t\t\t\tSIBR_DEBUG(u16);\r\n\t\t\t\t\tSIBR_DEBUG(s32);\r\n\t\t\t\t\tSIBR_DEBUG(u32);\r\n\t\t\t\t\tSIBR_DEBUG(f);\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t\t\tSomeData di = {true, -30, 120, -12000, 23000, -1234567, 2345678, 2.8f};\r\n\t\t\tstd::cout << \"Dumping DataIN:\" << std::endl;\r\n\t\t\tdi.dump();\r\n\r\n\t\t\tsibr::ByteStream bytes;\r\n\r\n\t\t\tbytes << di.b << di.s8 << di.u8 << di.s16 << di.u16 << di.s32 << di.u32 << di.f;\r\n\t\t\tSomeData dout = {false, 0, 0, 0, 0, 0, 0, 0.f};\r\n\t\t\tstd::cout << \"Dumping DataOUT:\" << std::endl;\r\n\t\t\tdout.dump();\r\n\r\n\t\t\tbytes.memoryDump();\r\n\r\n\r\n\t\t\tbytes >> dout.b >> dout.s8 >> dout.u8 >> dout.s16 >> dout.u16 >> dout.s32 >> dout.u32 >> dout.f;\r\n\t\t\tstd::cout << \"Dumping DataOUT:\" << std::endl;\r\n\t\t\tdout.dump();\r\n\t\t}\r\n\t\t*/\r\n\r\n\t} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nproject(sibr_system)\r\n\r\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\r\nsource_group(\"Source Files\" FILES ${SOURCES})\r\n\r\n## Specify target rules\r\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\r\n\r\ninclude_directories(\r\n\t${Boost_INCLUDE_DIRS}\r\n\t${picojson_INCLUDE_DIRS}\r\n\t${rapidxml_INCLUDE_DIRS}\r\n)\r\nif (WIN32)\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\t${Boost_LIBRARIES}\r\n\tpicojson\r\n\trapidxml\r\n\tnfd\r\n)\r\nelse()\r\ntarget_link_libraries(${PROJECT_NAME}\r\n\t${Boost_LIBRARIES}\r\n\tpicojson\r\n\trapidxml\r\n\tnativefiledialog\r\n)\r\nendif()\r\n\r\nadd_definitions( -DSIBR_SYSTEM_EXPORTS -DBOOST_ALL_DYN_LINK  )\r\n\r\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\r\n\r\n## High level macro to install in an homogen way all our ibr targets\r\ninclude(install_runtime)\r\nibr_install_target(${PROJECT_NAME}\r\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\r\n)\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/CommandLineArgs.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/system/CommandLineArgs.hpp\"\n#include \"core/system/Utils.hpp\"\n\n\nnamespace sibr\n{\n\tCommandLineArgs CommandLineArgs::global = CommandLineArgs();\n\n\tconst CommandLineArgs & getCommandLineArgs()\n\t{\n\t\treturn CommandLineArgs::getGlobal();\n\t}\n\n\tCommandLineArgs & CommandLineArgs::getGlobal()\n\t{\n\t\tstatic bool first = true;\n\t\tif (!global.init && first) {\n\t\t\tSIBR_WRG << \"CommandLineArgs::parseMainArgs(ac, av) was not called rigth after main(ac, av) \\n default value (empty command line) will be used\" << std::endl;\n\t\t\tfirst = false;\n\t\t}\n\t\treturn global;\n\t}\n\n\tvoid CommandLineArgs::parseMainArgs(const int argc, const char * const * argv)\n\t{\n\t\tstatic const std::vector<std::string> acceptable_prefixes = { \"--\", \"-\" };\n\n\t\tglobal.args.clear();\n\n\t\tglobal.args[\"app_path\"] = { std::string(argv[0])};\n\n\t\tstd::string current_arg;\n\t\tfor (int i = 1; i < argc; ++i) {\n\t\t\tstd::string arg = std::string(argv[i]);\n\t\t\tbool new_arg = false;\n\t\t\tfor (const auto & prefix : acceptable_prefixes) {\n\t\t\t\tif (arg.substr(0, prefix.size()) == prefix) {\n\t\t\t\t\tcurrent_arg = arg.substr(prefix.size());\n\t\t\t\t\tnew_arg = true;\t\t\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (current_arg.empty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (new_arg) {\n\t\t\t\tif (global.args.count(current_arg) > 0) {\n\t\t\t\t\tSIBR_WRG << \"Collision for argument : \" << arg << std::endl;\n\t\t\t\t} else {\n\t\t\t\t\tglobal.args[current_arg] = {};\n\t\t\t\t}\t\t\t\t\n\t\t\t} else {\n\t\t\t\tglobal.args[current_arg].push_back(arg);\n\t\t\t}\n\t\t}\n\n\t\tglobal.init = true;\n\t}\n\n\tbool CommandLineArgs::contains(const std::string & key) const\n\t{\n\t\treturn args.count(key) > 0;\n\t}\n\n\tint CommandLineArgs::numArguments(const std::string & key) const\n\t{\n\t\tif (contains(key)) {\n\t\t\treturn (int)args.at(key).size();\n\t\t} else {\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\n\tvoid CommandLineArgs::displayHelp() const {\n\t\t// Find the maximum length.\n\t\tsize_t maxLength = 0;\n\t\tfor (const auto & command : commands) {\n\t\t\tmaxLength = std::max(maxLength, command.first.size());\n\t\t}\n\n\t\tconst Path path = args.at(\"app_path\")[0];\n\t\tSIBR_LOG << \"Help for \" << path.filename().string() << \":\" << std::endl;\n\t\tfor(const auto & command : commands) {\n\t\t\t// Pad to align everything.\n#ifdef WIN32 // green\n\t\t\tstd::string req = \"[required]\";\n\t\t\tstd::string sec = command.second, xx;\n\t\t\tbool tgreen = false;\n\t\t\tif(sec.substr(sec.size()-req.size(), req.size()+1) == req) {\n\t\t\t\tsetupConsole();\n\t\t\t\tprintf(\"\\x1b[32m\");\n\t\t\t\ttgreen = true;\n\t\t\t}\n#endif\n\t\t\tstd::cout << \"\\t\" << \"--\" << command.first;\n\t\t\tstd::cout << std::string(int(maxLength) - command.first.size() + 1, ' ');\n\t\t\tstd::cout << command.second << std::endl;\n\n#ifdef WIN32\n\t\t\tif( tgreen )\n\t\t\t\trestoreConsole();\n#endif\n\t\t}\n\t\tstd::cout << std::endl;\n\t}\n\t\n\tvoid CommandLineArgs::registerCommand(const std::string & key, const std::string & description, const std::string & defaultValue) {\n\t\t// Register the command.\n\t\tstd::string defaultDesc = description.empty() ? \"\" : \" \";\n\t\tdefaultDesc.append(\"(default: \" + defaultValue + \")\");\n\t\tcommands[key] = description + defaultDesc;\n\t}\n\n\tvoid CommandLineArgs::registerRequiredCommand(const std::string & key, const std::string & description) {\n\t\t// Register the command.\n\t\tstd::string defaultDesc = description.empty() ? \"\" : \" \";\n\t\tdefaultDesc.append(\"[required]\");\n\t\tcommands[key] = description + defaultDesc;\n\t}\n\n\n\tAppArgs::AppArgs()\n\t{\n\t\tPath path = CommandLineArgs::getGlobal().getRequired<std::string>(\"app_path\");\n\t\tappName = path.filename().string();\n\t\tappPath = path.parent_path().string();\n\t}\n\n\tvoid AppArgs::displayHelpIfRequired() const {\n\t\tif(showHelp.get()) {\n\t\t\tgetCommandLineArgs().displayHelp();\n\t\t}\n\t}\n\n} // namespace sirb\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/CommandLineArgs.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <string>\n# include <vector>\n# include <map>\n\n# include \"Config.hpp\"\n# include <core/system/Vector.hpp>\n\nnamespace sibr\n{\n\t/**\n\t\\addtogroup sibr_system\n\t@{\n\t*/\n\n\t/// Used to wrap a toggle argument in the command line.\n\tstruct Switch {};\n\n\t/// uint contexpr helper, defining the number of command line tokens required to init T\n\ttemplate<typename T>\n\tconstexpr uint NumberOfArg = 1;\n\t/// uint contexpr helper, defining the number of command line tokens required to init T\n\ttemplate<>\n\tconstexpr uint NumberOfArg<bool> = 0;\n\t/// uint contexpr helper, defining the number of command line tokens required to init T\n\ttemplate<>\n\tconstexpr uint NumberOfArg<Switch> = 0;\n\t/// uint contexpr helper, defining the number of command line tokens required to init T\n\ttemplate<typename T, uint N>\n\tconstexpr uint NumberOfArg<sibr::Vector<T, N>> = N * NumberOfArg<T>;\n\n\t/// Helper to extract values from a vector of strings.\n\ttemplate<typename T> struct ValueGetter {\n\n\t\t/** Extract the N-th element from a vector of string representations.\n\t\t\\param values a list of strings representing elements\n\t\t\\param n the index of the element to query\n\t\t\\return the element corresponding to the N-th string.\n\t\t*/\n\t\tstatic T get(const std::vector<std::string> & values, uint n);\n\n\t\t/** Convert an element to its string representation.\n\t\t\\param value the element to convert\n\t\t\\return the string representation\n\t\t*/\n\t\tstatic std::string toString(const T & value);\n\t};\n\n\t/** Available rendering modes for IBR views. */\n\tenum RenderingModes {\n\t\tRENDERMODE_MONO,\n\t\tRENDERMODE_STEREO_ANAGLYPH,\n\t\tRENDERMODE_STEREO_QUADBUFFER\n\t};\n\n\n\n\t/** @} */\n\n\t/* Parse and store the command line arguments specified by the user.\n\t* Only a static instance exists, that must be init with parseMainArgs(argc,argv) right after main(argc,argv)\n\t* Parses -key or --key with any number of value.\n\t* \\ingroup sibr_system\n\t*/\n\tclass SIBR_SYSTEM_EXPORT CommandLineArgs {\n\n\tpublic:\n\n\t\t/** Populate arguments list, should be called once at launch.\n\t\t * \\param argc argument count\n\t\t * \\param argv argument list\n\t\t * */\n\t\tstatic void parseMainArgs(const int argc, const char* const* argv);\n\n\t\t/** Get the Nth parsed element following -key or --key as a T \n\t\t* If not available (key not found or not enough token), return default_val argument\n\t\t* \\param key the argument keyword\n\t\t* \\param default_val the default value to use if not present\n\t\t* */\n\t\ttemplate<typename T, uint N = 0>\n\t\tT get(const std::string & key, const T & default_val) const {\n\t\t\tT out;\n\t\t\tif (getInternal<T,N>(key, out)) {\n\t\t\t\treturn out;\n\t\t\t} else {\n\t\t\t\treturn default_val;\n\t\t\t}\n\t\t}\n\n\t\t/** Get the Nth parsed element following -key or --key as a T\n\t\t* If not available (key not found or not enough token), an error is raised.\n\t\t* \\param key the argument keyword\n\t\t* */\n\t\ttemplate<typename T, uint N = 0>\n\t\tT getRequired(const std::string & key) const {\n\t\t\tT out;\n\t\t\tif (!getInternal(key, out)) {\n\t\t\t\tSIBR_ERR;\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\t/** Register a command for the help message.\n\t\t * \\param key the command\n\t\t * \\param description a string describing the use of the command\n\t\t * \\param defaultValue string representation of the default value\n\t\t */\n\t\tvoid registerCommand(const std::string & key, const std::string & description, const std::string & defaultValue);\n\n\t\t/** Register a mandatory command for the help message.\n\t\t * \\param key the command\n\t\t * \\param description a string describing the use of the command\n\t\t */\n\t\tvoid registerRequiredCommand(const std::string & key, const std::string & description);\n\n\t\t/** Check if a given argument was specified by the user.\n\t\t *\\param key the argument to look for\n\t\t *\\return whether the argument was specified\n\t\t */\n\t\tbool contains(const std::string & key) const;\n\n\t\t/** Count how many parameters were specified by the suer for a given argument.\n\t\t *\\param key the argument to look for\n\t\t *\\return the number of parameters\n\t\t */\n\t\tint numArguments(const std::string & key) const;\n\n\t\t/** Global instance getter. */\n\t\tstatic CommandLineArgs & getGlobal();\n\n\t\t/** Display an help message to stdout. */\n\t\tvoid displayHelp() const;\n\n\tprotected:\n\n\t\t/// Default constructor.\n\t\tCommandLineArgs() = default;\n\n\t\t/** Get the Nth parsed element following -key or --key as a T\n\t\t* If not available (key not found or not enough token), an error is raised.\n\t\t* */\n\t\ttemplate<typename T, uint N = 0>\n\t\tbool getInternal(const std::string & key, T & val) const {\n\t\t\tif (contains(key) && (N + 1)*NumberOfArg<T> <= args.at(key).size()) {\n\t\t\t\tval = ValueGetter<T>::get(args.at(key), N);\n\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tstd::map<std::string, std::vector<std::string>> args; ///< List of arguments input by the user and their parameters.\n\t\tstd::map<std::string, std::string> commands; ///< List of registered commands to display the help.\n\t\tbool init = false; ///< Have the arguments been parsed.\n\n\t\tstatic CommandLineArgs global;///< Singleton (because there is only one command line).\n\t};\n\n\t/** Getter for the command line args manager singleton.\n\t* \\ingroup sibr_system\n\t */\n\tSIBR_SYSTEM_EXPORT const CommandLineArgs & getCommandLineArgs();\n\n\t/** Internal argument based interface.\n\t* \\ingroup sibr_system*/\n\ttemplate<typename T>\n\tclass ArgBase {\n\tpublic:\n\n\t\t/// \\return a reference to the argument value\n\t\toperator const T &() const { return _value; }\n\n\t\t/// \\return a reference to the argument value\n\t\tconst T & get() const { return _value; }\n\n\t\t/** Copy operator.\n\t\t\\param t the value to copy\n\t\t\\return a reference to the argument value\n\t\t*/\n\t\tT & operator=(const T & t) { _value = t; return _value; }\n\n\tprotected:\n\n\t\tT _value; ///< the argument value.\n\t};\n\n\t/** Template Arg class, will init itself in the defaut ctor using the command line args (ie. --key value)\n\t* Should be declared as some class/struct member using Arg<T> myArg = { \"key\", some_default_value };\n\t* is implicitly convertible to the template type\n\t* \\note As multiple implicit conversion is not possible in cpp, you might have to use the .get() method to access the inner T value\n\t* \\ingroup sibr_system\n\t* */\n\ttemplate<typename T>\n\tclass Arg : public ArgBase<T> {\n\tpublic:\n\t\t/** Constructor\n\t\t *\\param key the command argument\n\t\t *\\param default_value the default value\n\t\t *\\param description help message description\n\t\t */\n\t\tArg(const std::string & key, const T & default_value, const std::string & description = \"\") {\n\t\t\tthis->_value = CommandLineArgs::getGlobal().get<T>(key, default_value);\n\t\t\t// \\todo We could display default values if we had a common stringization method.\n\t\t\tCommandLineArgs::getGlobal().registerCommand(key, description, ValueGetter<T>::toString(default_value));\n\t\t}\n\t\tusing ArgBase<T>::operator=;\n\t};\n\n\t/// Specialization of Arg for Switch, default value get flipped if arg is present\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tclass Arg<Switch> : public ArgBase<bool> {\n\tpublic:\n\t\t/** Constructor\n\t\t *\\param key the command argument\n\t\t *\\param default_value the default boolean value\n\t\t *\\param description help message description\n\t\t */\n\t\tArg(const std::string & key, const bool & default_value, const std::string & description = \"\") {\n\t\t\tconst bool arg_is_present = CommandLineArgs::getGlobal().get<bool>(key, false);\n\t\t\tif (arg_is_present) {\n\t\t\t\t_value = !default_value;\n\t\t\t} else {\n\t\t\t\t_value = default_value;\n\t\t\t}\n\t\t\tconst std::string defaultDesc = (default_value ? \"enabled\" : \"disabled\");\n\t\t\tCommandLineArgs::getGlobal().registerCommand(key, description, defaultDesc);\n\t\t}\n\n\t\tusing ArgBase<bool>::operator=;\n\t};\n\tusing ArgSwitch = Arg<Switch>;\n\n\t/// Specialization of Arg for bool, value is true if key is present and false otherwise\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tclass Arg<bool> : public ArgBase<bool> {\n\tpublic:\n\t\t/** Constructor\n\t\t *\\param key the command argument\n\t\t *\\param description help message description\n\t\t *\\note Will default to false\n\t\t */\n\t\tArg(const std::string & key, const std::string & description = \"\") {\n\t\t\tconst bool arg_is_present = CommandLineArgs::getGlobal().get<bool>(key, false);\n\t\t\t_value = arg_is_present;\n\t\t\tCommandLineArgs::getGlobal().registerCommand(key, description, \"disabled\");\n\t\t}\n\n\t\tusing ArgBase<bool>::operator=;\n\t};\n\n\t/// Represent a mandatory argument\n\t/// \\ingroup sibr_system\n\ttemplate<typename T>\n\tclass RequiredArgBase {\n\tpublic:\n\t\t/** Constructor\n\t\t *\\param _key the command argument\n\t\t *\\param description help message description\n\t\t */\n\t\tRequiredArgBase(const std::string & _key, const std::string & description = \"\") : key(_key) {\n\t\t\tif (CommandLineArgs::getGlobal().contains(key)) {\n\t\t\t\t_value = CommandLineArgs::getGlobal().get<T>(key, _value);\n\t\t\t\twasInit = true;\n\t\t\t}\n\t\t\tCommandLineArgs::getGlobal().registerRequiredCommand(key, description);\n\t\t}\n\n\t\t/// \\return a reference to the argument value\n\t\toperator const T &() const { checkInit(); return _value; }\n\n\t\t/// \\return a reference to the argument value\n\t\tconst T & get() const { checkInit(); return _value; }\n\n\t\t/** Copy operator.\n\t\t\\param t the value to copy\n\t\t\\return a reference to the argument value\n\t\t*/\n\t\tT & operator=(const T & t) { _value = t; wasInit = true; return _value; }\n\n\t\t/// \\return true if the argument was given\n\t\tconst bool & isInit() const { return wasInit; }\n\n\tprotected:\n\n\t\t/** Check if the argument was init.If not, as it is a required argument we display the help message and raise an error.*/\n\t\tvoid checkInit() const {\n\t\t\tif (!wasInit) {\n\t\t\t\tCommandLineArgs::getGlobal().displayHelp();\n\t\t\t\tSIBR_ERR << \"Argument \\\"\" << key << \"\\\" is required.\" << std::endl;\n\t\t\t}\n\t\t}\n\n\t\tstd::string key; ///< Argument key.\n\t\tT _value; ///< Argument value.\n\t\tbool wasInit = false; ///< Was the argument initialized.\n\t};\n\n\t/// Similar to Arg, except this one will crash if attempt to use the value while not initialized\n\t/// initialization can be done using the command line or manually\n\t/// \\ingroup sibr_system\n\ttemplate<typename T>\n\tclass RequiredArg : public RequiredArgBase<T> {\n\t\tusing RequiredArgBase<T>::RequiredArgBase;\n\t};\n\n\t/// Specialization required for std::string as const string & key constructor and const T & constructor are ambiguous. \n\t/// TT : no const T & ctor anymore but operator const char*() const operator added\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tclass RequiredArg<std::string> : public RequiredArgBase<std::string> {\n\t\t\n\tpublic:\n\t\tusing RequiredArgBase<std::string>::RequiredArgBase;\n\t\tstd::string & operator=(const std::string & t) { _value = t; wasInit = true; return _value; }\n\n\t\toperator const char*() const { checkInit(); return _value.c_str(); }\n\t};\n\n\t/// Hierarchy of Args classes that can be seens as modules, and can be combined using virtual inheritance, with no duplication of code so derived Args has no extra work to do\n\t/// Assuming CommandLineArgs::parseMainArgs() was called once, Args arguments will be automatically initialized with the value from the command line by the constructor\n\t/// Existing Args structs should cover most of the existing IBR apps\n\t/// To add a new argument like --my-arg 5 on top of existing arguments and\n\t/// to add a new required argument like --important-param \"on\" on top of existing arguments, do the following:\n\t///\n\t/// struct SIBR_SYSTEM_EXPORT MyArgs : virtual ExistingArg1, virtual ExistingArgs2, ... {\n\t///\t\tArg<int> myParameter = { \"my-arg\", some_default_value };\n\t///\t\tRequiredArg<std::string> myRequiredParameter = { \"important-param\" };\n\t/// }\n\t/// \\ingroup sibr_system\n\tstruct SIBR_SYSTEM_EXPORT AppArgs {\n\t\t/// Constructor\n\t\tAppArgs();\n\n\t\tstd::string appName;\n\t\tstd::string appPath;\n\t\tArg<std::string> custom_app_path = { \"appPath\", \"./\", \"define a custom app path\" };\n\t\tArg<bool> showHelp = {\"help\", \"display this help message\"};\n\n\t\t/// Helper to print the help message if the help argument was passed.\n\t\tvoid displayHelpIfRequired() const;\n\n\t\t// offline path rendering options\n\t\tArg<bool> noExit = {\"noExit\", \"dont exit after rendering path \"};\n\t\tArg<std::string> pathFile = { \"pathFile\", \"\", \"filename of path to render offline; app renders path and exits\" }; // app needs to handle this; if it does default behavior is to render the path and exit\n\t\tArg<std::string> outPath = { \"outPath\", \"pathOutput\", \"Path of directory to store path output default relative the input path directory \" }; // app needs to handle this; if it does default behavior is to render the path and exit\n\n\t};\n\n\t/// Arguments related to a window.\n\t/// \\ingroup sibr_system\n\tstruct SIBR_SYSTEM_EXPORT WindowArgs {\n\t\tArg<int> win_width = { \"width\", 720, \"initial window width\" };\n\t\tArg<int> win_height = { \"height\", 480, \"initial window height\" };\n\t\tArg<int> vsync = { \"vsync\", 1, \"enable vertical sync\" };\n\t\tArg<bool> fullscreen = { \"fullscreen\", \"set the window to fullscreen\" };\n\t\tArg<bool> hdpi = { \"hd\", \"rescale UI elements for high-density screens\" };\n\t\tArg<bool> no_gui = { \"nogui\", \"do not use ImGui\" };\n\t\tArg<bool> gl_debug = { \"gldebug\", \"enable OpenGL error callback\" };\n\t\tArg<bool> offscreen = { \"offscreen\", \"do not open window\" };\n\t};\n\n\t/// Combination of window and application arguments.\n\t/// \\ingroup sibr_system\n\tstruct SIBR_SYSTEM_EXPORT WindowAppArgs :\n\t\tvirtual AppArgs, virtual WindowArgs {\n\t};\n\n\t/// Common rendering settings.\n\t/// \\ingroup sibr_system\n\tstruct SIBR_SYSTEM_EXPORT RenderingArgs {\n\t\tArg<std::string> scene_metadata_filename = { \"scene\", \"scene_metadata.txt\", \"scene metadata file\" };\n\t\tArg<Vector2i> rendering_size = { \"rendering-size\", { 0, 0 }, \"size at which rendering is performed\" };\n\t\tArg<int> texture_width = { \"texture-width\", 0 , \"size of the input data in memory\"};\n\t\tArg<float> texture_ratio = { \"texture-ratio\", 1.0f };\n\t\tArg<int> rendering_mode = { \"rendering-mode\", RENDERMODE_MONO, \"select mono (0) or stereo (1) rendering mode\" };\n\t\tArg<sibr::Vector3f> focal_pt = { \"focal-pt\", {0.0f, 0.0f, 0.0f} };\n\t\tArg<Switch> colmap_fovXfovY_flag = { \"colmap_fovXfovY_flag\", false };\n\t\tArg<Switch> force_aspect_ratio = { \"force-aspect-ratio\", false };\n\t};\n\n\t/// Dataset related arguments.\n\t/// \\ingroup sibr_system\n\tstruct SIBR_SYSTEM_EXPORT BasicDatasetArgs {\n\t\tRequiredArg<std::string> dataset_path = { \"path\", \"path to the dataset root\" };\n\t\tArg<std::string> dataset_type = { \"dataset_type\", \"\", \"type of dataset\" };\n\t};\n\n\t/// \"Default\" set of arguments.\n\t/// \\ingroup sibr_system\n\tstruct SIBR_SYSTEM_EXPORT BasicIBRAppArgs :\n\t\tvirtual WindowAppArgs, virtual BasicDatasetArgs, virtual RenderingArgs {\n\t};\n\n\t/// Specialization of value getter for strings.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<std::string> {\n\t\tstatic std::string get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn values[n];\n\t\t}\n\t\tstatic std::string toString(const std::string & value) {\n\t\t\treturn \"\\\"\" + value + \"\\\"\";\n\t\t}\n\t};\n\n\t/// Specialization of value getter for booleans.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<bool> {\n\t\tstatic bool get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn true;\n\t\t}\n\t\tstatic std::string toString(const bool & value) {\n\t\t\treturn value ? \"true\" : \"false\";\n\t\t}\n\t};\n\n\t/// Specialization of value getter for doubles.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<double> {\n\t\tstatic double get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn std::stod(values[n]);\n\t\t}\n\t\tstatic std::string toString(const double & value) {\n\t\t\treturn std::to_string(value);\n\t\t}\n\t};\n\n\t/// Specialization of value getter for floats.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<float> {\n\t\tstatic float get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn std::stof(values[n]);\n\t\t}\n\t\tstatic std::string toString(const float & value) {\n\t\t\treturn std::to_string(value);\n\t\t}\n\t};\n\n\t/// Specialization of value getter for integers.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<int> {\n\t\tstatic int get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn std::stoi(values[n]);\n\t\t}\n\t\tstatic std::string toString(const int & value) {\n\t\t\treturn std::to_string(value);\n\t\t}\n\t};\n\n\t/// Specialization of value getter for chars.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<char> {\n\t\tstatic char get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn static_cast<char>(std::stoi(values[n]));\n\t\t}\n\t\tstatic std::string toString(const char & value) {\n\t\t\treturn std::to_string(value);\n\t\t}\n\t};\n\n\t/// Specialization of value getter for unsigned integers.\n\t/// \\ingroup sibr_system\n\ttemplate<>\n\tstruct ValueGetter<uint> {\n\t\tstatic uint get(const std::vector<std::string> & values, uint n) {\n\t\t\treturn static_cast<uint>(std::stoi(values[n]));\n\t\t}\n\t\tstatic std::string toString(const uint & value) {\n\t\t\treturn std::to_string(value);\n\t\t}\n\t};\n\n\t/// Specialization of value getter for arrays.\n\t/// \\ingroup sibr_system\n\ttemplate<typename T, uint N>\n\tstruct ValueGetter<std::array<T, N>> {\n\t\tstatic std::array<T, N> get(const std::vector<std::string> & values, uint n) {\n\t\t\tstd::array<T, N> out;\n\t\t\tfor (uint i = 0; i < N; ++i) {\n\t\t\t\tout[i] = ValueGetter<T>::get(values, n*N*NumberOfArg<T> + i);\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\t\tstatic std::string toString(const std::array<T, N> & value) {\n\t\t\tstd::string res = \"(\";\n\t\t\tfor (uint i = 0; i < N; ++i) {\n\t\t\t\tres.append(ValueGetter<T>::toString(value[i]));\n\t\t\t\tif (i != N - 1) {\n\t\t\t\t\tres.append(\",\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn res + \")\";\n\t\t}\n\t};\n\n\t/// Specialization of value getter for sibr::Vectors & eigen matrices.\n\t/// \\ingroup sibr_system\n\ttemplate<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>\n\tstruct ValueGetter<Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>> {\n\t\tstatic Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> get(const std::vector<std::string> & values, uint n) {\n\t\t\tEigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> out;\n\t\t\tfor (uint i = 0; i < _Rows*_Cols; ++i) {\n\t\t\t\tout[i] = ValueGetter<_Scalar>::get(values, n*_Rows*_Cols*NumberOfArg<_Scalar> + i);\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\t\tstatic std::string toString(const Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> & value) {\n\t\t\tstd::string res = \"(\";\n\t\t\tfor (uint i = 0; i < _Rows*_Cols; ++i) {\n\t\t\t\tres.append(ValueGetter<_Scalar>::toString(value[i]));\n\t\t\t\tif(i != _Rows*_Cols-1) {\n\t\t\t\t\tres.append(\",\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn res + \")\";\n\t\t}\n\t};\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Config.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <mutex>\n#include \"core/system/Config.hpp\"\n\nstd::mutex\tgLogMutex;\n\nnamespace sibr\n{ \n\n\tLogExit::LogExit(void) :\n\t\tlock(gLogMutex)\n\t{ }\n\n\tvoid LogExit::operator <<=( const std::ostream& /*stream*/ )\n\t{\n\t\t// do exit, only profit a the rules of 'operator precedence'\n\t\t// to be executed after operator << when writing to the stream\n\t\t// itself.\n\t\t// So that this class is evaluated after writing the output and\n\t\t// it will exit (see dtor)\n\t\t//exit(EXIT_FAILURE);\n\t\tthrow std::runtime_error(\"See log for message errors\");\n\t}\n\n\tDebugScopeProfiler::~DebugScopeProfiler( void )\n\t{\n\t\tdouble t = double(clock() - _t0) / CLOCKS_PER_SEC;\n\t\tSIBR_LOG << \"[PROFILER] Scope '\" << _name <<\n\t\t\t\"' completed in \" << t << \"sec.\" << std::endl;\n\t}\n\n\tDebugScopeProfiler::DebugScopeProfiler( const std::string& name )\n\t\t: _name(name)\n\t{ \n\t\t_t0 = clock();\n\t}\n\n} // namespace sirb"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Config.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n//// Default includes ////\r\n# ifndef _USE_MATH_DEFINES\r\n# define _USE_MATH_DEFINES // for C++\r\n# endif\r\n# include <cmath>\r\n# include <cassert>\r\n# include <iostream>\r\n# include <string>\r\n# include <memory>\r\n# include <numeric>\r\n# include <algorithm>\r\n# include <mutex>\r\n# include <stdint.h>\r\n#include <boost/filesystem.hpp>\r\n\r\n//// Determine the operating system ////\r\n# if defined(_WIN32)\r\n#  define SIBR_OS_WINDOWS\r\n// Windows define macro for 'far' and 'near'...\r\n// http://stackoverflow.com/questions/118774/is-there-a-clean-way-to-prevent-windows-h-from-creating-a-near-far-macro\r\n// We could use other names than far and near but because we work in\r\n// computer graphics, I am sure that future guys will also try to\r\n// declare variables called far/near and loose time until finding\r\n// this is all because windows.\r\n\r\n// Edit: I wanted to do something about it (a warning message) but it\r\n// slow the compilation time (~5sec on my machine), so I let this code\r\n// but disabled by default.\r\n#  if SIBR_UNDEF_WINDOWMACROS\r\n// The strategy here is to undef macros AFTER including\r\n// headers that use them.\r\n#   pragma warning(push, 0)\r\n\r\n// Note including this file increase the compilation time\r\n// of the core libs by 5 additional seconds.\r\n#    include <windows.h>\r\n#    include <shlguid.h>\r\n#    include <commctrl.h>\r\n#    include <isguids.h>\r\n#    include <ShlObj.h>\r\n#   pragma warning(pop)\r\n#   undef far\r\n#   undef near\r\n#  endif // SIBR_UNDEF_WINDOWMACROS\r\n# elif defined(__unix__)\r\n#  define SIBR_OS_UNIX\r\n# elif defined(__APPLE__) && defined(__MACH__)\r\n#  define SIBR_OS_MAC\r\n# else\r\n#  error This operating system might be not supported.\r\n# endif\r\n\r\n//# undef NDEBUG /// \\todo By undefining NDEBUG, I enable the assert system definition. (TODO RELEASE: remove this)\r\n// (it certainly not a good pratice but it reveal previous assert already in the code)\r\n\r\n# ifdef SIBR_OS_WINDOWS\r\n\r\n#   pragma warning(disable:4503) // decorated name length exceeded, name was truncated\r\n//   The two following lines disable warning concerning 'inconsistent dll linkage'.\r\n//   MSVC doesn't like exporting STL containers because their implementation (their 'dll')\r\n//   can be different from one Windows to another. Unix garantees to provide a universal\r\n//   implementation and doesn't have this problem.\r\n//   My point of view is:\r\n//   - Make the code compliants with Windows' dlls will make us:\r\n//     1) lose lots of time (if we need to wrap STL containers each time we use them...)\r\n//     2) break the beauty of the code (we want to keep simple code).\r\n//   - Once we will release this code (for a large public), we should:\r\n//     1) Either explicitely export EVERY template/stl containers we use.\r\n//     2) Or provide msvc's dll (redistribuable) that contains the same stl implementation\r\n#   pragma warning(disable:4251)\r\n#   pragma warning(disable:4273)\r\n\r\n//// Export Macro (used for creating DLLs) ////\r\n#  ifdef SIBR_STATIC_DEFINE\r\n#    define SIBR_EXPORT\r\n#    define SIBR_NO_EXPORT\r\n#  else\r\n#    ifndef SIBR_SYSTEM_EXPORT\r\n#      ifdef SIBR_SYSTEM_EXPORTS\r\n          /* We are building this library */\r\n#        define SIBR_SYSTEM_EXPORT __declspec(dllexport)\r\n#      else\r\n          /* We are using this library */\r\n#        define SIBR_SYSTEM_EXPORT __declspec(dllimport)\r\n#      endif\r\n#    endif\r\n#    ifndef SIBR_NO_EXPORT\r\n#      define SIBR_NO_EXPORT\r\n#    endif\r\n#  endif\r\n# else\r\n#  define SIBR_SYSTEM_EXPORT\r\n# endif\r\n\r\n//// Deprecator Macro (used to flag as 'deprecated' some functionalities)  ////\r\n#ifndef SIBR_DEPRECATED\r\n#  define SIBR_DEPRECATED __declspec(deprecated)\r\n#endif\r\n\r\n//// Int To String Macro (used to convert int into string at compile-time) ////\r\n# define SIBR_MACROINTTOSTR_IMPL(x) #x\t\t// small trick to get __LINE__ into a string\r\n# define SIBR_MACROINTTOSTR(x) SIBR_MACROINTTOSTR_IMPL(x)\r\n//// Concatenate Macro (used to concatenate two things, whatever it is.    ////\r\n//// See SIBR_PROFILESCOPE for an example of use).                         ////\r\n# define SIBR_CATMACRO_IMPL(x, y) x ## y\r\n# define SIBR_CATMACRO(x, y) SIBR_CATMACRO_IMPL(x, y)\r\n\r\n//# if SIBR_OS_WINDOWS\r\n#  define __FUNCTION_STR__ __FUNCTION__\r\n//# else\r\n//#  define __FUNCTION_STR__ SIBR_MACROINTTOSTR(__FUNCTION__)\r\n//# endif\r\n\r\n// Macro used for\r\n// Use: #pragma message WARN(\"My message\")\r\n# if _MSC_VER\r\n#  define FILE_LINE_LINK __FILE__ \"(\" SIBR_MACROINTTOSTR(__LINE__) \") : \"\r\n#  define PRAGMAWARN(exp) (FILE_LINE_LINK \"WARNING: \" exp)\r\n# else//__GNUC__ - may need other defines for different compilers\r\n#  define PRAGMAWARN(exp) (\"WARNING: \" exp)\r\n# endif\r\n\r\n//// Math Macro ////\r\n# define SIBR_PI\t3.14159265358979323846\r\n# define SIBR_2PI (SIBR_PI * 2.0)\r\n\r\n# define SIBR_PI_DIV_180\t0.01745329251\r\n# define SIBR_180_DIV_PI\t57.2957795131\r\n\r\n# define SIBR_RADTODEG(x)\t((x) * (float)SIBR_180_DIV_PI) // ( (x) * (180.0f / PI) )\r\n# define SIBR_DEGTORAD(x)\t((x) * (float)SIBR_PI_DIV_180) // ( (x) * (PI / 180.0f) )\r\n\r\n//// Class Attribute Macro ////\r\n# define SIBR_DISALLOW_COPY( classname )\t\t\\\r\n\tprivate:\t\t\t\t\t\t\t\t\t\\\r\n\tclassname( const classname& );\t\t\t\t\\\r\n\tclassname& operator =( const classname& );\r\n\r\n# define SIBR_CLASS_PTR( classname )\t\t\t\\\r\n\tpublic:\t\t\t\t\t\t\t\t\t\t\\\r\n\ttypedef std::shared_ptr<classname>\tPtr;\t\\\r\n\ttypedef std::unique_ptr<classname>\tUPtr;\r\n\r\nnamespace sibr\r\n{\r\n\t/** Ensure that all logs are output before exiting when an error or exception is raised. \r\n\t\\ingroup sibr_system\r\n\t*/\r\n\tstruct SIBR_SYSTEM_EXPORT LogExit\r\n\t{\r\n\t\t/// Constructor.\r\n\t\tLogExit( void );\r\n\r\n\t\t/** Throw an exception and trigger exit.\r\n\t\t\\param stream the log stream.\r\n\t\t*/\r\n\t\tvoid operator <<=( const std::ostream& stream );\r\n\r\n\t\tstd::lock_guard<std::mutex>\t\tlock; ///< Sync lock.\r\n\t};\r\n}\r\n\r\n\r\n#ifdef NDEBUG\r\n# define SIBR_MAXIMIZE_INLINE\r\n#endif\r\n\r\n# ifdef SIBR_MAXIMIZE_INLINE\r\n#  define SIBR_OPT_INLINE\tinline\r\n# else\r\n#  define SIBR_OPT_INLINE\r\n# endif\r\n\r\n\r\n//// Log Macro ////\r\n# define SIBR_LOG\tstd::cout << \"[SIBR] --  INFOS  --:\\t\"\t\t\t// Must be replaced by a true log system\r\n# define SIBR_WRG\tstd::cout << \"[SIBR] !! WARNING !!:\\tFILE \" << __FILE__  << \"\\n\\t\\t\\tLINE \" << __LINE__ << \", FUNC \" << __FUNCTION_STR__ << \"\\n\\t\\t\\t\"\r\n# define SIBR_ERR ::sibr::LogExit() <<= \\\r\n\t\t\t\t\tstd::cerr << \"[SIBR] ##  ERROR  ##:\\tFILE \" << __FILE__  << \"\\n\\t\\t\\tLINE \" << __LINE__ << \", FUNC \" << __FUNCTION_STR__ << \"\\n\\t\\t\\t\"\t\t// Could be augmented for exiting\r\n\r\n// One drawback of using the standard assert is that you MUST catch the exception\r\n// it throws in order to display its message and know the error. Not everyone thinks\r\n// to do this (or want to add try/catch block in their code). Thus the solution here\r\n// is to, first display the message (btw we inform on the precise location where it\r\n// happens using __FILE__ and __LINE__) and then throw an exception (using the std\r\n// assert) so that we can retrieve the callstack easily for debugging).\r\n\r\n\r\n#ifdef NDEBUG\r\n# define SIBR_ASSERT(condition) ((void)0)\r\n# define SIBR_ASSERT_LOGIC(condition) (condition)\t// This assertion can contain code logic (this code will also be included at release)\r\n#else\r\n# define SIBR_ASSERT(condition)\t\t\tdo { if(!(condition)) { SIBR_WRG << \"ASSERT FAILED: \" #condition << std::endl; assert(condition); } } while(0)\r\n# define SIBR_ASSERT_LOGIC(condition)\tdo { if(!(condition)) { SIBR_WRG << \"ASSERT FAILED: \" #condition << std::endl; assert(condition); } } while(0)\r\n#endif\r\n\r\n// Small variants for adding function name\r\n# define SIBR_FLOG SIBR_LOG \"[\" << __FUNCTION_STR__ << \"]\"\r\n// Some code parts are written to manage additional or future features. They might remain untested\r\n// until they are required (avoiding losing time to test code that could be useless at the end).\r\n# define SIBR_UNTESTED\t\\\r\n\tSIBR_LOG << \"!Warning! Using an untested code flagged as potentially \"\t\t\\\r\n\t\"unstable. (if something goes wrong, check over here - \" __FILE__ \":\" << __LINE__ << \")\" << std::endl;\r\n# define SIBR_DEBUG(var) std::cout << __FILE__ \":\\n\" \"[Debug] \" #var \" = \"<< (var) << std::endl\t\t// No access to debug mode for now (so I made this tmp tool)\r\n\r\n// Note Visual studio is bugged with multiple statements macro (I avoided them):\r\n// http://stackoverflow.com/questions/22212737/strange-syntax-error-reported-in-a-range-based-for-loop\r\n\r\n//// TYPEDEF ////\r\ntypedef int8_t int8;\r\ntypedef int16_t int16;\r\ntypedef int32_t int32;\r\ntypedef int64_t int64;\r\ntypedef unsigned uint;\r\ntypedef uint8_t uint8;\r\ntypedef uint16_t uint16;\r\ntypedef uint32_t uint32;\r\ntypedef uint64_t uint64;\r\n\r\nusing Path = boost::filesystem::path;\r\n\r\n// This stuff should be in a file gathering all debug tools\r\n//# if !defined(NDEBUG)\r\n#  include <ctime>\r\nnamespace sibr\r\n{\r\n\t/// Used for quickly measuring time for completing a scope.\r\n\t/// \\ingroup sibr_system\r\n\tstruct SIBR_SYSTEM_EXPORT DebugScopeProfiler\r\n\t{\r\n\t\t/** Constructor.\r\n\t\t\\param name the display name of the profiling session\r\n\t\t*/\r\n\t\tDebugScopeProfiler( const std::string& name );\r\n\t\t\r\n\t\t/// Destructor.\r\n\t\t~DebugScopeProfiler( void );\r\n\t\r\n\tprivate:\r\n\t\tclock_t _t0; ///< Timing.\r\n\t\tstd::string _name; ///< Name.\r\n\t};\r\n\r\n# define SIBR_PROFILESCOPE_EXPAND(x, y) sibr::DebugScopeProfiler x(y);\r\n\t// its a bit weird (because of macro's tricks) but that just create an instance of DebugScopeProfiler (with a generated var name)\r\n# define SIBR_PROFILESCOPE\t\\\r\n\tSIBR_PROFILESCOPE_EXPAND(SIBR_CATMACRO(debugScopeProfiler,__COUNTER__), std::string(__FUNCTION_STR__) + std::string(\" (File: \" __FILE__ \":\" SIBR_MACROINTTOSTR(__LINE__) \")\") );\r\n# define SIBR_PROFILESCOPE_NAME(name) \\\r\n\tSIBR_PROFILESCOPE_EXPAND(SIBR_CATMACRO(debugScopeProfiler,__COUNTER__), name );\r\n} // namespace sibr\r\n//# endif\r\n\r\n//// Define the init behavior ////\r\n# define SIBR_INITZERO\r\n// Initializing with a default value (zero) can be slightly slower but\r\n// make the code safe. In the current case, we don't have performance\r\n// problems (of this level).\r\n# if defined(SIBR_INITZERO)\r\n#  ifndef EIGEN_INITIALIZE_MATRICES_BY_ZERO\r\n#   define EIGEN_INITIALIZE_MATRICES_BY_ZERO\r\n#  endif\r\n# endif\r\n\r\n// I didn't use the Plugin system for Eigen's MatrixBase\r\n// because I wanted also custom ctor\r\n// EDIT:\r\n// Now that libslmini leaved out, we don't need this ctor\r\n// anymore.\r\n# define EIGEN_MATRIXBASE_PLUGIN \"core/system/MatrixBasePlugin.hpp\"\r\n# define EIGEN_MATRIX_PLUGIN \"core/system/MatrixPlugin.hpp\"\r\n# include <Eigen/Core>\r\n# include <Eigen/Geometry>\r\n\r\n# define SIBR_USE_CHOLMOD_EIGEN\r\n\r\n\r\n\r\nnamespace sibr\r\n{\r\n\t/** Rounding operation.\r\n\t\\param x the value to round\r\n\t\\return the rounded value\r\n\t\\todo Compare behaviour with std::round\r\n\t\\ingroup sibr_system\r\n\t*/\r\n\tinline float round(float x) {\r\n\t\treturn x >= 0.0f ? floorf(x + 0.5f) : ceilf(x - 0.5f);\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/LoadingProgress.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/system/LoadingProgress.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\r\n\tLoadingProgress::LoadingProgress( size_t maxIteration,\r\n\t\tconst std::string& status, float interval )\r\n\t\t: _currentStep(0), _maxProgress(maxIteration), _status(status), _interval(interval)\r\n\t{\r\n\t\t_lastReport = clock::now();\r\n\t}\r\n\r\n\tvoid\t\t\t\tLoadingProgress::walk( size_t step )\r\n\t{\r\n\t\tstd::lock_guard<std::mutex> l(_mutex);\r\n\r\n\t\t_currentStep += step;\r\n\t\tif (std::chrono::duration<float>(clock::now()-_lastReport).count() >= _interval\r\n\t\t\t|| _currentStep >= _maxProgress)\r\n\t\t{\r\n\t\t\treport();\r\n\t\t\t_lastReport = clock::now();\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tfloat\t\t\t\tLoadingProgress::current( void ) const\r\n\t{\r\n\t\tif (_maxProgress <= 0)\r\n\t\t\treturn 1.f;\r\n\t\treturn (float)_currentStep/(float)_maxProgress;\r\n\t}\r\n\r\n\tvoid\t\t\t\tLoadingProgress::report( void ) const\r\n\t{\r\n\t\tif (_status.empty())\r\n\t\t\tSIBR_LOG << \"Progression [ \"<< current()*100.f <<\"% ]\" << std::endl;\r\n\t\telse\r\n\t\t\tSIBR_LOG << \"Progression [ \"<< current()*100.f <<\"% ] - \" << _status << std::endl;\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/LoadingProgress.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <functional>\n# include <chrono>\n# include <mutex>\n# include \"core/system//Config.hpp\"\n\n\nnamespace sibr\n{\n\t///\n\t/// Simple utility class for reporting on the standard output\n\t/// a loading progess. (So users know your heavy computations\n\t/// didn't crash)\n\t///\n\t/// Instructions:\n\t/// 1) Instantiate just before a loop (for or while), providing\n\t/// the max number of iterations.\n\t/// 2) Call walk() once in a the loop.\n\t/// \\ingroup sibr_system\n\t///\n\tclass SIBR_SYSTEM_EXPORT LoadingProgress\n\t{\n\tpublic:\n\t\ttypedef std::chrono::steady_clock\t\t\t\t\t\tclock;\n\t\ttypedef clock::time_point\t\t\t\t\t\t\t\ttime_point;\n\t\ttypedef std::function<void (float, const std::string&)>\tExternalCallback;\n\n\t\t/** Create a progress bar.\n\t\t\\param maxIteration total number of iterations\n\t\t\\param status a message that will be inserted in next reports\n\t\t\\param interval an interval of time between each report.\n\t\t*/\n\t\tLoadingProgress( size_t maxIteration,\n\t\t\tconst std::string& status=\"\", float interval=1.f );\n\n\t\t/// Make the loading progress by the given number of steps.\n\t\t/// \\param step number of steps\n\t\tvoid\t\t\t\twalk( size_t step = 1);\n\t\t///\t\\return the current progress in a range [0.0, 1.0]\n\t\tfloat\t\t\t\tcurrent( void ) const;\n\n\t\t/// \\return the time interval used\n\t\tinline float\t\t\t\tinterval( void ) const;\n\t\t/// Change the frequency of each report\n\t\t/// \\param interval the new step interval to use\n\t\tinline void\t\t\t\t\tinterval( float interval );\n\n\t\t/// \\return the status message used\n\t\tinline const std::string&\tstatus( void ) const;\n\t\t/// Insert a message in printed reports\n\t\t/// \\param message the message to insert\n\t\tinline void\t\t\t\t\tstatus( const std::string& message );\n\n\tprivate:\n\t\t/// Print a report\n\t\tvoid\t\t\t\treport( void ) const;\n\n\t\tsize_t\t\t_currentStep;\t///< current number of iterations\n\t\tsize_t\t\t_maxProgress;\t///< number of iterations before reaching 100%\n\t\tstd::string\t_status;\t\t///< inserted into a report (you can update it)\n\t\tfloat\t\t_interval;\t\t///< time interval before next report (sec)\n\t\ttime_point\t_lastReport;\t///< time point saved during the last report\n\t\tstd::mutex\t_mutex;\t\t\t///< used ot thread-safe this class (not heavly tested!)\n\t};\n\n\t///// DEFINITIONS /////\n\n\tfloat\t\t\t\tLoadingProgress::interval( void ) const {\n\t\treturn _interval;\n\t}\n\tvoid\t\t\t\tLoadingProgress::interval( float interval ) {\n\t\t_interval = interval;\n\t}\n\n\n\tconst std::string&\tLoadingProgress::status( void ) const {\n\t\treturn _status;\n\t}\n\tvoid\t\t\t\tLoadingProgress::status( const std::string& message ) {\n\t\t_status = message;\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/MD5.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*\n **********************************************************************\n ** md5.h -- Header file for implementation of MD5                   **\n ** RSA Data Security, Inc. MD5 Message Digest Algorithm             **\n ** Created: 2/17/90 RLR                                             **\n ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version              **\n ** Revised (for MD5): RLR 4/27/91                                   **\n **   -- G modified to have y&~z instead of y&z                      **\n **   -- FF, GG, HH modified to add in last register done            **\n **   -- Access pattern: round 2 works mod 5, round 3 works mod 3    **\n **   -- distinct additive constant for each step                    **\n **   -- round 4 added, working mod 7                                **\n **********************************************************************\n */\n\n/*\n **********************************************************************\n ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **\n **                                                                  **\n ** License to copy and use this software is granted provided that   **\n ** it is identified as the \"RSA Data Security, Inc. MD5 Message     **\n ** Digest Algorithm\" in all material mentioning or referencing this **\n ** software or this function.                                       **\n **                                                                  **\n ** License is also granted to make and use derivative works         **\n ** provided that such works are identified as \"derived from the RSA **\n ** Data Security, Inc. MD5 Message Digest Algorithm\" in all         **\n ** material mentioning or referencing the derived work.             **\n **                                                                  **\n ** RSA Data Security, Inc. makes no representations concerning      **\n ** either the merchantability of this software or the suitability   **\n ** of this software for any particular purpose.  It is provided \"as **\n ** is\" without express or implied warranty of any kind.             **\n **                                                                  **\n ** These notices must be retained in any copies of any part of this **\n ** documentation and/or software.                                   **\n **********************************************************************\n */\n\n#ifndef SIBR_SYSTEM_MD5\n#define SIBR_SYSTEM_MD5\n\n#include <stddef.h>\n\n/* typedef a 32 bit type */\ntypedef unsigned long int UINT4;\n\n/* Data structure for MD5 (Message Digest) computation */\ntypedef struct {\n  UINT4 i[2];                   /* number of _bits_ handled mod 2^64 */\n  UINT4 buf[4];                                    /* scratch buffer */\n  unsigned char in[64];                              /* input buffer */\n  unsigned char digest[16];     /* actual digest after MD5Final call */\n} MD5_CTX;\n\nvoid MD5Init (MD5_CTX *mdContext);\nvoid MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, size_t inLen);\nvoid MD5Final (MD5_CTX *mdContext);\n\n\n/*\n\tCompute MD5 for a binary blob\n\tWrites 16 bytes (4 uints) to pDigest\n\t\t\t\t\t\t\t\t\t*/\nvoid MD5Buffer( void* buffer, size_t bufLen, unsigned int* pDigest )\n{\n  int i;\n  MD5_CTX mdContext;\n\n  // compute MD5\n  MD5Init(&mdContext);\n  MD5Update(&mdContext, (unsigned char*)buffer, bufLen);\n  MD5Final(&mdContext);\n\n  // copy digest over\n  for( i = 0; i < 4; ++i )\n\t  pDigest[i] = *(((unsigned int*)mdContext.digest)+i);\n}\n\n/*\n **********************************************************************\n ** md5.c                                                            **\n ** RSA Data Security, Inc. MD5 Message Digest Algorithm             **\n ** Created: 2/17/90 RLR                                             **\n ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version                  **\n **********************************************************************\n */\n\n/*\n **********************************************************************\n ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **\n **                                                                  **\n ** License to copy and use this software is granted provided that   **\n ** it is identified as the \"RSA Data Security, Inc. MD5 Message     **\n ** Digest Algorithm\" in all material mentioning or referencing this **\n ** software or this function.                                       **\n **                                                                  **\n ** License is also granted to make and use derivative works         **\n ** provided that such works are identified as \"derived from the RSA **\n ** Data Security, Inc. MD5 Message Digest Algorithm\" in all         **\n ** material mentioning or referencing the derived work.             **\n **                                                                  **\n ** RSA Data Security, Inc. makes no representations concerning      **\n ** either the merchantability of this software or the suitability   **\n ** of this software for any particular purpose.  It is provided \"as **\n ** is\" without express or implied warranty of any kind.             **\n **                                                                  **\n ** These notices must be retained in any copies of any part of this **\n ** documentation and/or software.                                   **\n **********************************************************************\n */\n\n/* forward declaration */\nstatic void Transform (UINT4 *buf, UINT4 *in);\n\nstatic unsigned char PADDING[64] = {\n  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n};\n\n/* F, G and H are basic MD5 functions: selection, majority, parity */\n#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))\n#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))\n#define H(x, y, z) ((x) ^ (y) ^ (z))\n#define I(x, y, z) ((y) ^ ((x) | (~z))) \n\n/* ROTATE_LEFT rotates x left n bits */\n#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))\n\n/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */\n/* Rotation is separate from addition to prevent recomputation */\n#define FF(a, b, c, d, x, s, ac) \\\n  {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n   (a) = ROTATE_LEFT ((a), (s)); \\\n   (a) += (b); \\\n  }\n#define GG(a, b, c, d, x, s, ac) \\\n  {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n   (a) = ROTATE_LEFT ((a), (s)); \\\n   (a) += (b); \\\n  }\n#define HH(a, b, c, d, x, s, ac) \\\n  {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n   (a) = ROTATE_LEFT ((a), (s)); \\\n   (a) += (b); \\\n  }\n#define II(a, b, c, d, x, s, ac) \\\n  {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n   (a) = ROTATE_LEFT ((a), (s)); \\\n   (a) += (b); \\\n  }\n\nvoid MD5Init (MD5_CTX *mdContext)\n{\n  mdContext->i[0] = mdContext->i[1] = (UINT4)0;\n\n  /* Load magic initialization constants.\n   */\n  mdContext->buf[0] = (UINT4)0x67452301;\n  mdContext->buf[1] = (UINT4)0xefcdab89;\n  mdContext->buf[2] = (UINT4)0x98badcfe;\n  mdContext->buf[3] = (UINT4)0x10325476;\n}\n\nvoid MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, size_t inLen)\n{\n  UINT4 in[16];\n  int mdi;\n  unsigned int i, ii;\n\n  /* compute number of bytes mod 64 */\n  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);\n\n  /* update number of bits */\n  if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])\n    mdContext->i[1]++;\n  mdContext->i[0] += ((UINT4)inLen << 3);\n  mdContext->i[1] += ((UINT4)inLen >> 29);\n\n  while (inLen--) {\n    /* add new character to buffer, increment mdi */\n    mdContext->in[mdi++] = *inBuf++;\n\n    /* transform if necessary */\n    if (mdi == 0x40) {\n      for (i = 0, ii = 0; i < 16; i++, ii += 4)\n        in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |\n                (((UINT4)mdContext->in[ii+2]) << 16) |\n                (((UINT4)mdContext->in[ii+1]) << 8) |\n                ((UINT4)mdContext->in[ii]);\n      Transform (mdContext->buf, in);\n      mdi = 0;\n    }\n  }\n}\n\nvoid MD5Final (MD5_CTX *mdContext)\n{\n  UINT4 in[16];\n  int mdi;\n  unsigned int i, ii;\n  unsigned int padLen;\n\n  /* save number of bits */\n  in[14] = mdContext->i[0];\n  in[15] = mdContext->i[1];\n\n  /* compute number of bytes mod 64 */\n  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);\n\n  /* pad out to 56 mod 64 */\n  padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);\n  MD5Update (mdContext, PADDING, padLen);\n\n  /* append length in bits and transform */\n  for (i = 0, ii = 0; i < 14; i++, ii += 4)\n    in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |\n            (((UINT4)mdContext->in[ii+2]) << 16) |\n            (((UINT4)mdContext->in[ii+1]) << 8) |\n            ((UINT4)mdContext->in[ii]);\n  Transform (mdContext->buf, in);\n\n  /* store buffer in digest */\n  for (i = 0, ii = 0; i < 4; i++, ii += 4) {\n    mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);\n    mdContext->digest[ii+1] =\n      (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);\n    mdContext->digest[ii+2] =\n      (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);\n    mdContext->digest[ii+3] =\n      (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);\n  }\n}\n\n/* Basic MD5 step. Transform buf based on in.\n */\nstatic void Transform (UINT4 *buf, UINT4 *in)\n{\n  UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];\n\n  /* Round 1 */\n#define S11 7\n#define S12 12\n#define S13 17\n#define S14 22\n  FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */\n  FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */\n  FF ( c, d, a, b, in[ 2], S13,  606105819); /* 3 */\n  FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */\n  FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */\n  FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */\n  FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */\n  FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */\n  FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */\n  FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */\n  FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */\n  FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */\n  FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */\n  FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */\n  FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */\n  FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */\n\n  /* Round 2 */\n#define S21 5\n#define S22 9\n#define S23 14\n#define S24 20\n  GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */\n  GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */\n  GG ( c, d, a, b, in[11], S23,  643717713); /* 19 */\n  GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */\n  GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */\n  GG ( d, a, b, c, in[10], S22,   38016083); /* 22 */\n  GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */\n  GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */\n  GG ( a, b, c, d, in[ 9], S21,  568446438); /* 25 */\n  GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */\n  GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */\n  GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */\n  GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */\n  GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */\n  GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */\n  GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */\n\n  /* Round 3 */\n#define S31 4\n#define S32 11\n#define S33 16\n#define S34 23\n  HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */\n  HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */\n  HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */\n  HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */\n  HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */\n  HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */\n  HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */\n  HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */\n  HH ( a, b, c, d, in[13], S31,  681279174); /* 41 */\n  HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */\n  HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */\n  HH ( b, c, d, a, in[ 6], S34,   76029189); /* 44 */\n  HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */\n  HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */\n  HH ( c, d, a, b, in[15], S33,  530742520); /* 47 */\n  HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */\n\n  /* Round 4 */\n#define S41 6\n#define S42 10\n#define S43 15\n#define S44 21\n  II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */\n  II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */\n  II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */\n  II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */\n  II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */\n  II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */\n  II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */\n  II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */\n  II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */\n  II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */\n  II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */\n  II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */\n  II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */\n  II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */\n  II ( c, d, a, b, in[ 2], S43,  718787259); /* 63 */\n  II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */\n\n  buf[0] += a;\n  buf[1] += b;\n  buf[2] += c;\n  buf[3] += d;\n}\n\n#endif\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Matrix.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/system/Transform3.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\tMatrix4f perspective( float fovRadian, float ratio, float zn, float zf, const sibr::Vector2f & p)\r\n\t{\r\n\t\tconst float yScale = float(1.0)/std::tan(fovRadian/2.0f);\r\n\t\tconst float xScale = yScale/ratio;\r\n\r\n\t\tMatrix4f m;\r\n\t\tconst float dx = 2.0f * p.x() - 1.0f;\r\n\t\tconst float dy = 2.0f * p.y() - 1.0f;\r\n\t\tm << \r\n\t\t\txScale,    0,          dx,             0,\r\n\t\t\t0,    yScale,          dy,             0,\r\n\t\t\t0,         0, (zn+zf)/(zn-zf), 2*zn*zf/(zn-zf),\r\n\t\t\t0,         0,         -1,             0;\r\n\r\n\t\treturn m;\r\n\t}\r\n\r\n\tMatrix4f perspectiveOffCenter(\r\n\t\t\tfloat left, float right, float bottom, float top, float mynear, float myfar )\r\n\t{      \r\n\t\tfloat x =  (2.0f * mynear) / (right - left);\r\n\t\tfloat y =  (2.0f * mynear) / (top - bottom);\r\n\t\tfloat a =  (right + left) / (right - left);\r\n\r\n\t\tfloat b =  (top + bottom) / (top - bottom);\r\n\t\tfloat c = -(myfar + mynear) / (myfar - mynear);\r\n\t\tfloat d = -(2.0f * myfar * mynear) / (myfar - mynear);\r\n\t\tfloat e = -1.0f;\r\n\r\n\t\tMatrix4f m;\r\n\r\n\t\tm << \r\n\t\t\tx, 0, 0, 0, \r\n\t\t\t0, y, 0, 0,\r\n\t\t\ta, b, c, e,\r\n\t\t\t0, 0, d, 0;\r\n\r\n\t\treturn m;\r\n\t}\r\n\r\n\tMatrix4f perspectiveStereo(\r\n\t\t\tfloat fovRadian, float aspect, float zn, float zf, float focalDistance, float eyeDistance, bool isLeftEye )\r\n\t{\r\n\r\n\t\tfloat left, right;\r\n\t\tfloat a = float(1.0f)/std::tan(fovRadian/2.0f);\r\n\t\tfloat b = zf / focalDistance;\r\n\r\n\t\tif (isLeftEye)          // left camera\r\n\t\t{\r\n\t\t\tleft  = - aspect * a + (eyeDistance) * b;\r\n\t\t\tright =   aspect * a + (eyeDistance) * b;\r\n\t\t}\r\n\t\telse                 // right camera\r\n\t\t{\r\n\t\t\tleft  = - aspect * a - (eyeDistance) * b;\r\n\t\t\tright =   aspect * a - (eyeDistance) * b;\r\n\t\t}\r\n\r\n\t\treturn perspectiveOffCenter(left, right, -a, a, zn, zf);\r\n\t}\r\n\r\n\tMatrix4f orthographic(float right, float top, float mynear, float myfar)\r\n\t{\r\n\r\n\t\tMatrix4f m;\r\n\r\n\t\tm <<\r\n\t\t\t1.0f/right, 0.0f,\t\t0.0f,\t\t\t\t\t0.0f,\r\n\t\t\t0.0f,\t\t1.0f/top,\t0.0f,\t\t\t\t\t0.0f,\r\n\t\t\t0.0f,\t\t0.0f,\t\t-2.0f/(myfar-mynear),\t-(myfar + mynear) / (myfar - mynear),\r\n\t\t\t0.0f,\t\t0.0f,\t\t0.0f,\t\t\t\t\t1.0f;\r\n\r\n\t\treturn m;\r\n\t}\r\n\r\n\tMatrix4f\tlookAt(\r\n\t\t\tconst Vector3f& eye,\r\n\t\t\tconst Vector3f& center,\r\n\t\t\tconst Vector3f& up )\r\n\t{\r\n\t\tconst sibr::Vector3f f = (center - eye).normalized();\r\n\t\tsibr::Vector3f u = up.normalized();\r\n\t\tconst sibr::Vector3f s = f.cross(u).normalized();\r\n\t\tu = s.cross(f);\r\n\r\n\t\tEigen::Matrix<float, 4, 4, 0, 4, 4> res;\r\n\t\tres <<  s.x(),s.y(),s.z(),-s.dot(eye),\r\n\t\t\tu.x(),u.y(),u.z(),-u.dot(eye),\r\n\t\t\t-f.x(),-f.y(),-f.z(),f.dot(eye),\r\n\t\t\t0,0,0,1;\r\n\r\n\t\treturn res;\r\n\t}\r\n\r\n\tvoid \toperator<< (std::ofstream& outfile, const Matrix4f& m)\r\n\t{\r\n\t\toutfile << m(0,0) << \" \" << m(0,1) << \" \" << m(0,2) << \" \" << m(0,3) \r\n\t\t\t<< \" \" << m(1,0) << \" \" << m(1,1) << \" \" << m(1,2) << \" \" << m(1,3) \r\n\t\t\t<< \" \" << m(2,0) << \" \" << m(2,1) << \" \" << m(2,2) << \" \" << m(2,3) \r\n\t\t\t<< \" \" << m(3,0) << \" \" << m(3,1) << \" \" << m(3,2) << \" \" << m(3,3)  ;\r\n\r\n\t}\r\n\r\n\tvoid \toperator>>( std::ifstream& infile, Matrix4f& out)\r\n\t{\r\n\t\tfloat m[16];\r\n\t\tinfile >> m[0] >> m[1] >> m[2] >> m[3] \r\n\t\t\t>> m[4] >> m[5] >> m[6] >> m[7] \r\n\t\t\t>> m[8] >> m[9] >> m[10] >> m[11]\r\n\t\t\t>> m[12] >> m[13] >> m[14] >> m[15];\r\n\r\n\t\t\tout << m[0] , m[1] , m[2] , m[3] \r\n\t\t\t\t, m[4] , m[5] , m[6] , m[7] \r\n\t\t\t\t, m[8] , m[9] , m[10] , m[11]\r\n\t\t\t\t, m[12] , m[13] , m[14] , m[15];\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Matrix.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n/******************************************************************************\r\n\r\n  Design Decision -- Eigen Integration:\r\n\r\n    At the very beginning, we used vector/matrices from libminisl. Then we\r\n  began to switch to Eigen's tools (because already used in lots of our code).\r\n  Thus during the migration phase, we used custom class inheriting from\r\n  Eigen::Matrix.\r\n    However I encountered issues when executing the code under linux. It\r\n  appears it was because of SSE instructions (special pipeline on CPU allowing\r\n  to perform some vector operations in parallel). This SSE instruction required\r\n  128-bit aligned memory. There are articles about this on Eigen website:\r\n\teigen.tuxfamily.org/dox/group__DenseMatrixManipulation__Alignement.html\r\n  \r\n  But to summerize: keeping the alignment is difficult because you have to\r\n  overload new operator in each class containing an Eigen::Matrix. Too unsafe,\r\n  thus I disable this (Eigen::DontAlign) but missing assignment operators did\r\n  that this consideration was ignored in some cases.\r\n  E.g.: sibr::Matrix A, B, C;\r\n  // ... // set A and B\r\n  C = A*B; // A*B return a temporary class of Eigen but C didn't have the\r\n  // assignment operator for this class [...] it wrongly considered it has an\r\n  // not-aligned matrix and data was corrupted.\r\n\r\n  Now SIBR uses a plugin system to extend Eigen classes:\r\n  \teigen.tuxfamily.org/dox/TopicCustomizingEigen.html\r\n\r\n  It's both safer and faster. (but it was not possible during the migration\r\n  phase because I needed the child type to perfom automatic convertion with\r\n  remaining libminisl tools).\r\n\r\n******************************************************************************/\r\n\r\n#pragma once\r\n\r\n# include <fstream>\r\n# include \"core/system/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n\r\n\r\nnamespace sibr\r\n{\r\n\t/**\r\n\t\\addtogroup sibr_system\r\n\t@{\r\n\t*/\r\n\ttypedef\tEigen::Matrix<unsigned, 4, 4, Eigen::DontAlign, 4, 4>\t\tMatrix4u;\r\n\ttypedef\tEigen::Matrix<int, 4, 4, Eigen::DontAlign, 4, 4>\t\tMatrix4i;\r\n\ttypedef\tEigen::Matrix<float, 4, 4, Eigen::DontAlign, 4, 4>\tMatrix4f;\r\n\ttypedef\tEigen::Matrix<double, 4, 4, Eigen::DontAlign, 4, 4>\t\tMatrix4d;\r\n\ttypedef\tEigen::Matrix<unsigned, 3, 3, Eigen::DontAlign, 3, 3>\t\tMatrix3u;\r\n\ttypedef\tEigen::Matrix<int, 3, 3, Eigen::DontAlign, 3, 3>\t\tMatrix3i;\r\n\ttypedef\tEigen::Matrix<float, 3, 3, Eigen::DontAlign, 3, 3>\tMatrix3f;\r\n\ttypedef\tEigen::Matrix<double, 3, 3, Eigen::DontAlign, 3, 3>\t\tMatrix3d;\r\n\r\n\t/** Convert a quaternion to a rotation matrix.\r\n\t * \\param q the quaternion to convert\r\n\t * \\return the corresponding matrix\r\n\t */\r\n\ttemplate <typename T>\r\n\t\tEigen::Matrix<T, 4, 4, 0, 4, 4> matFromQuat( const Eigen::Quaternion<T, 0>& q ) {\r\n\t\t\tEigen::Matrix<T, 3, 3, 0, 3, 3> s = q.toRotationMatrix();\r\n\r\n\t\t\tEigen::Matrix<T, 4, 4, 0, 4, 4> mat;\r\n\t\t\tmat <<\r\n\t\t\t\ts(0,0), s(0,1), s(0,2), 0,\r\n\t\t\t\ts(1,0), s(1,1), s(1,2), 0,\r\n\t\t\t\ts(2,0), s(2,1), s(2,2), 0,\r\n\t\t\t\t0, 0, 0, 1;\r\n\t\t\treturn mat;\r\n\t\t}\r\n\t\r\n\t/** Convert a translation to a rotation matrix.\r\n\t * \\param vec the translation to convert\r\n\t * \\return the corresponding matrix\r\n\t */\r\n\ttemplate <typename T, int Options>\r\n\t\tEigen::Matrix<T, 4, 4, 0, 4, 4> matFromTranslation( const Eigen::Matrix<T, 3, 1, Options>& vec ) {\r\n\r\n\t\t\tEigen::Matrix<T, 4, 4, 0, 4, 4> mat;\r\n\t\t\tmat.setIdentity();\r\n\r\n\t\t\tmat(0,3) = vec.x();\r\n\t\t\tmat(1,3) = vec.y();\r\n\t\t\tmat(2,3) = vec.z();\r\n\t\t\treturn mat;\r\n\t\t}\r\n\r\n\t/** Generate a perspective matrix.\r\n\t * \\param fovRadian vertical field of view in radians\r\n\t * \\param ratio aspect ratio\r\n\t * \\param zn near plane\r\n\t * \\param zf far plane\r\n\t * \\param p the principal point, expressed in [0,1]\r\n\t * \\return the projection matrix */\r\n\tMatrix4f SIBR_SYSTEM_EXPORT perspective( float fovRadian, float ratio, float zn, float zf, const ::sibr::Vector2f & p = {0.5f, 0.5f});\r\n\r\n\t/** Generate an off-center perspective matrix.\r\n\t * Defined by giving the top/left/right/bottom extent in world units.\r\n\t *\t\\param left left extent\r\n\t *\t\\param right right extent\r\n\t *\t\\param bottom bottom extent\r\n\t *\t\\param top top extent\r\n\t *\t\\param mynear near plane\r\n\t *\t\\param myfar dar plane\r\n\t *\t\\return the projection matrix */\r\n\tMatrix4f SIBR_SYSTEM_EXPORT perspectiveOffCenter(\r\n\t\t\tfloat left, float right, float bottom, float top, float mynear, float myfar );\r\n\r\n\t/** Generate a perspective matrix for stereo rendering.\r\n\t * \\param fovRadian vertical field of view in radians\r\n\t * \\param aspect aspect ratio\r\n\t * \\param zn near plane\r\n\t * \\param zf far plane\r\n\t * \\param focalDistance the focal distance\r\n\t * \\param eyeDistance the inter-eye distance\r\n\t * \\param isLeftEye if true computes the left eye matrix, else the right eye\r\n\t * \\return the projection matrix */\r\n\tMatrix4f SIBR_SYSTEM_EXPORT perspectiveStereo( float fovRadian, float aspect, float zn, float zf, float focalDistance,\r\n\t\t\tfloat eyeDistance, bool isLeftEye ); \r\n\r\n\t/** Generate an orthographic matrix.\r\n\t * Defined by giving the top/right extent in world units.\r\n\t *\t\\param right right extent\r\n\t *\t\\param top top extent\r\n\t *\t\\param mynear near plane\r\n\t *\t\\param myfar dar plane\r\n\t *\t\\return the projection matrix */\r\n\tMatrix4f SIBR_SYSTEM_EXPORT orthographic(float right, float top, float mynear, float myfar);\r\n\r\n\t/** Generate a view matrix using the look at parameters.\r\n\t *\t\\param eye camera position\r\n\t *\t\\param center point the camera is looking at\r\n\t *\t\\param up up vector\r\n\t *\t\\return the projection matrix */\r\n\tMatrix4f SIBR_SYSTEM_EXPORT lookAt( const Vector3f& eye, const Vector3f& center, const Vector3f& up );\r\n\r\n\t/** Output a Matrix4f to a file stream.\r\n\t *\\param outfile the output file\r\n\t *\\param m the matrix\r\n\t */\r\n\tvoid \tSIBR_SYSTEM_EXPORT operator<< (std::ofstream& outfile, const Matrix4f& m);\r\n\r\n\t/** Read a Matrix4f from a file stream.\r\n\t *\\param infile the input file\r\n\t *\\param out the matrix\r\n\t */\r\n\tvoid \tSIBR_SYSTEM_EXPORT operator>>( std::ifstream& infile, Matrix4f& out);\r\n\r\n\t/** }@ */\r\n} // namespace sibr\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/MatrixBasePlugin.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n// This file is used to extend Eigen's MatrixBase class using\n// the following tricks:\n// https://eigen.tuxfamily.org/dox-3.2/TopicCustomizingEigen.html\n\npublic:\n\n/** Helper to evaluate a transposed matrix without overwriting risks.\n\t\\return a copy of the matrix, transposed\n*/\ninline MatrixBase transposed( void ) { return this->transpose().eval(); }\n\n/** Get the first two components, filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 2, 1,Eigen::DontAlign>\txy( float fill=0.f ) const {\n\treturn Matrix<Scalar, 2, 1,Eigen::DontAlign>( this->operator[](0), size()<2? fill:this->operator[](1));\n}\n\n/** Get the first two components swapped, filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 2, 1, Eigen::DontAlign>\tyx(float fill = 0.f) const {\n\treturn Matrix<Scalar, 2, 1, Eigen::DontAlign>(this->operator[](1), size()<2 ? fill : this->operator[](0));\n}\n\n/** Get the last two components swapped, filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 2, 1, Eigen::DontAlign>\twz(float fill = 0.f) const {\n\treturn Matrix<Scalar, 2, 1, Eigen::DontAlign>(size()<4 ? fill : this->operator[](3), size()<3 ? fill : this->operator[](2));\n}\n\n/** Get the first three components, filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 3, 1,Eigen::DontAlign>\txyz( float fill=0.f ) const {\n\treturn Matrix<Scalar, 3, 1,Eigen::DontAlign>( this->operator[](0), size()<2? fill:this->operator[](1), size()<3? fill:this->operator[](2));\n}\n\n/** Get the first four components, filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 4, 1,Eigen::DontAlign>\txyzw( float fill=0.f ) const {\n\treturn Matrix<Scalar, 4, 1,Eigen::DontAlign>( this->operator[](0), size()<2? fill:this->operator[](1), size()<3? fill:this->operator[](2), size()<4? fill:this->operator[](3));\n}\n\n/** Get the first three components swapped (YXZ), filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 3, 1, Eigen::DontAlign>\tyxz(float fill = 0.f) const {\n\treturn Matrix<Scalar, 3, 1, Eigen::DontAlign>(size()<2 ? fill : this->operator[](1), this->operator[](0), size()<3 ? fill : this->operator[](2));\n}\n\n/** Get the first three components swapped (YZX), filling with a default value if some are missing.\n\t\\param fill the default value to use\n\t\\return the selected components.\n*/\nMatrix<Scalar, 3, 1, Eigen::DontAlign>\tyzx(float fill = 0.f) const {\n\treturn Matrix<Scalar, 3, 1, Eigen::DontAlign>(size()<2 ? fill : this->operator[](1), size()<3 ? fill : this->operator[](2), this->operator[](0));\n}\n\n/** Check if a vector is exactly zero for all components\n\\return true if all components are exactly zero.\n*/\nbool\tisNull( void ) const { \n\treturn (array() == 0).all();\n}\n\ntypedef Scalar Type;\n\n//enum { NumComp = Derived::RowsAtCompileTime };\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/MatrixPlugin.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n// This file is used to extend Eigen's MatrixBase class using\n// the following tricks:\n// https://eigen.tuxfamily.org/dox-3.2/TopicCustomizingEigen.html\n\npublic:\n\ntypedef Scalar Type;\nenum { NumComp = RowsAtCompileTime };\n\n//Matrix( const Scalar* data ) { for(int i=0; i<NumComp; i++) this->operator [] (i) = data[i]; }\n\n/**\nMatrix( float x, float y=0.f, float z=0.f, float w=0.f ) {\n\tfloat data[] = {x, y, z, w};\n\tfor(int i=0; i<NumComp; i++) this->operator [] (i) = data[i];\n}\n**/\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Quaternion.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/system/Transform3.hpp\"\n\nnamespace sibr\n{\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Quaternion.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <Eigen/Core>\n# include <Eigen/Geometry>\n# include \"core/system/Config.hpp\"\n# include \"core/system/Matrix.hpp\"\n# include \"core/system/Vector.hpp\"\n\nnamespace sibr\n{\n\t/**\n\t\\addtogroup sibr_system\n\t@{\n\t*/\n\n\t/** Build a quaternion from a rotation matrix\n\t *\\param m the rotation matrix\n\t *\\return the quaternion\n\t *\\todo Seems to be different from sibr::Quaternion(rotationmatrix)\n\t */\n\ttemplate <typename T, int Options>\n\tEigen::Quaternion<T, 0>\tquatFromMatrix(const Eigen::Matrix<T, 3, 3, Options, 3, 3>& m) {\n\t\tEigen::Quaternion<T, 0> q;\n\t\tfloat trace = m(0, 0) + m(1, 1) + m(2, 2) + 1.f;\n\t\tif (trace > 0)\n\t\t{\n\t\t\tfloat s = 0.5f / sqrtf(trace);\n\t\t\tq.x() = (m(1, 2) - m(2, 1)) * s;\n\t\t\tq.y() = (m(2, 0) - m(0, 2)) * s;\n\t\t\tq.z() = (m(0, 1) - m(1, 0)) * s;\n\t\t\tq.w() = 0.25f / s;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((m(0, 0) > m(1, 1)) && (m(0, 0) > m(2, 2)))\n\t\t\t{\n\t\t\t\tfloat s = sqrtf(1.f + m(0, 0) - m(1, 1) - m(2, 2)) * 2.f;\n\t\t\t\tq.x() = 0.5f / s;\n\t\t\t\tq.y() = (m(1, 0) + m(0, 1)) / s;\n\t\t\t\tq.z() = (m(2, 0) + m(0, 2)) / s;\n\t\t\t\tq.w() = (m(2, 1) + m(1, 2)) / s;\n\t\t\t}\n\t\t\telse if (m(1, 1) > m(2, 2))\n\t\t\t{\n\t\t\t\tfloat s = sqrtf(1.f - m(0, 0) + m(1, 1) - m(2, 2)) * 2.f;\n\t\t\t\tq.x() = (m(1, 0) + m(0, 1)) / s;\n\t\t\t\tq.y() = 0.5f / s;\n\t\t\t\tq.z() = (m(2, 1) + m(1, 2)) / s;\n\t\t\t\tq.w() = (m(2, 0) + m(0, 2)) / s;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloat s = sqrtf(1.f - m(0, 0) - m(1, 1) + m(2, 2)) * 2.f;\n\t\t\t\tq.x() = (m(2, 0) + m(0, 2)) / s;\n\t\t\t\tq.y() = (m(2, 1) + m(1, 2)) / s;\n\t\t\t\tq.z() = 0.5f / s;\n\t\t\t\tq.w() = (m(1, 0) + m(0, 1)) / s;\n\t\t\t}\n\t\t}\n\t\treturn q;\n\t}\n\n\t/** Build a quaternion from a rotation matrix\n\t *\\param m the rotation matrix\n\t *\\return the quaternion\n\t */\n\ttemplate <typename T, int Options>\n\tEigen::Quaternion<T, 0>\tquatFromMatrix( const Eigen::Matrix<T, 4,4, Options, 4,4>& m ) {\n\t\tEigen::Quaternion<T, 0> q;\n\t\tfloat trace = m(0, 0) + m(1, 1) + m(2, 2) + 1.f;\n\t\tif (trace > 0)\n\t\t{\n\t\t\tfloat s = 0.5f / sqrtf(trace);\n\t\t\tq.x() = (m(1, 2) - m(2, 1)) * s;\n\t\t\tq.y() = (m(2, 0) - m(0, 2)) * s;\n\t\t\tq.z() = (m(0, 1) - m(1, 0)) * s;\n\t\t\tq.w() = 0.25f / s;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((m(0, 0) > m(1, 1)) && (m(0, 0) > m(2, 2)))\n\t\t\t{\n\t\t\t\tfloat s = sqrtf(1.f + m(0, 0) - m(1, 1) - m(2, 2)) * 2.f;\n\t\t\t\tq.x() = 0.5f / s;\n\t\t\t\tq.y() = (m(1, 0) + m(0, 1)) / s;\n\t\t\t\tq.z() = (m(2, 0) + m(0, 2)) / s;\n\t\t\t\tq.w() = (m(2, 1) + m(1, 2)) / s;\n\t\t\t}\n\t\t\telse if (m(1, 1) > m(2, 2))\n\t\t\t{\n\t\t\t\tfloat s = sqrtf(1.f - m(0, 0) + m(1, 1) - m(2, 2)) * 2.f;\n\t\t\t\tq.x() = (m(1, 0) + m(0, 1)) / s;\n\t\t\t\tq.y() = 0.5f / s;\n\t\t\t\tq.z() = (m(2, 1) + m(1, 2)) / s;\n\t\t\t\tq.w() = (m(2, 0) + m(0, 2)) / s;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloat s = sqrtf(1.f - m(0, 0) - m(1, 1) + m(2, 2)) * 2.f;\n\t\t\t\tq.x() = (m(2, 0) + m(0, 2)) / s;\n\t\t\t\tq.y() = (m(2, 1) + m(1, 2)) / s;\n\t\t\t\tq.z() = 0.5f / s;\n\t\t\t\tq.w() = (m(1, 0) + m(0, 1)) / s;\n\t\t\t}\n\t\t}\n\t\treturn q;\n\t}\n\n\t/** Build a quaternion from rotation euler angles.\n\t *\\param deg the rotation angles\n\t *\\return the quaternion\n\t *\\todo Explicit the angles order (yaw, pitch, roll?)\n\t */\n\ttemplate <typename T, int Options>\n\tEigen::Quaternion<T, 0>\tquatFromEulerAngles( const Eigen::Matrix<T, 3, 1,Options>& deg ) {\n\t\tVector3f v(SIBR_DEGTORAD(deg.x()), SIBR_DEGTORAD(deg.y()), SIBR_DEGTORAD(deg.z()));\n\t\tVector3f halfAngles( v.x() * 0.5f, v.y() * 0.5f, v.z() * 0.5f );\n\n\t\tconst float cx = cosf (halfAngles.x());\n\t\tconst float sx = sinf (halfAngles.x());\n\t\tconst float cy = cosf (halfAngles.y());\n\t\tconst float sy = sinf (halfAngles.y());\n\t\tconst float cz = cosf (halfAngles.z());\n\t\tconst float sz = sinf (halfAngles.z());\n\n\t\tconst float cxcz = cx*cz;\n\t\tconst float cxsz = cx*sz;\n\t\tconst float sxcz = sx*cz;\n\t\tconst float sxsz = sx*sz;\n\n\t\tEigen::Quaternion<T, 0> dst;\n\t\tdst.vec().x() = (cy * sxcz) - (sy * cxsz);\n\t\tdst.vec().y() = (cy * sxsz) + (sy * cxcz);\n\t\tdst.vec().z() = (cy * cxsz) - (sy * sxcz);\n\t\tdst.w() = (cy * cxcz) + (sy * sxsz);\n\t\treturn dst;\n\t}\n\n\t/** Rotate a vector using a quaternion.\n\t *\\param rotation the quaternion\n\t *\\param vec the vector\n\t *\\return the rotated vector.\n\t */\n\ttemplate <typename T, int Options>\n\tEigen::Matrix<T, 3, 1, Options>\tquatRotateVec(\n\t\tconst Eigen::Quaternion<T, 0>& rotation, const Eigen::Matrix<T, 3, 1, Options>& vec ) {\n\t\treturn rotation._transformVector(vec);\n\t}\n\n\t/** Quaternion product.\n\t * \\param q1 first quaternion\n\t * \\param q2 second quaternion\n\t * \\return the result quaternion\n\t */\n\ttemplate <typename T>\n\tinline static Eigen::Quaternion<T> dot( const Eigen::Quaternion<T>& q1, const Eigen::Quaternion<T>& q2 ) {\n\t\treturn q1.vec().dot(q2.vec()) + q1.w()*q2.w();\n\t}\n\n\t/** Compute the delta angle between two quaternions.\n\t *\\param q1 first quaternion\n\t *\\param q2 second quaternion\n\t *\\return the angle in radians\n\t *\\note Will return the smallest angle possible\n\t */\n\ttemplate <typename T>\n\tinline static float\t\tangleRadian( const Eigen::Quaternion<T>& q1, const Eigen::Quaternion<T>& q2 ) {\n\t\tconst float mid = 3.14159f;\n\t\tconst float angle = q1.angularDistance(q2);\n\t\treturn angle > mid? mid-angle : angle; // be sure to return the shortest angle\n\t}\n\n    /** Linear quaternion interpolation\n     *\\param q1 first quaternion\n\t *\\param q2 second quaternion\n\t *\\param t interpolation factor\n\t *\\return the interpolated quaternion\n\t */\n\ttemplate <typename T>\n    inline static Eigen::Quaternion<T> lerp( const Eigen::Quaternion<T>& q1, const Eigen::Quaternion<T>& q2, float t ) {\n\t\treturn (q1*(1-t) + q2*t).normalized();\n\t}\n\n\t/** Spherical quaternion interpolation\n\t*\\param q1 first quaternion\n\t*\\param q2 second quaternion\n\t*\\param t interpolation factor\n\t*\\return the interpolated quaternion\n\t*/\n\ttemplate <typename T>\n\tstatic Eigen::Quaternion<T> slerp( const Eigen::Quaternion<T>& q1, const Eigen::Quaternion<T>& q2, float t ) {\n\t\tEigen::Quaternion<T> q3;\n\t\tfloat dot = q1.dot(q2);// Eigen::Quaternion<T>::dot(q1, q2);\n\t\t// dot = cos(theta)\n\t\t// \t if (dot < 0), q1 and q2 are more than 90 degrees apart,\n\t\t// \t so we can invert one to reduce spinning\n\t\tif (dot < 0)\n\t\t{\n\t\t\tdot = -dot;\n\t\t\tq3 = -q2;\n\t\t} else q3 = q2;\n\t\tif (dot < 0.95f)\n\t\t{\n\t\t\tfloat angle = acosf(dot);\n\t\t\treturn (q1*sinf(angle*(1-t)) + q3*sinf(angle*t))/sinf(angle);\n\t\t} else // if the angle is small, use linear interpolation\n\t\t\treturn lerp(q1,q3,t);\n\t}\n\n\ttypedef\tEigen::Quaternion<unsigned>\t\tQuaternionu;\n\ttypedef\tEigen::Quaternion<int>\t\t\tQuaternioni;\n\ttypedef\tEigen::Quaternion<float>\t\tQuaternionf;\n\ttypedef\tEigen::Quaternion<double>\t\tQuaterniond;\n\n\t/** }@ */\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Rect.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/system/Config.hpp\"\r\n# include \"core/system/Vector.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\t/** Represents a simple aligned axis rectangle\r\n\t\r\n\tI use left, top, right, bottom and not x1, x2, y1, y2\r\n\tor x, y, w, h for being free of coordinate systems.\r\n\t(so you can use this class with an origin starting in\r\n\tthe top left corner or the bottom right corner; this\r\n\tshould be the same).\r\n\t\r\n\tNote this class is currently what I call a constclass:\r\n\tonce built you cannot modify it.\r\n\t* \\ingroup sibr_system\r\n\t*/\r\n\ttemplate <typename T>\r\n\tclass Rect\r\n\t{\r\n\tpublic:\r\n\t\ttypedef\tEigen::Matrix<T, 2, 1,Eigen::DontAlign>\t\t\tVec;\r\n\r\n\tpublic:\r\n\r\n\t\t/// Default constructor (null everything)\r\n\t\tRect( void );\r\n\t\t\r\n\t\t/** Constructor\r\n\t\t \\param left x-coordinate of the left border\r\n\t\t \\param top y-coordinate of the top border\r\n\t\t \\param right x-coordinate of the right border\r\n\t\t \\param bottom y-coordinate of the bottom border\r\n\t\t*/\r\n\t\tRect( T left, T top, T right, T bottom );\r\n\r\n\t\t/// \\return the position of the left side\r\n\t\tinline T\t\tleft( void ) const;\r\n\t\t/// \\return the position of the right side\r\n\t\tinline T\t\tright( void ) const;\r\n\t\t/// \\return the position of the top side\r\n\t\tinline T\t\ttop( void ) const;\r\n\t\t/// \\return the position of the bottom side\r\n\t\tinline T\t\tbottom( void ) const;\r\n\r\n\t\t/// \\return the width\r\n\t\tinline T\t\twidth( void ) const;\r\n\t\t/// \\return the height\r\n\t\tinline T\t\theight( void ) const;\r\n\r\n\t\t/// \\return the top left rectangle corner location.\r\n\t\tinline Eigen::Matrix<T, 2, 1,Eigen::DontAlign>\tcornerLeftTop( void ) const;\r\n\t\t/// \\return the bottom left rectangle corner location.\r\n\t\tinline Eigen::Matrix<T, 2, 1,Eigen::DontAlign>\tcornerLeftBottom( void ) const;\r\n\t\t/// \\return the bottom right rectangle corner location.\r\n\t\tinline Eigen::Matrix<T, 2, 1,Eigen::DontAlign>\tcornerRightBottom( void ) const;\r\n\t\t/// \\return the top right rectangle corner location.\r\n\t\tinline Eigen::Matrix<T, 2, 1,Eigen::DontAlign>\tcornerRightTop( void ) const;\r\n\r\n\tprivate:\r\n\t\tT\t_left;\t\t///< x-coordinate of the left border\r\n\t\tT\t_top;\t\t///< y-coordinate of the top border\r\n\t\tT\t_right;\t\t///< x-coordinate of the right border\r\n\t\tT\t_bottom;\t///< y-coordinate of the bottom border\r\n\t};\r\n\r\n\t///// EXPORT DEFAULT TYPES /////\r\n\r\n\ttypedef Rect<float>\t\tRectf;\r\n\ttypedef Rect<int>\t\tRecti;\r\n\r\n\t///// DEFINITION /////\r\n\r\n\ttemplate <typename T>\r\n\tRect<T>::Rect( void )\r\n\t: _left(T(0)), _top(T(0)), _right(T(0)), _bottom(T(0)) {\r\n\t}\r\n\ttemplate <typename T>\r\n\tRect<T>::Rect( T left, T top, T right, T bottom )\r\n\t: _left(left), _top(top), _right(right), _bottom(bottom) {\r\n\t}\r\n\r\n\ttemplate <typename T>\r\n\tT\t\tRect<T>::left( void ) const {\r\n\t\treturn _left;\r\n\t}\r\n\ttemplate <typename T>\r\n\tT\t\tRect<T>::right( void ) const {\r\n\t\treturn _right;\r\n\t}\r\n\ttemplate <typename T>\r\n\tT\t\tRect<T>::top( void ) const {\r\n\t\treturn _top;\r\n\t}\r\n\ttemplate <typename T>\r\n\tT\t\tRect<T>::bottom( void ) const {\r\n\t\treturn _bottom;\r\n\t}\r\n\r\n\ttemplate <typename T>\r\n\tT\t\tRect<T>::width( void ) const {\r\n\t\tT v = _right - _left;\r\n\t\treturn (v<0.f)? -v : v;\r\n\t}\r\n\ttemplate <typename T>\r\n\tT\t\tRect<T>::height( void ) const {\r\n\t\tT v = _top - _bottom;\r\n\t\treturn (v<0.f)? -v : v;\r\n\t}\r\n\r\n\ttemplate <typename T>\r\n\tEigen::Matrix<T, 2, 1,Eigen::DontAlign>\tRect<T>::cornerLeftTop( void ) const {\r\n\t\treturn Eigen::Matrix<T, 2, 1,Eigen::DontAlign>(left(),top());\r\n\t}\r\n\ttemplate <typename T>\r\n\tEigen::Matrix<T, 2, 1,Eigen::DontAlign>\tRect<T>::cornerLeftBottom( void ) const {\r\n\t\treturn Eigen::Matrix<T, 2, 1,Eigen::DontAlign>(left(),bottom());\r\n\t}\r\n\ttemplate <typename T>\r\n\tEigen::Matrix<T, 2, 1,Eigen::DontAlign>\tRect<T>::cornerRightBottom( void ) const {\r\n\t\treturn Eigen::Matrix<T, 2, 1,Eigen::DontAlign>(right(),bottom());\r\n\t}\r\n\ttemplate <typename T>\r\n\tEigen::Matrix<T, 2, 1,Eigen::DontAlign>\tRect<T>::cornerRightTop( void ) const {\r\n\t\treturn Eigen::Matrix<T, 2, 1,Eigen::DontAlign>(right(),top());\r\n\t}\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/SimpleTimer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/system/Config.hpp\"\n#include <vector>\n#include <chrono>\n\nnamespace sibr\n{\n\t/**\n\t* Timer to monitor performance of a section of code.\n\t* \\ingroup sibr_system\n\t*/\n\tclass Timer\n\t{\n\tpublic:\n\t\ttypedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point;\n\t\ttypedef std::chrono::nanoseconds nano;\n\t\ttypedef std::chrono::microseconds micro;\n\t\ttypedef std::chrono::milliseconds milli;\n\t\ttypedef std::chrono::seconds s;\n\n\t\tconst double timeResolution = (double)std::chrono::high_resolution_clock::period::num\n\t\t\t/ std::chrono::high_resolution_clock::period::den; ///< Timer resolution.\n\n\t\t/** Constructor. Create a timer.\n\t\t\\param start_now start to measure time at creation\n\t\t*/\n\t\tTimer(bool start_now = false ) : hasStarted(false)\n\t\t{\n\t\t\tif (start_now) {\n\t\t\t\ttic();\n\t\t\t}\n\t\t}\n\n\t\t/** Copy constructor\n\t\t\\param timer another timer\n\t\t*/\n\t\tTimer(const Timer & timer) {\n\t\t\thasStarted = timer.hasStarted;\n\t\t\tcurrent_tic = timer.current_tic;\n\t\t}\n\n\t\t/** Start measuring elapsed time.\n\t\t * \\warning This will clear existing recorded times.\n\t\t*/\n\t\tvoid tic()\n\t\t{\n\t\t\ttocs.resize(0);\n\t\t\thasStarted = true;\n\t\t\tcurrent_tic = std::chrono::high_resolution_clock::now();\n\t\t}\n\n\t\t/** Save currently elapsed time.\n\t\t * \\note You can call toc multiple times in a row.\n\t\t*/\n\t\tvoid toc()\n\t\t{\n\t\t\tauto toc = std::chrono::high_resolution_clock::now();\n\t\t\ttocs.push_back(toc);\n\t\t}\n\n\t\t/** Get the time elapsed since the last tic, with a precisiond etemrined by the tempalte argument.\n\t\t\t\\return the measured time (default: in ms)\n\t\t*/\n\t\ttemplate<typename T = Timer::milli>\n\t\tdouble deltaTimeFromLastTic() const\n\t\t{\n\t\t\tif (!hasStarted) { return std::numeric_limits<double>::max(); }\n\t\t\tauto toc = std::chrono::high_resolution_clock::now();\n\t\t\t\n\t\t\tdouble deltaTime = 1;\n\t\t\tif (!getDeltaTime<T>(current_tic, toc, deltaTime)) {\n\t\t\t\tstd::cout << \"[SIBR - Timer] : below time reslution \" << std::endl;\n\t\t\t}\n\n\t\t\treturn deltaTime;\n\t\t}\n\n\t\t/** Print a list of all the recorded tocs, with the precision specified as a template argument (by default in ms).\n\t\t\t\\param toc_now should a toc be generated right now.\n\t\t*/\n\t\ttemplate<typename T = Timer::milli>\n\t\tvoid display(bool toc_now = false)\n\t\t{\n\t\t\tif (toc_now) {\n\t\t\t\ttoc();\n\t\t\t}\n\t\t\tconst int n = (int)tocs.size();\n\t\t\tif (!hasStarted || n == 0) {\n\t\t\t\tstd::cout << \"[SIBR - Timer] : no tic or no toc\" << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdouble deltaTime;\n\t\t\t\tfor (auto & toc : tocs) {\n\t\t\t\t\tif (getDeltaTime<T>(current_tic,toc,deltaTime) ) {\n\t\t\t\t\t\tstd::cout << \"[SIBR - Timer] : \" << deltaTime << std::endl;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tstd::cout << \"[SIBR - Timer] : below time reslution \" << std::endl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/** Get the time elapsed between two points in time, using the precision specified as a template argument (default to ms).\n\t\t\\param tic first time point\n\t\t\\param toc second time point\n\t\t\\param deltaTime will contain the computed duration\n\t\t\\return false if the elapsed time was below the timer precision.\n\t\t*/\n\t\ttemplate<typename T = Timer::milli>\n\t\tbool getDeltaTime(const time_point & tic, const time_point & toc, double & deltaTime) const {\n\t\t\tdouble timediff_nanoSeconds = (double)std::chrono::duration_cast<Timer::nano>(toc - tic).count();\n\t\t\tif (timediff_nanoSeconds < Timer::timeResolution) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdeltaTime = (double)std::chrono::duration_cast<T>(toc - tic).count();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\tprivate:\n\t\ttime_point current_tic; ///< Initial tic.\n\t\tstd::vector<time_point> tocs; ///< Recorded time points.\n\t\tbool hasStarted; ///< Is the timer currently running.\n\t};\n\t\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/String.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include \"core/system/String.hpp\"\r\n#include <cstdarg>\r\n#include <chrono>\r\n#include <iomanip>\r\n\r\nnamespace sibr\r\n{\r\n\tstd::string strSearchAndReplace( const std::string& src, const std::string& search, const std::string& replaceby )\r\n\t{\r\n\t\tsize_t pos = src.find(search);\r\n\t\tif (pos != std::string::npos)\r\n\t\t{\r\n\t\t\tstd::string out;\r\n\t\t\tout = src.substr(0, pos) + replaceby + src.substr(pos+search.size(), src.size()-pos+search.size());\r\n\t\t\treturn out;\r\n\t\t}\r\n\t\treturn src;\r\n\t}\r\n\r\n\tstd::string removeExtension(const std::string & str)\r\n\t{\r\n\t\treturn  str.substr(0, str.find_last_of('.'));\r\n\t}\r\n\r\n\tstd::string getExtension(const std::string & str)\r\n\t{\r\n\t\tconst std::string::size_type dotPos = str.find_last_of('.');\r\n\t\tif(dotPos == std::string::npos) {\r\n\t\t\treturn \"\";\r\n\t\t}\r\n\t\treturn str.substr(dotPos+1);\r\n\t}\r\n\r\n\tstd::string parentDirectory(const std::string & str)\r\n\t{\r\n\t\tconst char kPathSeparator =\r\n#ifdef _WIN32\r\n\t\t\t\t'\\\\';\r\n#else\r\n\t\t\t\t'/';\r\n#endif\r\n\t\tconst std::string::size_type pos = str.find_last_of(\"/\\\\\");\r\n\t\t// If no separator, return empty path.\r\n\t\tif(pos == std::string::npos) {\r\n\t\t\treturn str + kPathSeparator + \"..\";\r\n\t\t}\r\n\t\t// If the separator is not trailing, we are done. \r\n\t\tif(pos < str.size()-1) {\r\n\t\t\treturn str.substr(0, pos);\r\n\t\t}\r\n\t\t// Else we have to look for the previous one.\r\n\t\tconst std::string::size_type pos1 = str.find_last_of(\"/\\\\\", pos-1);\r\n\t\treturn str.substr(0, pos1);\r\n\t}\r\n\r\n\tSIBR_SYSTEM_EXPORT std::string getFileName(const std::string & str)\r\n\t{\r\n\t\tconst std::string::size_type pos = str.find_last_of(\"/\\\\\");\r\n\t\tif (pos == std::string::npos) {\r\n\t\t\treturn str;\r\n\t\t}\r\n\t\treturn str.substr(pos+1);\r\n\t}\r\n\r\n\tbool strContainsOnlyDigits(const std::string& str)\r\n\t{\r\n\t\tfor (char c : str)\r\n\t\t\tif (c < '0' || c > '9')\r\n\t\t\t\treturn false;\r\n\t\treturn true;\r\n\t}\r\n\r\n\tstd::vector<std::string>\tsplit(const std::string& str, char delim)\r\n\t{\r\n\t\tstd::stringstream\tss(str);\r\n\t\tstd::string\t\t\tto;\r\n\t\tstd::vector<std::string>\tout;\r\n\r\n\t\tif (str.empty())\r\n\t\t\treturn out;\r\n\r\n\t\twhile (std::getline(ss, to, delim))\r\n\t\t\tout.push_back(to);\r\n\t\treturn out;\r\n\t}\r\n\r\n\t/// Wrapper around sibr::sprintf that returns a string\r\n\tstd::string sprint(const char *msg, ...)\r\n\t{\r\n#define TEMP_STR_SIZE 4096\r\n\t\tva_list args;\r\n\t\tva_start(args, msg);\r\n\t\tchar s_StrSingle[TEMP_STR_SIZE];\r\n#ifdef WIN32\r\n\t\tvsprintf_s(s_StrSingle, TEMP_STR_SIZE, msg, args);\r\n#else\r\n\t\tvsnprintf(s_StrSingle, TEMP_STR_SIZE, msg, args);\r\n#endif\r\n\t\tva_end(args);\r\n\t\treturn std::string(s_StrSingle);\r\n#undef TEMP_STR_SIZE\r\n\t}\r\n\r\n\tint \t\tsprintf(char* buffer, size_t size, const char* format, ...)\r\n\t{\r\n\t\tva_list args;\r\n\t\tint ret = 0;\r\n\t\tva_start(args, format);\r\n#ifdef WIN32\r\n\t\tret = vsprintf_s(buffer, size, format, args);\r\n#else\r\n\t\tret = vsnprintf(buffer, size, format, args);\r\n#endif\r\n\t\tva_end(args);\r\n\t\treturn ret;\r\n\t}\r\n\r\n\tSIBR_SYSTEM_EXPORT std::string to_lower(const std::string& str)\r\n\t{\r\n\t\tstd::string out;\r\n\t\tout.reserve(str.length());\r\n\r\n\t\tfor (size_t i = 0; i < str.length(); ++i)\r\n\t\t\tout.push_back(tolower(str[i]));\r\n\r\n\t\treturn out;\r\n\t}\r\n\r\n\r\n\tSIBR_SYSTEM_EXPORT bool find_any(const std::vector<std::string>& needles, const std::string& haystack)\r\n\t{\r\n\t\tfor (std::string needle : needles)\r\n\t\t{\r\n\t\t\tif (haystack.find(needle) != std::string::npos)\r\n\t\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tstd::string timestamp(const std::string & format) {\r\n\t\tauto now = std::time(nullptr);\r\n#ifdef SIBR_OS_WINDOWS\r\n\t\ttm ltm = { 0,0,0,0,0,0,0,0,0 };\r\n\t\tlocaltime_s(&ltm, &now);\r\n#else\r\n\t\ttm ltm = *(std::localtime(&now));\r\n#endif\r\n\t\tstd::stringstream buffer;\r\n\t\tbuffer << std::put_time(&ltm, format.c_str());\r\n\t\treturn buffer.str();\r\n\t}\r\n\r\n} // namespace sirb\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/String.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/system/Config.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/**\r\n\t* \\addtogroup sibr_system\r\n\t* @{\r\n\t*/\r\n\r\n\t/**\r\n\t* Replaces all occurences of a substring with another substring.\r\n\t* \\param src the string to perform replacements in\r\n\t* \\param search the substring to replace\r\n\t* \\param replaceby the new substring to substitute\r\n\t* \\return the string with the substitutions performed.\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT std::string strSearchAndReplace( const std::string& src, const std::string& search, const std::string& replaceby );\r\n\r\n\t/**\r\n\t* Process a string (a filename or path) to remove any extension if it exists.\r\n\t* \\param str the string to remove the extension from\r\n\t* \\return the string without extension\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT std::string removeExtension(const std::string& str);\r\n\r\n\t/**\r\n\t* Process a string (a filename or path) to extract the file extension if it exists.\r\n\t* \\param str the string to get the extension from\r\n\t* \\return the extension string (without the leading dot)\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT std::string getExtension(const std::string& str);\r\n\r\n\t/**\r\n\t* Process a string (a path) to return the parent directory.\r\n\t* \\param str the string to process\r\n\t* \\return the string with the last component removed\r\n\t* \\note Will return the empty string if no separator was found.\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT std::string parentDirectory(const std::string& str);\r\n\r\n\t/**\r\n\t* Process a string (a path) to return the file name.\r\n\t* \\param str the string to process\r\n\t* \\return the string with all but the last component removed\r\n\t* \\note Will return the full string if no separator was found.\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT std::string getFileName(const std::string& str);\r\n\r\n\t/**\r\n\t* Check if a string only contains digits.\r\n\t* \\param str the string to check\r\n\t* \\return true if it only contains digits\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT bool strContainsOnlyDigits(const std::string& str);\r\n\r\n\t/** Split string into sub-strings delimited by a given character. \r\n\t * \\param str the input string\r\n\t * \\param delim the delimiting characters\r\n\t * \\return a list of split substrings\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT std::vector<std::string>\tsplit(const std::string& str, char delim = '\\n');\r\n\r\n\t/** Wrapper around sibr::sprintf that returns a string \r\n\t * \\param msg the string with C placeholders\r\n\t * \\param ... the values for each placeholder\r\n\t * \\return the string with the formatted values inserted\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT std::string\tsprint(const char *msg, ...);\r\n\r\n\t/** Write a formatted string with inserted values to a buffer.\r\n\t * \\param buffer the destination string\r\n\t * \\param size the size of the format string\r\n\t * \\param format the string with C placeholders\r\n\t * \\param ... the values for each placeholder\r\n\t * \\return a status code similar to sprintf\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT int \t\tsprintf(char* buffer, size_t size, const char* format, ...);\r\n\r\n\t/** Convert the input string to lowert case.\r\n\t * \\param str the input string\r\n\t * \\return the input string in lower case\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT std::string\t\t\t\t\tto_lower(const std::string& str);\r\n\r\n\t/** Find if a list of substring is present in a given string.\r\n\t * \\param needles the list of substring\r\n\t * \\param haystack the search string\r\n\t * \\return true if any substring is present in the search string, else false\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT bool\t\t\t\t\tfind_any(const std::vector<std::string>& needles, const std::string& haystack);\r\n\r\n\t/** Write the current timestamp to a string.\r\n\t * \\param format the formatting to use for the timestamp (see default value for an example)\r\n\t * \\return a string containing the timestamp\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT std::string timestamp(const std::string & format = \"%Y_%m_%d_%H_%M_%S\");\r\n\r\n\r\n\t/*** @} */\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/ThreadIdWorker.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/system/ThreadIdWorker.hpp\"\n\nnamespace sibr\n{\n\t/*static*/ std::mutex\tThreadIdWorker::g_mutex;\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/ThreadIdWorker.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <atomic>\n# include <thread>\n# include <mutex>\n# include <queue>\n\n# include \"core/system/Config.hpp\"\n\nnamespace sibr\n{\n\t/** Class used to work concurrently on multiple tasks/instructions.\n\t The only shared object is a queue (TaskIds) that\n\t contains ids of remaining tasks to perform.\n\t\n\t Typically, you use this id to access a const array\n\t (input) and write results to another array using once\n\t again this id. The output array is already resized\n\t at the begin so that you can freely modify its\n\t element without hurting other threads.\n\t\n\tCode Example:\n\n\t\tstd::vector<ThreadIdWorker>\tworkers(MASKPATCH_NBTHREADS);\n\n\t\t// Launch all threads\n\t\tfor (ThreadIdWorker& t: workers)\n\t\tt = std::move(ThreadIdWorker(taskId, workFunc));\n\n\t\t// Wait for all threads\n\t\tfor (ThreadIdWorker& t: workers)\n\t\tif (t.joinable())\n\t\tt.join();\n\n\t \\ingroup sibr_system\n\t*/\n\tclass /*SIBR_SYSTEM_EXPORT*/ ThreadIdWorker : public std::thread\n\t{\n\tpublic:\n\t\ttypedef\tstd::queue<uint>\tTaskIds;\n\tpublic:\n\t\t/// Build an empty worker (placeholder)\n\t\tThreadIdWorker( void );\n\n\t\t/** Move constructor\n\t\t *\\param other worker to move\n\t\t */\n\t\tThreadIdWorker( ThreadIdWorker&& other ) noexcept;\n\n\t\t/** Constructor. Will call the passed function for each given task ID.\n\t\t \\param ids a list of task ids\n\t\t \\param func a function receiving a task ID as parameter returning either FALSE for signaling the worker to stop or TRUE for keep going.\n\t\t*/\n\t\tThreadIdWorker( TaskIds& ids, std::function<bool(uint)> func );\n\n\t\t/** Move operator.\n\t\t *\\param other worker to assign\n\t\t *\\return the current worker\n\t\t */\n\t\tThreadIdWorker& operator =( ThreadIdWorker&& other ) noexcept;\n\n\t\t/// Deleted copy operator.\n\t\tThreadIdWorker(const ThreadIdWorker&) = delete;\n\n\tprivate:\n\n\t\t/** Will pull the next task or automatically stop.\n\t\t \\param ids a list of task ids\n\t\t \\param func a function receiving a task ID as parameter\n\t\t */\n\t\tvoid\t\ttaskPuller( TaskIds& ids, std::function<bool(uint)> func );\n\n\t\tSIBR_SYSTEM_EXPORT static std::mutex\tg_mutex;\t///< used to protect the common shared TaskIds list\n\t};\n\n\t///// INLINES /////\n\tinline ThreadIdWorker::ThreadIdWorker( void ) {\n\t}\n\n\tinline ThreadIdWorker::ThreadIdWorker( ThreadIdWorker&& other ) noexcept :\n\t\tstd::thread(std::move((std::thread&)other)) {\n\t}\n\n\tinline ThreadIdWorker::ThreadIdWorker( TaskIds& ids, std::function<bool(uint)> func )\n\t\t: std::thread( [this, &ids, &func]() { taskPuller(ids, std::move(func)); } ) {\n\t}\n\n\tinline ThreadIdWorker& ThreadIdWorker::operator =( ThreadIdWorker&& other ) noexcept {\n\t\t((std::thread*)this)->operator=(std::move(other)); return *this;\n\t}\n\n\tinline void ThreadIdWorker::taskPuller( TaskIds& ids, std::function<bool(uint)> func ) {\n\t\tuint id = 0;\n\t\tbool stop = false;\n\t\twhile (!stop)\n\t\t{\n\t\t\t{ // Pop next id\n\t\t\t\tstd::lock_guard<std::mutex>\tlock(g_mutex);\n\t\t\t\tstop = ids.empty();\n\n\t\t\t\tif (!stop)\n\t\t\t\t{\n\t\t\t\t\tid = ids.front();\n\t\t\t\t\tids.pop();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!stop)\n\t\t\t\tstop = !func(id);\n\t\t}\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Transform3.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/system/ByteStream.hpp\"\n# include \"core/system/Config.hpp\"\n# include \"core/system/Matrix.hpp\"\n# include \"core/system/Vector.hpp\"\n# include \"core/system/Quaternion.hpp\"\n\n\nnamespace sibr\n{\n\t/**\n\t * Represent a 3D transformation composed of a rotation and translation.\n\t* \\ingroup sibr_system\n\t*/\n\ttemplate <typename T>\n\tclass Transform3\n\t{\n\tpublic:\n\t\ttypedef Eigen::Matrix<T,3, 1, Eigen::DontAlign>\t\tVector3;\n\t\ttypedef Eigen::Quaternion<T>\t\t\t\t\t\tQuaternion;\n\n\tpublic:\n\n\t\t/** Constructor: identity transform. */\n\t\tTransform3( void ) : _position(0, 0, 0), _scale(1., 1., 1.) {\n\t\t\t_rotation.setIdentity();\n\t\t}\n\n\t\t/** Set the transformation parameters.\n\t\t *\\param translation the translation vector\n\t\t *\\param rotation the rotation quaternion\n\t\t */\n\t\tvoid\t       set( const Vector3& translation, const Quaternion& rotation ) {\n\t\t\t\t_position = translation;\n\t\t\t\t_rotation = rotation;\n\t\t}\n\n\t\t/** Apply a translation.\n\t\t *\\param x x shift\n\t\t *\\param y y shift\n\t\t *\\param z z shift\n\t\t **/\n\t\tvoid\t\t\t\ttranslate( float x, float y, float z );\n\n\t\t/** Apply a translation that is itself rotated by another transformation.\n\t\t *\\param x x shift\n\t\t *\\param y y shift\n\t\t *\\param z z shift\n\t\t *\\param ref additional rotation trnasofrmation to apply to the translation vector.\n\t\t **/\n\t\tvoid\t\t\t\ttranslate( float x, float y, float z, const Transform3& ref);\n\n\t\t/** Apply a translation.\n\t\t *\\param v translation vector\n\t\t **/\n\t\tvoid\t\t\t\ttranslate( const Vector3& v );\n\t\t\n\t\t/** Apply a translation that is itself rotated by another transformation.\n\t\t *\\param v translation vector\n\t\t *\\param ref additional rotation trnasofrmation to apply to the translation vector.\n\t\t **/\n\t\tvoid\t\t\t\ttranslate( const Vector3& v, const Transform3& ref );\n\n\n\t\tvoid\t\t\t\tscale(const float& s);\n\t\t/** Set the position.\n\t\t *\\param x x position\n\t\t *\\param y y position\n\t\t *\\param z z position\n\t\t **/\n\t\tvoid\t\t\t\tposition( float x, float y, float z );\n\n\t\t/** Set the position.\n\t\t *\\param v position\n\t\t **/\n\t\tvoid\t\t\t\tposition( const Vector3& v );\n\n\t\t/** \\return the position */\n\t\tconst Vector3&\tposition( void ) const;\n\n\n\t\t/** Apply a rotation.\n\t\t *\\param rotation quaternion rotation\n\t\t */\n\t\tvoid\t\t\t\t\trotate( const Quaternion& rotation );\n\n\t\t/** Apply a rotation using Euler angles.\n\t\t *\\param x yaw\n\t\t *\\param y pitch\n\t\t *\\param z roll\n\t\t *\\todo Clarify the angles order.\n\t\t *\\sa quatFromEulerAngles\n\t\t */\n\t\tvoid\t\t\t\t\trotate( float x, float y, float z );\n\n\t\t/** Apply a rotation using Euler angles and composite with an additional transformation.\n\t\t *\\param x yaw\n\t\t *\\param y pitch\n\t\t *\\param z roll\n\t\t *\\param ref additional rotation\n\t\t *\\todo Clarify the angles order.\n\t\t *\\sa quatFromEulerAngles\n\t\t */\n\t\tvoid\t\t\t\t\trotate( float x, float y, float z,\n\t\t\t\t\t\t\t\t\t\t\tconst Transform3& ref);\n\n\t\t/** Apply a rotation using Euler angles.\n\t\t *\\param v angles\n\t\t *\\todo Clarify the angles order.\n\t\t *\\sa quatFromEulerAngles\n\t\t */\n\t\tvoid\t\t\t\t\trotate( const Vector3& v );\n\n\t\t/** Apply a rotation using Euler angles and composite with an additional transformation.\n\t\t *\\param v angles\n\t\t *\\param ref additional rotation\n\t\t *\\todo Clarify the angles order.\n\t\t *\\sa quatFromEulerAngles\n\t\t */\n\t\tvoid\t\t\t\t\trotate( const Vector3& v, const Transform3& ref );\n\n\t\t/** Set the rotation from Euler angles.\n\t\t *\\param x yaw\n\t\t *\\param y pitch\n\t\t *\\param z roll\n\t\t *\\todo Clarify the angles order.\n\t\t *\\sa quatFromEulerAngles\n\t\t */\n\t\tvoid\t\t\t\t\trotation( float x, float y, float z );\n\n\t\t/** Set the rotation from Euler angles.\n\t\t *\\param v angles\n\t\t *\\todo Clarify the angles order.\n\t\t *\\sa quatFromEulerAngles\n\t\t */\n\t\tvoid\t\t\t\t\trotation( const Vector3& v );\n\n\t\t/** Set the rotation.\n\t\t *\\param q quaternion rotation\n\t\t */\n\t\tvoid\t\t\t\t\trotation( const Quaternion& q );\n\n\t\t/// \\return the rotation\n\t\tconst Quaternion&\trotation( void ) const;\n\n\t\t/// \\return the transformation matrix\n\t\tMatrix4f\t\tmatrix( void ) const;\n\t\t/// \\return the inverse of the transformation matrix\n\t\tMatrix4f\t\tinvMatrix( void ) const;\n\n\t\t/** Interpolate between two transformations.\n\t\t *\\param from source transformation\n\t\t *\\param to destination transformation\n\t\t *\\param dist01 interpolation factor\n\t\t *\\return the interpolated transformation\n\t\t */\n\t\tstatic Transform3<T>\tinterpolate( const Transform3<T>& from, const Transform3<T>& to, float dist01 ) {\n\t\t\tdist01 = std::max(0.f, std::min(1.f, dist01)); // clamp\n\t\t\t\n\t\t\tTransform3<T> out;\n\t\t\tout.position((1.0f-dist01)*from.position() + dist01*to.position());\n\t\t\tout.rotation(from.rotation().slerp(dist01, to.rotation()));\n\t\t\treturn out;\n\t\t}\n\n\t\t/** Linearly extrapolate based on two transformations, by reapplying the delta between the two transformations to the current one \n\t\t * and interpolating between the current and the new estimate.\n\t\t *\\param previous source transformation\n\t\t *\\param current current transformation\n\t\t *\\param dist01 extrapolation factor\n\t\t *\\return the extrapolated transformation\n\t\t *\\note dist01 should still be in 0,1\n\t\t */\n\t\tstatic Transform3<T>\textrapolate(const Transform3<T>& previous, const Transform3<T>& current, float dist01) {\n\n\t\t\tVector3f deltaPosition = current.position() - previous.position();\n\t\t\tQuaternion deltaRotation = previous.rotation().inverse() * current.rotation();\n\n\t\t\tTransform3<T> t = current;\n\t\t\tt.rotate(deltaRotation);\n\t\t\tt.translate(deltaPosition);\n\t\t\treturn interpolate(current, t, dist01);\n\t\t}\n\n\t\t/** Compute a trnasformation made by compsoiting a parent and child transformations.\n\t\t * \\param parentTr the parent\n\t\t * \\param childTr the child\n\t\t * \\return the composite transformation\n\t\t */\n\t\tstatic Transform3<T>\tcomputeFinal( const Transform3<T>& parentTr, const Transform3<T>& childTr ) {\n\t\t\tTransform3<T>\t\tfinalTr;\n\t\t\tfinalTr.position(parentTr.position() + parentTr.rotation() * childTr.position());\n\t\t\tfinalTr.rotation(parentTr.rotation() * childTr.rotation());\n\t\t\treturn finalTr;\n\t\t}\n\n\t\t/** Equality operator with a 1e-3 tolerance.\n\t\t *\\param other transformation to test equality with\n\t\t *\\return true if other is equal\n\t\t */\n\t\tbool operator==(const Transform3 & other) const {\n\t\t\tstatic const float eps = 1e-3f;\n\t\t\treturn (_position-other._position).norm()/ _position.norm() < eps && std::abs(_rotation.dot(other._rotation)) > ( 1 - eps);\n\t\t}\n\n\t\t/** Difference operator.\n\t\t *\\param other transformation to test difference with\n\t\t *\\return true if other is different\n\t\t **/\n\t\tbool operator!=(const Transform3 & other) const {\n\t\t\treturn !(*this == other);\n\t\t}\n\n\tprivate:\n\t\tVector3\t\t    _position;\n\t\tQuaternion\t\t_rotation;\n\t\tVector3\t\t\t_scale;\n\t};\n\n\t/// Helper def.\n\ttypedef Transform3<float> Transform3f;\n\n\t/** Write transformation to a byte stream.\n\t *\\param stream the byte stream\n\t *\\param t the transform\n\t *\\return the stream for compositing\n\t \\ingroup sibr_system\n\t */\n\ttemplate <typename T>\n\tByteStream&\t\toperator << (ByteStream& stream, const Transform3<T>& t ) {\n\t\ttypename Transform3<T>::Vector3 v = t.position();\n\t\ttypename Transform3<T>::Quaternion q = t.rotation();\n\t\treturn stream\n\t\t\t<< v.x() << v.y() << v.z()\n\t\t\t<< q.x() << q.y() << q.z() << q.w();\n\t}\n\n\t/** Read transformation from a byte stream.\n\t *\\param stream the byte stream\n\t *\\param t the transform\n\t *\\return the stream for compositing\n\t \\ingroup sibr_system\n\t */\n\ttemplate <typename T>\n\tByteStream&\t\toperator >> (ByteStream& stream, Transform3<T>& t ) {\n\t\ttypename Transform3<T>::Vector3 v;\n\t\ttypename Transform3<T>::Quaternion q;\n\t\tstream\n\t\t\t>> v.x() >> v.y() >> v.z()\n\t\t\t>> q.x() >> q.y() >> q.z() >> q.w();\n\t\tt.position(v);\n\t\tt.rotation(q);\n\t\treturn stream;\n\t}\n\n\t//==================================================================//\n\t// Inlines\n\t//==================================================================//\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::translate( float x, float y, float z ) {\n\t\t_position.x() += x; _position.y() += y; _position.z() += z;\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::translate( float x, float y, float z,\n\t\tconst Transform3<T>& ref) {\n\t\t\ttranslate( Vector3( x, y, z ), ref );\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::translate( const Vector3& v ) {\n\t\t_position.x() += v.x(); _position.y() += v.y(); _position.z() += v.z();\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::translate( const Vector3& v, const Transform3& ref ) {\n\t\ttranslate( ref.rotation().operator*(v) );\n\t}\n\n\ttemplate<typename T>\n\tinline void Transform3<T>::scale(const float& s)\n\t{\n\t\t_scale = Vector3(s, s, s);\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::position( float x, float y, float z ) {\n\t\t_position.x() = x; _position.y() = y; _position.z() = z;\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::position( const Vector3& v ) {\n\t\t_position.x() = v.x(); _position.y() = v.y(); _position.z() = v.z();\n\t}\n\n\ttemplate <typename T>\n\tconst typename Transform3<T>::Vector3&\tTransform3<T>::position( void ) const {\n\t\treturn _position;\n\t}\n\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotate( const Quaternion& rotation ) {\n\t\t_rotation = rotation * _rotation;\n\t\t_rotation.normalize();\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotate( float x, float y, float z ) {\n\t\tQuaternion q = quatFromEulerAngles(Vector3(x, y, z));\n\t\tq.normalize();\n\t\trotate(q);\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotate( const Vector3& v ) {\n\t\trotate( v.x(), v.y(), v.z() );\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotate( const Vector3& v, const Transform3& ref ) {\n\t\trotate( v.x(), v.y(), v.z(), ref );\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotation( float x, float y, float z ) {\n\t\t_rotation = quatFromEulerAngles(Vector3(x, y, z));\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotation( const Vector3& v ) {\n\t\trotation( v.x(), v.y(), v.z() );\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotation( const Quaternion& q ) {\n\t\t_rotation = q;\n\t}\n\n\ttemplate <typename T>\n\tconst typename Transform3<T>::Quaternion&\tTransform3<T>::rotation( void ) const {\n\t\treturn _rotation;\n\t}\n\n\ttemplate <typename T>\n\tMatrix4f Transform3<T>::matrix( void ) const {\n\t\tMatrix4f trans = matFromQuat(_rotation);\n\t\tMatrix4f scaleMat = Matrix4f::Identity();\n\t\tscaleMat(0, 0) = _scale.x();\n\t\tscaleMat(1, 1) = _scale.y();\n\t\tscaleMat(2, 2) = _scale.z();\n\n\t\ttrans = matFromTranslation(_position) * trans * scaleMat; // Opti (direct)\n\n\t\treturn trans;\n\t}\n\n\ttemplate <typename T>\n\tMatrix4f Transform3<T>::invMatrix( void ) const {\n\t\t// This is wrapped so we can (in the future) add a policy class\n\t\t// to enable caching this inv matrix\n\t\treturn matrix().inverse();\n\t}\n\n\ttemplate <typename T>\n\tvoid\t\tTransform3<T>::rotate( float x, float y, float z,\n\t\tconst Transform3<T>& ref)\n\t{\n\t\tQuaternion q = quatFromEulerAngles(Vector3(x, y, z));\n\t\tq.normalize();\n\n\t\tif ( &ref == this ) // Local Rotation\n\t\t{\n\t\t\t_rotation = _rotation * q;\n\t\t\t_rotation.normalize();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQuaternion refConj = ref.rotation();\n\t\t\trefConj.conjugate();\n\n\t\t\t// 1) Apply global rotation of ref on 'q' (ref * q)\n\t\t\t// 2) Apply local rotation of ref.conj (~inv) on 'q' (q*ref.conj)\n\t\t\t// 3) The rotation is converted and can be applied using rotate\n\t\t\trotate((ref.rotation() * q) * refConj);\n\t\t}\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Utils.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include <boost/filesystem.hpp>\n#include <fstream>\n#include <sstream>\n#include <vector>\n#include \"core/system/Utils.hpp\"\n\n#ifdef SIBR_OS_WINDOWS \n\t#include <nfd.h>\n\t#include <Windows.h>\n\t#include <shlobj.h>\n\t#include <stdio.h>\n\t// Some old MinGW/CYGWIN distributions don't define this:\n\t#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING\n\t#define ENABLE_VIRTUAL_TERMINAL_PROCESSING  0x0004\n\t#endif\n#else\n\t#include <nfd.h>\n\t#include <libgen.h>\n\t#include <linux/limits.h>\n\t#include <unistd.h>\n\t#include <sys/types.h>\n\t#include <pwd.h>\n#endif\n\nnamespace sibr\n{\n#ifdef SIBR_OS_WINDOWS\n\t\tstatic HANDLE stdoutHandle;\n\t\tstatic DWORD outModeInit;\n\n\t\tvoid setupConsole(void) {\n\t\t\tDWORD outMode = 0;\n\t\t\tstdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);\n\n\t\t\tif(stdoutHandle == INVALID_HANDLE_VALUE) {\n\t\t\t\texit(GetLastError());\n\t\t\t}\n\t\t\t\n\t\t\tif(!GetConsoleMode(stdoutHandle, &outMode)) {\n\t\t\t\texit(GetLastError());\n\t\t\t}\n\n\t\t\toutModeInit = outMode;\n\t\t\t\n\t\t\t// Enable ANSI escape codes\n\t\t\toutMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;\n\n\t\t\tif(!SetConsoleMode(stdoutHandle, outMode)) {\n\t\t\t\texit(GetLastError());\n\t\t\t}\t\n\t\t}\n\n\t\tvoid restoreConsole(void) {\n\t\t\t// Reset colors\n\t\t\tprintf(\"\\x1b[0m\");\t\n\t\t\t\n\t\t\t// Reset console mode\n\t\t\tif(!SetConsoleMode(stdoutHandle, outModeInit)) {\n\t\t\t\texit(GetLastError());\n\t\t\t}\n\t\t}\n#endif\n\n\n\tstd::string\tloadFile(const std::string& fname)\n\t{\n\t\tstd::ifstream file(fname.c_str(), std::ios::binary);\n\t\tif (!file || !file.is_open()) {\n\t\t\tSIBR_ERR << \"File not found: \" << fname << std::endl;\n\t\t\treturn \"\";\n\t\t}\n\t\tfile.seekg(0, std::ios::end);\n\n\t\tstd::streampos length = file.tellg();\n\t\tfile.seekg(0, std::ios::beg);\n\n\t\tstd::vector<char> buffer(length);\n\t\tfile.read(&buffer[0], length);\n\t\tfile.close();\n\n\t\treturn std::string(buffer.begin(), buffer.end());\n\t}\n\n\tvoid\t\t\tmakeDirectory(const std::string& path)\n\t{\n\t\tboost::filesystem::path p(path);\n\t\tif (boost::filesystem::exists(p) == false)\n\t\t\tboost::filesystem::create_directories(p);\n\t}\n\n\tstd::vector<std::string> listFiles(const std::string & path, const bool listHidden, const bool includeSubdirectories, const std::vector<std::string> & allowedExtensions)\n\t{\n\t\tif (!directoryExists(path)) {\n\t\t\treturn {};\n\t\t}\n\n\t\tstd::vector<std::string> files;\n\t\tbool shouldCheckExtension = !allowedExtensions.empty();\n\n\t\ttry {\n\t\t\tboost::filesystem::directory_iterator end_iter;\n\t\t\tfor (boost::filesystem::directory_iterator dir_itr(path); dir_itr != end_iter; ++dir_itr) {\n\n\t\t\t\tconst std::string itemName = dir_itr->path().filename().string();\n\t\t\t\tif (includeSubdirectories && boost::filesystem::is_directory(dir_itr->status())) {\n\t\t\t\t\tif (listHidden || (itemName.size() > 0 && itemName.at(0) != '.')) {\n\t\t\t\t\t\tfiles.push_back(itemName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (boost::filesystem::is_regular_file(dir_itr->status())) {\n\t\t\t\t\tbool shouldKeep = !shouldCheckExtension;\n\t\t\t\t\tif (shouldCheckExtension) {\n\t\t\t\t\t\tfor (const auto & allowedExtension : allowedExtensions) {\n\t\t\t\t\t\t\tif (dir_itr->path().extension() == (\".\" + allowedExtension) || dir_itr->path().extension() == allowedExtension) {\n\t\t\t\t\t\t\t\tshouldKeep = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (shouldKeep && (listHidden || (itemName.size() > 0 && itemName.at(0) != '.'))) {\n\t\t\t\t\t\tfiles.push_back(itemName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (const boost::filesystem::filesystem_error&) {\n\t\t\tstd::cout << \"Can't access or find directory.\" << std::endl;\n\t\t}\n\n\t\tstd::sort(files.begin(), files.end());\n\n\t\treturn files;\n\t}\n\n\tstd::vector<std::string> listSubdirectories(const std::string & path, const bool listHidden)\n\t{\n\t\tif (!directoryExists(path)) {\n\t\t\treturn {};\n\t\t}\n\n\t\tstd::vector<std::string> dirs;\n\n\n\t\ttry {\n\t\t\tboost::filesystem::directory_iterator end_iter;\n\t\t\tfor (boost::filesystem::directory_iterator dir_itr(path); dir_itr != end_iter; ++dir_itr) {\n\n\t\t\t\tconst std::string itemName = dir_itr->path().filename().string();\n\t\t\t\tif (boost::filesystem::is_directory(dir_itr->status())) {\n\t\t\t\t\tif (listHidden || (itemName.size() > 0 && itemName.at(0) != '.')) {\n\t\t\t\t\t\tdirs.push_back(itemName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (const boost::filesystem::filesystem_error& ) {\n\t\t\tstd::cout << \"Can't access or find directory.\" << std::endl;\n\t\t}\n\n\t\tstd::sort(dirs.begin(), dirs.end());\n\n\t\treturn dirs;\n\t}\n\n\n\tbool copyDirectory(const std::string& src, const std::string& dst)\n\t{\n\t\tboost::filesystem::path source = src;\n\t\tboost::filesystem::path destination = dst;\n\t\tnamespace fs = boost::filesystem;\n\t\ttry\n\t\t{\n\t\t\t// Check whether the function call is valid\n\t\t\tif (!fs::exists(source) || !fs::is_directory(source))\n\t\t\t{\n\t\t\t\tstd::cerr << \"Source directory \" << source.string()\n\t\t\t\t\t<< \" does not exist or is not a directory.\" << '\\n'\n\t\t\t\t\t;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (fs::exists(destination))\n\t\t\t{\n\t\t\t\tstd::cerr << \"Destination directory \" << destination.string()\n\t\t\t\t\t<< \" already exists.\" << '\\n'\n\t\t\t\t\t;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Create the destination directory\n\t\t\tif (!fs::create_directory(destination))\n\t\t\t{\n\t\t\t\tstd::cerr << \"Unable to create destination directory\"\n\t\t\t\t\t<< destination.string() << '\\n'\n\t\t\t\t\t;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tcatch (fs::filesystem_error const & e)\n\t\t{\n\t\t\tstd::cerr << e.what() << '\\n';\n\t\t\treturn false;\n\t\t}\n\t\t// Iterate through the source directory\n\t\tfor (fs::directory_iterator file(source); file != fs::directory_iterator(); ++file)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfs::path current(file->path());\n\t\t\t\tif (fs::is_directory(current))\n\t\t\t\t{\n\t\t\t\t\t// Found directory: Recursion\n\t\t\t\t\tif (!copyDirectory(current.string(), (destination / current.filename()).string()))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Found file: Copy\n\t\t\t\t\tfs::copy_file(\n\t\t\t\t\t\tcurrent,\n\t\t\t\t\t\tdestination / current.filename()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (fs::filesystem_error const & e)\n\t\t\t{\n\t\t\t\tstd::cerr << e.what() << '\\n';\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tbool copyFile(const std::string & src, const std::string & dst, const bool overwrite)\n\t{\n\t\tboost::filesystem::path source = src;\n\t\tboost::filesystem::path destination = dst;\n\t\tnamespace fs = boost::filesystem;\n\t\ttry {\n\t\t\t// Check whether the function call is valid\n\t\t\tif (!fs::exists(source) || !fs::is_regular_file(source))\n\t\t\t{\n\t\t\t\tstd::cerr << \"Source file \" << source.string()\n\t\t\t\t\t<< \" does not exist or is not a regular file.\" << '\\n'\n\t\t\t\t\t;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// If the destination is a directory, we copy the file into this directory, with the same name.\n\t\t\tif (fs::is_directory(destination)) {\n\t\t\t\tdestination = destination / source.filename();\n\t\t\t}\n\n\t\t\tif (fs::exists(destination) && !overwrite)\n\t\t\t{\n\t\t\t\tstd::cerr << \"Destination file \" << destination.string()\n\t\t\t\t\t<< \" already exists.\" << '\\n'\n\t\t\t\t\t;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif(overwrite) {\n\t\t\t\tfs::copy_file(source, destination, boost::filesystem::copy_option::overwrite_if_exists);\n\t\t\t} else {\n\t\t\t\tfs::copy_file(source, destination);\n\t\t\t}\n\n\t\t}\n\t\tcatch (fs::filesystem_error const & e)\n\t\t{\n\t\t\tstd::cerr << e.what() << '\\n';\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tvoid\t\t\temptyDirectory(const std::string& path) {\n\t\tboost::filesystem::path p(path);\n\t\tfor (boost::filesystem::directory_iterator end_dir_it, it(p); it != end_dir_it; ++it) {\n\t\t\tboost::filesystem::remove_all(it->path());\n\t\t}\n\t}\n\n\tbool\t\t\tfileExists(const std::string& path)\n\t{\n\t\tboost::filesystem::path p(path);\n\t\treturn boost::filesystem::exists(p) && boost::filesystem::is_regular_file(path);\n\t}\n\n\tbool\t\t\tdirectoryExists(const std::string& path)\n\t{\n\t\tboost::filesystem::path p(path);\n\t\treturn boost::filesystem::exists(p) && boost::filesystem::is_directory(path);\n\t}\n\n\tsize_t getAvailableMem() {\n#define DIV 1024\n\n#ifdef SIBR_OS_WINDOWS \n\t\tMEMORYSTATUSEX statex;\n\t\tstatex.dwLength = sizeof(statex);\n\t\tGlobalMemoryStatusEx(&statex);\n\t\treturn static_cast<size_t>(statex.ullAvailPhys) / DIV;\n#else\n\t\tlong pages = sysconf(_SC_PHYS_PAGES);\n\t\tlong page_size = sysconf(_SC_PAGE_SIZE);\n\t\treturn static_cast<size_t>(pages * page_size) / DIV;\n#endif\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getInstallDirectory()\n\t{\n\t\tchar exePath[4095];\n\n#ifdef SIBR_OS_WINDOWS \n\t\tunsigned int len = GetModuleFileNameA(GetModuleHandleA(0x0), exePath, MAX_PATH);\n\n\t\tstd::string installDirectory = parentDirectory(parentDirectory(exePath));\n#else\n\t\tunsigned int len=0;\n\n\t\tchar result[PATH_MAX];\n\t\tssize_t c = readlink(\"/proc/self/exe\", result, PATH_MAX);\n\t\tlen = c;\n\t\tresult[len]='\\0';\n\t\tconst char* path;\n\t\tif( c != -1 )\n\t\t\tpath = dirname(result);\n\t\telse\n\t\t\tSIBR_ERR  << \"Cant find executable path  \"<< std::endl;\n\n\n\t\tstd::string installDirectory(parentDirectory(path));\n#endif\n\n\t\tif (len == 0 && \n\t\t!directoryExists(installDirectory + \"/bin\")) // memory not sufficient or general error occured\n\t\t{\n\t\t\tSIBR_ERR << \"Can't find install folder! Please specify as command-line option using --appPath option!\" << std::endl;\n\t\t}\n\t\treturn installDirectory;\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getBinDirectory()\n\t{\n\t\treturn getInstallSubDirectory(\"bin\");\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getShadersDirectory(const std::string & subfolder)\n\t{\n\t\treturn getInstallSubDirectory(\"shaders\" + ((subfolder != \"\") ? \"/\" + subfolder : \"\"));\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getScriptsDirectory()\n\t{\n\t\treturn getInstallSubDirectory(\"scripts\");\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getResourcesDirectory()\n\t{\n\t\treturn getInstallSubDirectory(\"resources\");\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getAppDataDirectory()\n\t{\n\t\tstd::string appDataDirectory = \"\";\n#ifdef SIBR_OS_WINDOWS \n\t\tPWSTR path_tmp;\n\n\t\t/* Attempt to get user's AppData folder\n\t\t*\n\t\t* Microsoft Docs:\n\t\t* https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath\n\t\t* https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid\n\t\t*/\n\t\tauto get_folder_path_ret = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &path_tmp);\n\n\t\t/* Error check */\n\t\tif (get_folder_path_ret != S_OK) {\n\t\t\tCoTaskMemFree(path_tmp);\n\t\t\tSIBR_ERR << \"Could not access AppData folder.\";\n\t\t}\n\n\t\tstd::wstring path_wtmp(path_tmp);\n\t\tappDataDirectory += std::string( path_wtmp.begin(), path_wtmp.end() );\n\t\tappDataDirectory += \"\\\\sibr\";\n\t\tCoTaskMemFree(path_tmp);\n#else\n\t\tstruct passwd *pw = getpwuid(getuid());\n\t\tappDataDirectory += pw->pw_dir + std::string(\"/.sibr\");\n#endif\n\n\t\tmakeDirectory(appDataDirectory);\n\n\t\treturn appDataDirectory;\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::string getInstallSubDirectory(const std::string & subfolder)\n\t{\n\t\tstd::string installDirectory = getInstallDirectory();\n\t\tstd::string installSubDirectory = installDirectory + \"/\" + subfolder;\n\n\t\tif(!directoryExists(installSubDirectory))\n\t\t{\n\t\t\t// try subdirs GD LINUX issue\n\t\t\tinstallSubDirectory = installDirectory + \"/install/\" + subfolder;\n\t\t\tif(!directoryExists(installSubDirectory))\n\t\t\t\tSIBR_ERR << \"Can't find subfolder \" << subfolder << \" in \" << installDirectory << \". Please specify correct app folder as command-line option using --appPath option!\" << std::endl;\n\t\t}\n\n\t\treturn installSubDirectory;\n\t}\n\n\tbool showFilePicker(std::string & selectedElement,\n\n\t\tconst FilePickerMode mode, const std::string & directoryPath, const std::string & extensionsAllowed) {\n\n\t\tnfdchar_t *outPath = NULL;\n\t\tnfdresult_t result = NFD_CANCEL;\n\t\t\n\t\tif (mode == Directory) {\n\t\t\tresult = NFD_PickFolder(directoryPath.c_str(), &outPath);\n\t\t} else if (mode == Save) {\n\t\t\tresult = NFD_SaveDialog(extensionsAllowed.empty() ? NULL : extensionsAllowed.c_str(), directoryPath.c_str(), &outPath);\n\t\t} else {\n\t\t\tresult = NFD_OpenDialog(extensionsAllowed.empty() ? NULL : extensionsAllowed.c_str(), directoryPath.c_str(), &outPath);\n\t\t}\n\n\n\t\tif (result == NFD_OKAY) {\n\t\t\tselectedElement = std::string(outPath);\n\t\t\tfree(outPath);\n\t\t\treturn true;\n\t\t} else if (result == NFD_CANCEL) {\n\t\t\t// User canceled, do nothing.\n\t\t} else {\n\t\t\t// Programmatic error.\n\t\t\tSIBR_WRG << \"Unable to present file dialog.\" << std::endl;\n\t\t\tstd::cout << std::string(NFD_GetError()) << std::endl;\n\t\t}\n\t\tfree(outPath);\n\n\t\treturn false;\n\n\t}\n\n\tSIBR_SYSTEM_EXPORT std::istream& safeGetline(std::istream& is, std::string& t)\n\t{\n#ifdef SIBR_OS_WINDOWS\n\t\treturn std::getline(is, t);\n#else\n\t    t.clear();\n\n\t    // The characters in the stream are read one-by-one using a std::streambuf.\n\t    // That is faster than reading them one-by-one using the std::istream.\n\t    // Code that uses streambuf this way must be guarded by a sentry object.\n\t    // The sentry object performs various tasks,\n\t    // such as thread synchronization and updating the stream state.\n\n\t    std::istream::sentry se(is, true);\n\t    std::streambuf* sb = is.rdbuf();\n\n\t    for(;;) {\n\t\tint c = sb->sbumpc();\n\t\tswitch (c) {\n\t\tcase '\\n':\n\t\t    return is;\n\t\tcase '\\r':\n\t\t    if(sb->sgetc() == '\\n')\n\t\t\tsb->sbumpc();\n\t\t    return is;\n\t\tcase std::streambuf::traits_type::eof():\n\t\t    // Also handle the case when the last line has no line ending\n\t\t\tis.setstate(std::ios::eofbit);\n\t\t\t// this helps ignore the last line if it's empty (otherwise it's a different behavior from std::get_line)\n\t\t\tif (t.empty()) is.setstate(std::ios::badbit);\n\t\t    return is;\n\t\tdefault:\n\t\t    t += (char)c;\n\t\t}\n\t    }\n#endif\n\t}\n\n} // namespace sirb\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Utils.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <vector>\n# include \"core/system/Config.hpp\"\n# include \"core/system/String.hpp\"\n\nnamespace sibr\n{\n\t/**\n\t* \\addtogroup sibr_system\n\t* @{\n\t*/\n\n#ifdef SIBR_OS_WINDOWS \n\t/** setup console to allow color printing in console\n\t */\n\tSIBR_SYSTEM_EXPORT void\t\t\tsetupConsole(void) ;\n\t/** restore console to no colors\n\t */\n\tSIBR_SYSTEM_EXPORT void\t\t\trestoreConsole(void) ;\n\n#endif\n\n\t/** Load the whole file into a std::string\n\t * \\param filename the file path\n\t * \\return the loaded content */\n\tSIBR_SYSTEM_EXPORT std::string\tloadFile( const std::string& filename );\n\n\t/** Create directory (if it doesn't exist already) \n\t * \\param path the directory path\n\t */\n\tSIBR_SYSTEM_EXPORT void\t\t\tmakeDirectory( const std::string& path );\n\n\t/** List content of directory, sorted alphabetically.\n\t * \\param path directory path\n\t * \\param listHidden should hidden files be listed\n\t * \\param includeSubdirectories should subdirectories be explored\n\t * \\param allowedExtensions a list of allowed extensions to filter the list with (for instance {\"png\", \"bmp\"})\n\t * \\return a list of file names/subpaths\n\t * \\note To get each element full path, use path + \"/\" + itemPath\n\t */\n\tSIBR_SYSTEM_EXPORT std::vector<std::string>\tlistFiles(const std::string & path, const bool listHidden = false, const bool includeSubdirectories = false, const std::vector<std::string> & allowedExtensions = {});\n\n\t/** List content of directory, sorted alphabetically, including subdirectories. \n\t * \\param path directory path\n\t * \\param listHidden should hidden directories be listed\n\t * \\return a list of directory names/subpaths\n\t * \\note To get each element full path, use path + \"/\" + itemPath\n\t */\n\tSIBR_SYSTEM_EXPORT std::vector<std::string>\tlistSubdirectories(const std::string& path, const bool listHidden = false);\n\n\t/** Copy directory. \n\t * \\param src source path\n\t * \\param dst destination path\n\t * \\return success boolean\n\t */\n\tSIBR_SYSTEM_EXPORT bool copyDirectory(const std::string& src, const std::string& dst);\n\n\t/** Copy file.\n\t * \\param src source path\n\t * \\param dst destination path\n\t * \\param overwrite if the file already exists, should it be overwritten\n\t * \\return success boolean\n\t */\n\tSIBR_SYSTEM_EXPORT bool copyFile(const std::string& src, const std::string& dst, const bool overwrite = false);\n\n\t/** Empty a directory (if it exist already) \n\t * \\param path the directory path\n\t */\n\tSIBR_SYSTEM_EXPORT void\t\t\temptyDirectory(const std::string& path);\n\n\t/** Test if a file exists.\n\t *\\param path the file path\n\t *\\return true if file exists\n\t */\n\tSIBR_SYSTEM_EXPORT bool\t\t\tfileExists( const std::string& path );\n\n\t/** Test if a directory exists.\n\t *\\param path the directory path\n\t *\\return true if directory exists\n\t */\n\tSIBR_SYSTEM_EXPORT bool\t\t\tdirectoryExists( const std::string& path );\n\n\t/** \\return the available memory on windows system in Ko*/\n\tSIBR_SYSTEM_EXPORT size_t\t\tgetAvailableMem();\n\n\t/** \\return the binary directory on windows system*/\n\tSIBR_SYSTEM_EXPORT std::string\tgetInstallDirectory();\n\n\t/** \\return the binary directory on windows system*/\n\tSIBR_SYSTEM_EXPORT std::string\tgetBinDirectory();\n\n\t/**\n\t * \\param subfolder optional subfolder for subproject\n\t * \\return the binary directory on windows system\n\t */\n\tSIBR_SYSTEM_EXPORT std::string\tgetShadersDirectory(const std::string & subfolder = \"\");\n\n\t/** \\return the scripts directory on windows system*/\n\tSIBR_SYSTEM_EXPORT std::string\tgetScriptsDirectory();\n\n\t/** \\return the resources directory on windows system*/\n\tSIBR_SYSTEM_EXPORT std::string\tgetResourcesDirectory();\n\n\t/** \\return the user specific application directory */\n\tSIBR_SYSTEM_EXPORT std::string\tgetAppDataDirectory();\n\n\t/** \n\t * \\param subfolder the subfolder to get\n\t * \\return the provided subfolder path on windows system\n\t */\n\tSIBR_SYSTEM_EXPORT std::string\tgetInstallSubDirectory(const std::string & subfolder);\n\n\t/** Selection mode for the file picker. */\n\tenum FilePickerMode {\n\t\tDefault, Save, Directory\n\t};\n\n\t/**\n\t * Present a native OS file picker.\n\t * \\param selectedElement will contain the path to the element selected by the user if any.\n\t * \\param mode the mode to use, pick from Save, Directory, Default.\n\t * \\param directoryPath the initial directory to present to the user.\n\t * \\param extensionsAllowed a list of file extensions to allow: \"obj,ply\" for instance.\n\t * \\return true if an element was selected, else false.\n\t * \\warning '.' relative path is unsupported for directoryPath.\n\t */\n\tSIBR_SYSTEM_EXPORT bool showFilePicker(std::string & selectedElement,\n\t\tconst FilePickerMode mode, const std::string & directoryPath = \"\", const std::string & extensionsAllowed = \"\");\n\n\t/** Measure and print the timing of a task.\n\t *\\param s description\n\t *\\param f function to run\n\t *\\param args arguments for the function\n\t */\n\ttemplate<typename FunType, typename ...ArgsType>\n\tvoid taskTiming(const std::string & s, FunType && f, ArgsType && ... args) {\n\t\tconst auto start = std::chrono::high_resolution_clock::now();\n\t\tf(args...);\n\t\tconst auto end = std::chrono::high_resolution_clock::now();\n\t\tstd::cout << s << \" : \" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << \" ms\" << std::endl;\n\t};\n\n\tSIBR_SYSTEM_EXPORT std::istream& safeGetline(std::istream& is, std::string& t);\n\n\t/*** @} */\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Vector.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/system/Vector.hpp\"\n# include \"core/system/Quaternion.hpp\"\n\nnamespace sibr\n{\n\n\tVector3f toColorFloat(Vector3ub & colorUB ) {\n\t\treturn colorUB.cast<float>().unaryExpr( [] (unsigned char c) { return (float)c/255.0f; } );\n\t}\n\n\tVector3ub toColorUB( Vector3f & colorFloat ) {\n\t\treturn colorFloat.unaryExpr( [] (float f) { return std::floor(f*255.0f); } ).cast<unsigned char>();\n\t}\n\n\tEigen::Matrix<float, 4, 4, Eigen::DontAlign> alignRotationMatrix(const sibr::Vector3f & from, const sibr::Vector3f & to)\n\t{\n\t\tsibr::Quaternionf q = sibr::Quaternionf::FromTwoVectors(from, to);\n\t\tq.normalize();\n\t\tEigen::Matrix3f R = q.toRotationMatrix();\n\t\tsibr::Matrix4f R4;\n\t\tR4.setIdentity();\n\t\tR4.block<3, 3>(0, 0) = R;\n\t\treturn R4;\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/Vector.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/system/Config.hpp\"\r\n\r\n\r\n\r\nnamespace Eigen\r\n{\r\n\t/**\r\n\t* \\addtogroup sibr_system\r\n\t* @{\r\n\t*/\r\n\r\n\t// The following operators work with Eigen structs, so\r\n\t// they must be declared in the namespace Eigen (or\r\n\t// you would have to do sibr::operator < (left, right)\r\n\t// instead of simple left < right)\r\n\r\n\t/** Lexicographic comparison (from left to right).\r\n\t *\\param left first element\r\n\t *\\param right second element\r\n\t *\\return true if left is lexicographically smaller than right.\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tbool operator<(const Eigen::Matrix<T, N, 1, Options>& left, const Eigen::Matrix<T, N, 1, Options>& right) {\r\n\r\n\t\tfor (int c = 0; c < N; c++) {\r\n\t\t\tif (left[c] < right[c]) return true;\r\n\t\t\telse if (left[c] > right[c]) return false;\r\n\t\t}\r\n\t\treturn false; //case where they are equal\r\n\r\n\t}\r\n\r\n\t// stream\r\n\r\n\t/** Output matrix to a stream.\r\n\t *\\param s stream\r\n\t *\\param t matrix\r\n\t *\\return the stream for chaining\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tstd::ostream& operator<<( std::ostream& s, const Eigen::Matrix<T, N, 1, Options>& t ) {\r\n\t\ts << '(';\r\n\t\tfor (uint i=0; i<N; i++) { s << t[i]; if (i < N-1) s << ','; }\r\n\t\ts << ')';\r\n\t\treturn (s);\r\n\t}\r\n\r\n\t/** Read matrix from a stream.\r\n\t *\\param s stream\r\n\t *\\param t matrix\r\n\t *\\return the stream for chaining\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tstd::istream& operator>>( std::istream& s, Eigen::Matrix<T, N, 1, Options>& t ) {\r\n\t\tchar tmp = 0;\r\n\t\ts >> tmp; // (\r\n\t\tfor (int i = 0; i < N; ++i)\r\n\t\t{\r\n\t\t\ts >> t [i];\r\n\t\t\ts >> tmp; //, or )\r\n\t\t}\r\n\r\n\t\treturn s;\r\n\t}\r\n\r\n\t/** @} */\r\n}\r\n\r\nnamespace sibr\r\n{\r\n\r\n\t/**\r\n\t* \\addtogroup sibr_system\r\n\t* @{\r\n\t*/\r\n\r\n\ttemplate <typename T, int N>\r\n\tusing Vector = Eigen::Matrix<T, N, 1, Eigen::DontAlign>;\r\n\r\n\t/** Fractional part of each component.\r\n\t *\\param A vector\r\n\t *\\return the fractional matrix\r\n\t **/\r\n\ttemplate <typename T, int N, int Options>\r\n\tEigen::Matrix<T, N, 1, Options>\t\t\tfrac( const Eigen::Matrix<T, N, 1, Options>& A ) {\r\n\t\tEigen::Matrix<T, N, 1, Options> out = A;\r\n\t\tfor (int i = 0; i < N; ++i)\r\n\t\t\tout[i] = out[i] - floor(out[i]);\r\n\t\treturn out;\r\n\t}\r\n\r\n\t/** Distance between two vectors\r\n\t *\\param A first vector\r\n\t *\\param B second vector\r\n\t *\\return norm(A-B)\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tinline T\t\t\tdistance( const Eigen::Matrix<T, N, 1, Options>& A, const Eigen::Matrix<T, N, 1, Options>& B ) {\r\n\t\treturn (A-B).norm();\r\n\t}\r\n\r\n\t/** Return the length of a vector.\r\n\t *\\param A vector\r\n\t *\\return norm(A)\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tinline T\t\t\tlength( const Eigen::Matrix<T, N, 1, Options>& A ) {\r\n\t\treturn A.norm();\r\n\t}\r\n\r\n\t/** Return the squared length of a vector.\r\n\t *\\param A vector\r\n\t *\\return norm(A)^2\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tinline T\t\t\tsqLength( const Eigen::Matrix<T, N, 1, Options>& A ) {\r\n\t\treturn A.squaredNorm();\r\n\t}\r\n\r\n\t/** Compute the dot product of two vectors\r\n\t *\\param A first vector\r\n\t *\\param B second vector\r\n\t *\\return A.B\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tinline T\t\t\tdot( const Eigen::Matrix<T, N, 1, Options>& A, const Eigen::Matrix<T, N, 1, Options>& B ) {\r\n\t\treturn A.dot(B);\r\n\t}\r\n\r\n\t/** Compute the cross product of two vectors\r\n\t *\\param A first vector\r\n\t *\\param B second vector\r\n\t *\\return AxB\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tinline Eigen::Matrix<T, N, 1, Options>\tcross( const Eigen::Matrix<T, N, 1, Options>& A, const Eigen::Matrix<T, N, 1, Options>& B ) {\r\n\t\treturn A.cross(B);\r\n\t}\r\n\r\n\t/** Clamp each component of a vector between two values.\r\n\t * \\param A vector\r\n\t * \\param min min values vector\r\n\t * \\param max max values vector\r\n\t * \\return min(max(A, min), max)\r\n\t */\r\n\ttemplate <typename T, int N>\r\n\tinline Vector<T,N> clamp(const Vector<T, N>& A, const Vector<T, N> & min, const Vector<T, N> & max) {\r\n\t\treturn A.cwiseMax(min).cwiseMin(max);\r\n\t}\r\n\r\n\t/** Compute the cotangent of the angle between two vectors.\r\n\t *\\param A first vector\r\n\t *\\param B second vector\r\n\t *\\return the cotangent\r\n\t */\r\n\ttemplate <typename T, int N, int Options>\r\n\tinline T cotan(const Eigen::Matrix<T, N, 1, Options>& A, const Eigen::Matrix<T, N, 1, Options>& B) {\r\n\t\treturn A.dot(B) / A.cross(B).norm();\r\n\t}\r\n\r\n\t/** Convert an unsigned char color in [0,255] to a float color in [0,1].\r\n\t *\\param colorUB the color vector\r\n\t *\\return the [0,1] float vector\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT Eigen::Matrix<float, 3,\t1, Eigen::DontAlign>  toColorFloat( Vector<unsigned char, 3> & colorUB );\r\n\r\n\t/** Convert a float color in [0,1] to an unsigned char color in [0,255].\r\n\t *\\param colorFloat the color vector\r\n\t *\\return the [0,255] float vector\r\n\t */\r\n\tSIBR_SYSTEM_EXPORT Eigen::Matrix<unsigned char, 3,1,Eigen::DontAlign> toColorUB( Vector<float,3> & colorFloat );\r\n\r\n\t// Typedefs.\r\n\r\n\ttypedef\tEigen::Matrix<float, 1,\t\t\t1,Eigen::DontAlign>\t\t\tVector1f;\r\n\ttypedef\tEigen::Matrix<int, 1,\t\t\t1,Eigen::DontAlign>\t\t\tVector1i;\r\n\r\n\ttypedef\tEigen::Matrix<unsigned, 2,\t\t1,Eigen::DontAlign>\t\t\tVector2u;\r\n\ttypedef\tEigen::Matrix<unsigned char, 2,\t1,Eigen::DontAlign>\t\t\tVector2ub;\r\n\ttypedef\tEigen::Matrix<int, 2,\t\t\t1,Eigen::DontAlign>\t\t\tVector2i;\r\n\ttypedef\tEigen::Matrix<float, 2,\t\t\t1,Eigen::DontAlign>\t\t\tVector2f;\r\n\ttypedef\tEigen::Matrix<double, 2,\t\t1,Eigen::DontAlign>\t\t\tVector2d;\r\n\r\n\ttypedef\tEigen::Matrix<unsigned, 3,\t\t1,Eigen::DontAlign>\t\t\tVector3u;\r\n\ttypedef\tEigen::Matrix<unsigned char, 3,\t1,Eigen::DontAlign>\t\t\tVector3ub;\r\n\ttypedef\tEigen::Matrix<unsigned short int, 3, 1,Eigen::DontAlign>\tVector3s;\r\n\ttypedef\tEigen::Matrix<int, 3,\t\t\t1,Eigen::DontAlign>\t\t\tVector3i;\r\n\ttypedef\tEigen::Matrix<float, 3,\t\t\t1,Eigen::DontAlign>\t\t\tVector3f;\r\n\ttypedef\tEigen::Matrix<double, 3,\t\t1,Eigen::DontAlign>\t\t\tVector3d;\r\n\r\n\ttypedef\tEigen::Matrix<unsigned, 4,\t\t1,Eigen::DontAlign>\t\t\tVector4u;\r\n\ttypedef\tEigen::Matrix<unsigned char, 4,\t1,Eigen::DontAlign>\t\t\tVector4ub;\r\n\ttypedef\tEigen::Matrix<int, 4,\t\t\t1,Eigen::DontAlign>\t\t\tVector4i;\r\n\ttypedef\tEigen::Matrix<float, 4,\t\t\t1,Eigen::DontAlign>\t\t\tVector4f;\r\n\ttypedef\tEigen::Matrix<double, 4,\t\t1,Eigen::DontAlign>\t\t\tVector4d;\r\n\r\n\t/**\r\n\t\tReturn a 4x4 3D rotation matrix that aligns the first vector onto the second one.\r\n\t\t\\param from source vector, current direction\r\n\t\t\\param to destination vector, target direction\r\n\t\t\\return the rotation matrix\r\n\t*/\r\n\tSIBR_SYSTEM_EXPORT Eigen::Matrix<float, 4, 4, Eigen::DontAlign> alignRotationMatrix(const sibr::Vector3f & from, const sibr::Vector3f & to);\r\n\r\n\t/** @} */\r\n} // namespace sibr\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/VectorUtils.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"VectorUtils.hpp\""
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/VectorUtils.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <vector>\n# include \"core/system/Config.hpp\"\n#include <functional>\n\nnamespace sibr\n{\n\t/**\n\t* \\addtogroup sibr_system\n\t* @{\n\t*/\n\n\t/** Sum elements from a vector.\n\t *\\param vec vector\n\t *\\param f validity function (f(i) == true if the i-th element should be taken into account.\n\t *\\return the accumulated sum\n\t **/\n\ttemplate<typename T_in, typename T_out = T_in> T_out sum(\n\t\tconst std::vector<T_in> & vec ,\n\t\tconst std::function<bool(T_in)> & f = [](T_in val) { return true; }\n\t) {\n\t\tdouble sum = 0;\n\t\tfor (T_in val : vec) {\n\t\t\tif( f(val) ){\n\t\t\t\tsum += (double)val;\n\t\t\t}\n\t\t}\n\t\treturn (T_out)sum;\n\t}\n\n\t/** Weighted sum of elements in a vector.\n\t *\\param vec vector\n\t *\\param weights per-element weight\n\t *\\param f validity function (f(i) == true if the i-th element should be taken into account.\n\t *\\return the weighted sum\n\t **/\n\ttemplate<typename T_in, typename T_out = T_in> std::vector<T_out> weighted_normalization(\n\t\tconst std::vector<T_in> & vec,\n\t\tconst std::vector<T_in> & weights,\n\t\tconst std::function<bool(T_in)> & f = [](T_in val) { return true; }\n\t) {\n\t\tdouble sum = 0;\n\t\tint size = (int)std::min(vec.size(), weights.size());\n\t\tfor (int i = 0; i < size; ++i) {\n\t\t\tT_in val = vec[i];\n\t\t\tif (f(val)) {\n\t\t\t\tsum += (double)val*(double)weights[i];\n\t\t\t}\n\t\t}\n\n\t\tstd::vector<T_out> out(size);\n\t\tfor (int i = 0; i < size; ++i) {\n\t\t\tif ((sum == 0) || !f(vec[i])) {\n\t\t\t\tout[i] = (T_out)vec[i];\n\t\t\t} else {\n\t\t\t\tout[i] = (T_out)( ( (double)vec[i] * (double)weights[i] )/sum );\n\t\t\t}\n\t\t}\n\t\t\t\n\t\treturn out;\n\t}\n\n\t/** Apply a function to each element of a vector (not in place)\n\t *\\param vec vector\n\t *\\param f function to apply\n\t *\\return vector containing the processed results\n\t */\n\ttemplate<typename T_in, typename T_out = T_in> std::vector<T_out> applyLambda(\n\t\tconst std::vector<T_in> & vec,\n\t\tconst std::function<T_out(T_in)> & f\n\t) {\n\t\tstd::vector<T_out> out(vec.size());\n\t\tfor (int i = 0; i < vec.size(); ++i) {\n\t\t\tout[i] = f(vec[i]);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**  Apply a function to each pair of elements from two vectors of same size (not in place).\n\t *\\param vec1 first vector\n\t *\\param vec2 second vector\n\t *\\param f function to apply\n\t *\\return vector containing the processed results\n\t */\n\ttemplate<typename T_in, typename T_out> std::vector<T_out> applyLambda(\n\t\tconst std::vector<T_in> & vec1,\n\t\tconst std::vector<T_in> & vec2,\n\t\tconst std::function<T_out(T_in,T_in)> & f\n\t) {\n\t\tint size = (int)std::min(vec1.size(), vec2.size());\n\t\tstd::vector<T_out> out(size);\n\t\tfor (int i = 0; i < size; ++i) {\n\t\t\tout[i] = f(vec1[i],vec2[i]);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Compute the variance of elements in a vector.\n\t *\\param vec vector\n\t *\\param f validity function (f(i) == true if the i-th element should be taken into account.\n\t *\\return the variance\n\t **/\n\ttemplate<typename T_in, typename T_out = T_in> T_out var(\n\t\tconst std::vector<T_in> & vec,\n\t\tconst std::function<bool(T_in)> & f = [](T_in val) { return true; } \n\t) {\n\t\tdouble sum = 0;\n\t\tdouble sum2 = 0;\n\t\tint n = 0;\n\n\t\tfor (T_in val : vec) {\n\t\t\tif ( f(val) ) {\n\t\t\t\tsum += (double)val;\n\t\t\t\tsum2 += (double)val*(double)val;\n\t\t\t\t++n;\n\t\t\t}\n\t\t}\n\n\t\tif (n < 2) {\n\t\t\treturn (T_out)(-1);\n\t\t}\n\t\telse {\n\t\t\treturn (T_out)((sum2 - sum*sum / (double)n) / double(n - 1));\n\t\t}\n\t\t\n\t}\n\n\t/** Normalize all elements in a vector based on the min and max values in it (not in place).\n\t *\\param vec vector\n\t *\\param f validity function (f(i) == true if the i-th element should be taken into account.\n\t *\\return a vector containing the normalized values\n\t **/\n\ttemplate<typename T_in, typename T_out = T_in> std::vector<T_out> normalizedMinMax(\n\t\tconst std::vector<T_in> & vec,\n\t\tconst std::function<bool(T_in)> & f = [](T_in val) { return true; }\n\t) {\n\t\tT_in min = 0, max = 0;\n\t\tbool first = true;\n\t\tfor (T_in val : vec) {\n\t\t\tif (f(val)) {\n\t\t\t\tif (first || val > max) {\n\t\t\t\t\tmax = val;\n\t\t\t\t}\n\t\t\t\tif (first || val < min) {\n\t\t\t\t\tmin = val;\n\t\t\t\t}\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t}\n\t\tif (min == max) {\n\t\t\treturn std::vector<T_out>();\n\t\t}\n\t\t\n\t\tstd::vector<T_out> out(vec.size());\n\t\tconst double normFactor = 1.0 / (double)(max - min);\n\t\tfor (int i = 0; i < (int)vec.size(); ++i) {\n\t\t\tout[i] = f(vec[i]) ? (T_out)((double)(vec[i] - min)*normFactor) : (T_out)vec[i];\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Apply a power-sum normalization.\n\t *\\param vec vector\n\t *\\param f validity function (f(i) == true if the i-th element should be taken into account.\n\t *\\return a vector containing the normalized values\n\t */\n\ttemplate<typename T_in, typename T_out = T_in, unsigned int Power = 2> std::vector<T_out> normalizedZeroOne(\n\t\tconst std::vector<T_in> & vec,\n\t\tconst std::function<bool(T_in)> & f = [](T_in val) { return true; }\n\t) {\n\t\tdouble sumP = 0;\n\n\t\tfor (T_in val : vec) {\n\t\t\tif (f(val)) {\n\t\t\t\tsumP += std::pow((double)val, Power);\n\t\t\t}\n\t\t\t\n\t\t}\n\n\t\tif (sumP == 0) {\n\t\t\treturn std::vector<T_out>();\n\t\t}\n\t\t\n\t\tstd::vector<T_out> out(vec.size());\n\t\tfor (int i = 0; i <(int)vec.size(); ++i) {\n\t\t\tout[i] = f(vec[i]) ? (T_out)(vec[i] / sumP) : (T_out)vec[i];\n\t\t}\n\t\treturn out;\n\t\t\t\n\t}\n\n\t/*** @} */\n\n\t/**\n\t * Multi dimensional vector.\n\t* \\ingroup sibr_system\n\t*/\n\ttemplate< typename T, unsigned int N >\n\tclass MultiVector : public std::vector< MultiVector<T, N - 1> >\n\t{\n\t\tstatic_assert(N >= 1, \" MultiVector<N> : the number of dimensions N must be >= 1 \");\n\n\t\tfriend class MultiVector<T, N + 1>;\n\n\t\ttypedef MultiVector<T, N - 1> SubVector;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tMultiVector() {}\n\n\t\t/** Constructor.\n\t\t *\\param n number of elements on each axis\n\t\t *\\param t default value\n\t\t */\n\t\tMultiVector(int n, const T & t = T() )\n\t\t\t: std::vector< SubVector >(n, SubVector(n, t)) { }\n\n\t\t/** Constructor.\n\t\t *\\param dims number of elements on each axis\n\t\t *\\param t default value\n\t\t */\n\t\tMultiVector(const std::vector<int> & dims, const T & t = T() )\n\t\t\t: std::vector< SubVector >(dims.at(dims.size()-N), SubVector(dims, t)) { }\n\n\t\t/** Getter\n\t\t *\\param  ids N-d coordinates\n\t\t *\\return a reference to the corresponding value.\n\t\t */\n\t\tT & multiAt(const std::vector<int> & ids) {\n\t\t\treturn this.at(ids.at(ids.size() - N)).multiAt(ids);\n\t\t}\n\n\t\t/** Getter\n\t\t *\\param  ids N-d coordinates\n\t\t *\\return a const reference to the corresponding value.\n\t\t */\n\t\tconst T & multiAt(const std::vector<int> & ids) const {\n\t\t\treturn this.at(ids.at(ids.size() - N)).multiAt(ids);\n\t\t}\n\n\t\t/** Get the size along each dimension.\n\t\t *\\return the N-d size\n\t\t **/\n\t\tstd::vector<int> dims() const\n\t\t{\n\t\t\tstd::vector<int> v;\n\t\t\tdimsRecur(v);\n\t\t\treturn v;\n\t\t}\n\n\t\t/**Print the size along each dimension.\n\t\t */\n\t\tvoid dimsDisplay() const {\n\t\t\tstd::vector<int> d(dims());\n\t\t\tstd::cout << \" [ \";\n\t\t\tfor (int i = 0; i < N; ++i) {\n\t\t\t\tstd::cout << d[i] << (i != N - 1 ? \" x \" : \"\");\n\t\t\t}\n\t\t\tstd::cout << \" ] \" << std::endl;\n\t\t}\n\n\tprotected:\n\n\t\t/** Helper to get the dimensions.\n\t\t *\\param v will contain the size along each axis\n\t\t */\n\t\tvoid dimsRecur(std::vector<int> & v) const\n\t\t{\n\t\t\tv.push_back((int)this.size());\n\t\t\tthis.at(0).dimsRecur(v);\n\t\t}\n\t};\n\n\t/** Base multi-dimensional vector class (a 1D vector). \n\t\\ingroup sibr_system\n\t*/\n\ttemplate<typename T>\n\tclass MultiVector<T, 1> : public std::vector<T>\n\t{\n\t\tfriend class MultiVector<T, 2>;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tMultiVector() {}\n\n\t\t/** Constructor.\n\t\t *\\param n number of elements\n\t\t *\\param t default value\n\t\t */\n\t\tMultiVector(int n, const T & t = T() )\n\t\t\t: std::vector<T>(n, t) { }\n\n\t\t/** Constructor.\n\t\t *\\param dims number of elements on each axis (only the last one will be considered here).\n\t\t *\\param t default value\n\t\t */\n\t\tMultiVector(const std::vector<int> & dims, const T & t = T())\n\t\t\t: std::vector<T>(dims.at(dims.size()-1), t) { }\n\n\t\t/** Getter\n\t\t *\\param  ids N-d coordinates\n\t\t *\\return a reference to the corresponding value.\n\t\t */\n\t\tT & multiAt(const std::vector<int> & ids) {\n\t\t\treturn this.at(ids.at(ids.size() - 1));\n\t\t}\n\n\t\t/** Getter\n\t\t *\\param  ids N-d coordinates\n\t\t *\\return a const reference to the corresponding value.\n\t\t */\n\t\tconst T & multiAt(const std::vector<int> & ids) const {\n\t\t\treturn this.at(ids.at(ids.size() - 1));\n\t\t}\n\n\t\t/**Print the size along each dimension.\n\t\t */\n\t\tvoid dimsDisplay() const {\n\t\t\tstd::cout << \" [ \" << this.size() << \" ] \" << std::endl;\n\t\t}\n\n\tprotected:\n\n\t\t/** Helper to get the dimensions.\n\t\t *\\param v will contain the size along each axis\n\t\t */\n\t\tvoid dimsRecur(std::vector<int> & v) const\n\t\t{\n\t\t\tv.push_back((int)this.size());\n\t\t}\n\n\t};\n\n\t\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/XMLTree.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"XMLTree.h\"\n#include \"rapidxml/rapidxml_print.hpp\"\n#include <iostream>\n#include <fstream>\n#include <sstream>\n\n\nnamespace sibr {\n\tXMLTree::XMLTree(const std::string &  path)\n\t{\n\t\tstd::cout << \"Parsing xml file < \" << path << \" > : \";\n\t\tstd::ifstream file(path.c_str());\n\t\tif (file) {\n\t\t\tstd::stringstream buffer;\n\t\t\tbuffer << file.rdbuf();\n\t\t\tfile.close();\n\t\t\txmlString = std::move(std::string(buffer.str()));\n\t\t\tthis->parse<0>(&xmlString[0]);\n\t\t\tstd::cout << \"success \" << std::endl;\n\t\t}\n\t\telse {\n\t\t\tstd::cout << \"error, cant open file \" << std::endl;\n\t\t}\n\t}\n\n\n\tXMLTree::~XMLTree(void)\n\t{\n\t}\n\n\n\tbool XMLTree::save(const std::string & path) const {\n\t\tstd::ofstream file(path);\n\t\tif(!file.is_open()) {\n\t\t\tSIBR_WRG << \"Unable to save XML to path \\\"\" << path << \"\\\".\" << std::endl;\n\t\t\treturn false;\n\t\t}\n\n\t\tfile << *this;\n\t\tfile.close();\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/XMLTree.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <rapidxml/rapidxml.hpp>\n#include <string>\n#include \"Config.hpp\"\n\nnamespace sibr {\n\n\t/** Wrapper of rapidxml xml_document<> class so that the string associated to the xml file stays in memory.\n\tNeeded to access nodes by their names.\n\t* \\ingroup sibr_system\n\t*/\n\tclass SIBR_SYSTEM_EXPORT XMLTree : public rapidxml::xml_document<>\n\t{\n\tpublic:\n\t\t/** Construct an XML structure from the content of a file.\n\t\t\\param path the file path\n\t\t*/\n\t\tXMLTree(const std::string & path);\n\n\t\t/** Destructor. */\n\t\t~XMLTree(void);\n\n\t\t/** Save the XML structure to a file as a string representation.\n\t\t\\param path output path\n\t\t\\return a success flag\n\t\t*/\n\t\tbool save(const std::string & path) const;\n\n\tprivate:\n\t\tstd::string xmlString; //< Internal copy of the laoded string.\n\t};\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/system/sibr_system.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_system sibr_system\n\n\t\\brief System utilities.\n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(sibr_video)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS})\ntarget_link_libraries(${PROJECT_NAME}\n\t${FFMPEG_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\tsibr_graphics\n)\n\n\n\nadd_definitions( -DSIBR_VIDEO_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\n\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/graphics/Config.hpp\"\n\n//// Export Macro (used for creating DLLs) ////\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_VIDEO_DEFINE\n#    define SIBR_VIDEO_EXPORT\n#    define SIBR_NO_VIDEO_EXPORT\n#  else\n#    ifndef SIBR_VIDEO_EXPORT\n#      ifdef SIBR_VIDEO_EXPORTS\n          /* We are building this library */\n#        define SIBR_VIDEO_EXPORT __declspec(dllexport)\n#      else\n          /* We are using this library */\n#        define SIBR_VIDEO_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT \n#    endif\n#  endif\n# else\n#  define SIBR_VIDEO_EXPORT\n# endif\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/FFmpegVideoEncoder.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"FFmpegVideoEncoder.hpp\"\n\n#ifndef HEADLESS\nextern \"C\"\n{\n#include <libavcodec/avcodec.h>\n#include <libavformat/avformat.h>\n#include <libswscale/swscale.h>\n}\n#endif\n\n#define QQ(rat) (rat.num/(double)rat.den)\n\n// Disable ffmpeg deprecation warning.\n#pragma warning(disable : 4996)\n\nnamespace sibr {\n\n\tbool FFVideoEncoder::ffmpegInitDone = false;\n\n\tFFVideoEncoder::FFVideoEncoder(\n\t\tconst std::string & _filepath,\n\t\tdouble _fps,\n\t\tconst sibr::Vector2i & size,\n\t\tbool forceResize\n\t) : filepath(_filepath), fps(_fps), _forceResize(forceResize)\n\t{\n#ifndef HEADLESS\n\t\t/** Init FFMPEG, registering available codec plugins. */\n\t\tif (!ffmpegInitDone) {\n\t\t\tSIBR_LOG << \"[FFMPEG] Registering all.\" << std::endl;\n\t\t\t// Ignore next line warning.\n#pragma warning(suppress : 4996)\n\t\t\tav_register_all();\n\t\t\tffmpegInitDone = true;\n\t\t}\n\t\t\n\t\tsibr::Vector2i sizeFix = size;\n\t\tbool hadToFix = false;\n\t\tif(sizeFix[0]%2 != 0) {\n\t\t\tsizeFix[0] -= 1;\n\t\t\thadToFix = true;\n\t\t}\n\t\tif (sizeFix[1] % 2 != 0) {\n\t\t\tsizeFix[1] -= 1;\n\t\t\thadToFix = true;\n\t\t}\n\t\tif(hadToFix) {\n\t\t\tSIBR_WRG << \"Non-even video dimensions, resized to \" << sizeFix[0] << \"x\" << sizeFix[1] << \".\" << std::endl;\n\t\t\t_forceResize = true;\n\t\t}\n\t\t\n\t\tinit(sizeFix);\n#endif\n\t}\n\n\tbool FFVideoEncoder::isFine() const\n\t{\n\t\treturn initWasFine;\n\t}\n\n\tvoid FFVideoEncoder::close()\n\t{\n#ifndef HEADLESS\n\t\tif (av_write_trailer(pFormatCtx) < 0) {\n\t\t\tSIBR_WRG << \"[FFMPEG] Can not av_write_trailer \" << std::endl;\n\t\t}\n\n\t\tif (video_st) {\n\t\t\tavcodec_close(video_st->codec);\n\t\t\tav_free(frameYUV);\n\t\t}\n\t\tavio_close(pFormatCtx->pb);\n\t\tavformat_free_context(pFormatCtx);\n\n\t\tneedFree = false;\n#endif\n\t}\n\n\tFFVideoEncoder::~FFVideoEncoder()\n\t{\n\t\tif (needFree) {\n\t\t\tclose();\n\t\t}\n\n\t}\n\n\tvoid FFVideoEncoder::init(const sibr::Vector2i & size)\n\t{\n#ifndef HEADLESS\n\t\tw = size[0];\n\t\th = size[1];\n\n\t\tauto out_file = filepath.c_str();\n\n\n\t\tpFormatCtx = avformat_alloc_context();\n\n\t\tfmt = av_guess_format(NULL, out_file, NULL);\n\t\tpFormatCtx->oformat = fmt;\n\n\t\tconst bool isH264 = pFormatCtx->oformat->video_codec == AV_CODEC_ID_H264;\n\t\tif(isH264){\n\t\t\tSIBR_LOG << \"[FFMPEG] Found H264 codec.\" << std::endl;\n\t\t} else {\n\t\t\tSIBR_LOG << \"[FFMPEG] Found codec with ID \" << pFormatCtx->oformat->video_codec << \" (not H264).\" << std::endl;\n\t\t}\n\t\t\n\t\tif (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {\n\t\t\tSIBR_WRG << \"[FFMPEG] Could not open file \" << filepath << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tpCodec = avcodec_find_encoder(pFormatCtx->oformat->video_codec);\n\t\tif (!pCodec) {\n\t\t\tSIBR_WRG << \"[FFMPEG] Could not find codec.\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tvideo_st = avformat_new_stream(pFormatCtx, pCodec);\n\n\t\tif (video_st == NULL) {\n\t\t\tSIBR_WRG << \"[FFMPEG] Could not create stream.\" << std::endl;\n\t\t\treturn;\n\t\t}\n\n\t\tpCodecCtx = video_st->codec;\n\t\tpCodecCtx->codec_id = fmt->video_codec;\n\t\tpCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;\n\t\tpCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;\n\t\tpCodecCtx->width = w;\n\t\tpCodecCtx->height = h;\n\t\tpCodecCtx->gop_size = 10;\n\t\tpCodecCtx->time_base.num = 1;\n\t\tpCodecCtx->time_base.den = (int)std::round(fps);\n\n\t\t// Required for the header to be well-formed and compatible with Powerpoint/MediaPlayer/...\n\t\tif (pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER) {\n\t\t\tpCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;\n\t\t}\n\n\t\t//H.264 specific options.\n\t\tAVDictionary *param = 0;\n\t\tif (pCodecCtx->codec_id == AV_CODEC_ID_H264) {\n\t\t\tav_dict_set(&param, \"preset\", \"slow\", 0);\n\t\t\tav_dict_set(&param, \"tune\", \"zerolatency\", 0);\n\t\t}\n\n\t\tav_dump_format(pFormatCtx, 0, out_file, 1);\n\n\t\tint res = avcodec_open2(pCodecCtx, pCodec, &param);\n\t\tif(res < 0){\n\t\t\tSIBR_WRG << \"[FFMPEG] Failed to open encoder, error: \" << res << std::endl;\n\t\t\treturn;\n\t\t}\n\t\t// Write the file header.\n\t\tavformat_write_header(pFormatCtx, NULL);\n\n\t\t// Prepare the scratch frame.\n\t\tframeYUV = av_frame_alloc();\n\t\tframeYUV->format = (int)pCodecCtx->pix_fmt;\n\t\tframeYUV->width = w;\n\t\tframeYUV->height = h;\n\t\tframeYUV->linesize[0] = w;\n\t\tframeYUV->linesize[1] = w / 2;\n\t\tframeYUV->linesize[2] = w / 2;\n\n\t\tyuSize[0] = frameYUV->linesize[0] * h;\n\t\tyuSize[1] = frameYUV->linesize[1] * h / 2;\n\n\t\tpkt = av_packet_alloc();\n\n\t\tinitWasFine = true;\n\t\tneedFree = true;\n#endif\n\t}\n\n\n\tbool FFVideoEncoder::operator<<(cv::Mat frame)\n\t{\n#ifndef HEADLESS\n\t\tif (!video_st) {\n\t\t\treturn false;\n\t\t}\n\t\tcv::Mat local;\n\t\tif (frame.cols != w || frame.rows != h) {\n\t\t\tif(_forceResize) {\n\t\t\t\tcv::resize(frame, local, cv::Size(w,h));\n\t\t\t} else {\n\t\t\t\tSIBR_WRG << \"[FFMPEG] Frame doesn't have the same dimensions as the video.\" << std::endl;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tlocal = frame;\n\t\t}\n\n\t\tcv::cvtColor(local, cvFrameYUV, cv::COLOR_BGR2YUV_I420);\n\t\tframeYUV->data[0] = cvFrameYUV.data;\n\t\tframeYUV->data[1] = frameYUV->data[0] + yuSize[0];\n\t\tframeYUV->data[2] = frameYUV->data[1] + yuSize[1];\n\n\t\t//frameYUV->pts = (1.0 / std::round(fps)) *frameCount * 90;\n\t\tframeYUV->pts = (int)(frameCount*(video_st->time_base.den) / ((video_st->time_base.num) * std::round(fps)));\n\t\t++frameCount;\n\n\t\treturn encode(frameYUV);\n#else\n\t\tSIBR_ERR << \"Not supported in headless\" << std::endl;\n\t\treturn false;\n#endif\n\t}\n\n\tbool FFVideoEncoder::operator<<(const sibr::ImageRGB & frame){\n\t\treturn (*this)<<(frame.toOpenCVBGR());\n\t}\n\n#ifndef HEADLESS\n\tbool FFVideoEncoder::encode(AVFrame * frame)\n\t{\n\t\tint got_picture = 0;\n\n\t\tint ret = avcodec_encode_video2(pCodecCtx, pkt, frameYUV, &got_picture);\n\t\tif (ret < 0) {\n\t\t\tSIBR_WRG << \"[FFMPEG] Failed to encode frame.\" << std::endl;\n\t\t\treturn false;\n\t\t}\n\t\tif (got_picture == 1) {\n\t\t\tpkt->stream_index = video_st->index;\n\t\t\tret = av_write_frame(pFormatCtx, pkt);\n\t\t\tav_packet_unref(pkt);\n\t\t}\n\n\t\treturn true;\n\t}\n#endif\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/FFmpegVideoEncoder.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n\n#include <string>\n#include <core/graphics/Image.hpp>\n#include \"Video.hpp\"\n#include \"Config.hpp\"\n\n// Forward libav declarations.\nstruct AVFrame;\nstruct AVFormatContext;\nstruct AVOutputFormat;\nstruct AVStream;\nstruct AVCodecContext;\nstruct AVCodec;\nstruct AVPacket;\n\nnamespace sibr {\n\n\t\n\t/** Video encoder using ffmpeg.\n\tAdapted from https://github.com/leixiaohua1020/simplest_ffmpeg_video_encoder/blob/master/simplest_ffmpeg_video_encoder/simplest_ffmpeg_video_encoder.cpp\n\t\\ingroup sibr_video\n\t*/\n\tclass SIBR_VIDEO_EXPORT FFVideoEncoder {\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t\\param _filepath destination file, the extension will be used to infer the container type.\n\t\t\\param fps target video framerate\n\t\t\\param size target video size, should be even else a resize will happen\n\t\t\\param forceResize resize frames that are not at the target dimensions instead of ignoring them\n\t\t*/\n\t\tFFVideoEncoder(\n\t\t\tconst std::string & _filepath,\n\t\t\tdouble fps,\n\t\t\tconst sibr::Vector2i & size,\n\t\t\tbool forceResize = false\n\t\t);\n\n\t\t/** \\return true if the encoder was properly setup. */\n\t\tbool isFine() const;\n\n\t\t/** Close the file. */\n\t\tvoid close();\n\n\t\t/** Encode a frame.\n\t\t\\param frame the frame to encode\n\t\t\\return a success flag \n\t\t*/\n\t\tbool operator << (cv::Mat frame);\n\n\t\t/** Encode a frame.\n\t\t\\param frame the frame to encode\n\t\t\\return a success flag \n\t\t*/\n\t\tbool operator << (const sibr::ImageRGB & frame);\n\n\t\t/// Destructor.\n\t\t~FFVideoEncoder();\n\n\tprotected:\n\n\t\t/** Setup the encoder.\n\t\t\\param size the video target size, prfer using power of two.\n\t\t*/\n\t\tvoid init(const sibr::Vector2i & size);\n\t\t\n\t\t/** Encode a frame to the file.\n\t\t\\param frame the frame to encode\n\t\t\\return a success flag.\n\t\t*/\n//#define HEADLESS\n#ifndef HEADLESS\n\t\tbool encode(AVFrame *frame);\n#endif \n\n\t\tbool initWasFine = false; ///< Was the encoder init properly.\n\t\tbool needFree = false; ///< Is the file open.\n\t\tstd::string filepath; ///< Destination path.\n\t\tint w, h; ///< Dimensions.\n\t\tint frameCount = 0; ///< Current frame.\n\t\tdouble fps; ///< Framerate.\n\t\tbool _forceResize = false; ///< Resize frames.\n\t\t\n#ifndef HEADLESS\n\t\tAVFrame * frameYUV = NULL; ///< Working frame.\n#endif\n\t\tcv::Mat cvFrameYUV; ///< Working frame data.\n\t\tsibr::Vector2i yuSize; ///< Working size.\n\n#ifndef HEADLESS\n\t\tAVFormatContext* pFormatCtx; ///< Format context.\n\t\tAVOutputFormat* fmt; ///< Output format.\n\t\tAVStream* video_st; ///< Output stream.\n\t\tAVCodecContext* pCodecCtx; ///< Codec context.\n\t\tAVCodec* pCodec; ///< Codec.\n\t\tAVPacket * pkt; ///< Encoding packet.\n\t\t\n#endif\n\t\tstatic bool ffmpegInitDone; ///< FFMPEG initialization status.\n\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/MultipleVideoDecoder.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n\n#include <core/video/Video.hpp>\n\nnamespace sibr\n{\n\n\t/** Double-buffered texture, used to load/display video frames for instance.\n\t* \\ingroup sibr_video\n\t*/\n\ttemplate<uint N>\n\tstruct PingPongTexture {\n\t\tusing TexPtr = std::shared_ptr<sibr::Texture2D<uchar, N>>;\n\n\t\t/** \\return the current loading texture. */\n\t\tTexPtr & getLoadingTex();\n\n\t\t/** \\return the current display texture. */\n\t\tTexPtr & getDisplayTex();\n\n\t\t/** Update the content of the loading texture and swap the two textures.\n\t\t\\param frame the new data\n\t\t*/\n\t\ttemplate<typename ImgType>\n\t\tvoid update(const ImgType & frame);\n\n\t\t/** Load the frame into the loading texture.\n\t\t\\param frame the new data\n\t\t*/\n\t\ttemplate<typename ImgType>\n\t\tvoid updateGPU(const ImgType & frame);\n\t\t\n\t\tint displayTex = 1, loadingTex = 1; /// Textures indices.\n\t\tTexPtr ping, pong; ///< Textures.\n\t\tbool first = true; ///< First update.\n\t};\n\n\n\t/** Batch decoding of multiple videos at the same time, stored in a texture array.\n\t* \\ingroup sibr_video\n\t*/\n\ttemplate<typename T, uint N>\n\tstruct MultipleVideoDecoder {\n\t\tusing TexArray = sibr::Texture2DArray<T,N>;\n\t\tusing TexArrayPtr = typename TexArray::Ptr;\n\n\t\t/** Update a set of video players to the next frame.\n\t\t\\param videos the video players to udpate\n\t\t\\note Internally calls both updateCPU and updateGPU.\n\t\t*/\n\t\tvoid update(const std::vector<sibr::VideoPlayer::Ptr> & videos) {\n\t\t\tupdateCPU(videos);\n\t\t\tupdateGPU(videos);\n\n\t\t\tloadingTexArray = (loadingTexArray + 1) % 2;\n\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tdisplayTexArray = (displayTexArray + 1) % 2;\n\t\t\t}\n\t\t}\n\n\t\t/** Load the next frame on the CPU for a set of video players.\n\t\t\\param videos the video players to udpate\n\t\t*/\n\t\tvoid updateCPU(const std::vector<sibr::VideoPlayer::Ptr> & videos) {\n\t\t\tsize_t numVids = videos.size();\n\n\t\t\tfor (size_t i = 0; i < numVids; ++i) {\n\t\t\t\tvideos[i]->updateCPU();\n\t\t\t}\n\n\t\t}\n\n\t\t/** Upload the next frame to the GPU for a set of video players.\n\t\t\\param videos the video players to udpate\n\t\t*/\n\t\tvoid updateGPU(const std::vector<sibr::VideoPlayer::Ptr> & videos) {\n\t\t\tsize_t numVids = videos.size();\n\t\t\tstd::vector<cv::Mat> frames(numVids);\n\t\t\tfor (size_t i = 0; i < numVids; ++i) {\n\t\t\t\tif (std::is_same_v<T, uchar> && N == 3) {\n\t\t\t\t\tframes[i] = videos[i]->getCurrentFrame();\n\t\t\t\t} else {\n\t\t\t\t\tstd::vector<cv::Mat> cs;\n\t\t\t\t\tcv::split(videos[i]->getCurrentFrame(), cs);\n\t\t\t\t\tframes[i] = cs[0];\n\t\t\t\t}\t\t\t\n\t\t\t}\n\n\t\t\tif (getLoadingTexArray().get()) {\n\t\t\t\tgetLoadingTexArray()->updateFromImages(frames);\n\t\t\t} else {\n\t\t\t\tgetLoadingTexArray() = TexArrayPtr(new TexArray(frames));\n\t\t\t}\n\t\t}\n\n\t\t/** \\return the current loading texture array. */\n\t\tTexArrayPtr & getLoadingTexArray() { return loadingTexArray ? ping : pong; }\n\n\t\t/** \\return the current display texture array. */\n\t\tconst TexArrayPtr & getDisplayTexArray() const { return displayTexArray ? ping : pong; }\n\n\t\tbool first = true; ///< First frame.\n\t\tint loadingTexArray = 1, displayTexArray = 1; ///< Texture indices.\n\t\tTexArrayPtr ping, pong; ///< Textures.\n\t};\n\n\n\t/** Batch decoding of multiple videos at the same time, stored in a texture array.\n\t* Support updating an arbitrary subset. \n\t* \\ingroup sibr_video\n\t*/\n\ttemplate<typename T, uint N>\n\tstruct MultipleVideoDecoderArray : public MultipleVideoDecoder<T,N> {\n\t\tusing TexArray = sibr::Texture2DArray<T, N>;\n\t\tusing TexArrayPtr = typename TexArray::Ptr;\n\n\t\t/** Update a set of video players to the next frame.\n\t\t\\param videos the video players list\n\t\t\\param slices the indices of the videos to update\n\t\t\\note Internally calls both updateCPU and updateGPU.\n\t\t*/\n\t\tvoid update(const std::vector<sibr::VideoPlayer::Ptr> & videos, const std::vector<int> & slices) {\n\t\t\tupdateCPU(videos, slices);\n\t\t\tupdateGPU(videos, slices);\n\n\t\t\tloadingTexArray = (loadingTexArray + 1) % 2;\n\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tdisplayTexArray = (displayTexArray + 1) % 2;\n\t\t\t}\n\t\t}\n\n\t\t/** Load the next frame on the CPU for a set of video players.\n\t\t\\param videos the video players list\n\t\t\\param slices the indices of the videos to update\n\t\t*/\n\t\tvoid updateCPU(const std::vector<sibr::VideoPlayer::Ptr> & videos, const std::vector<int> & slices) {\n#pragma omp parallel for num_threads(4)\n\t\t\tfor (int i = 0; i < (int)slices.size(); ++i) {\n\t\t\t\tvideos[slices[i]]->updateCPU();\n\t\t\t}\n\t\t}\n\n\t\t/** Upload the next frame to the GPU for a set of video players.\n\t\t\\param videos the video players list\n\t\t\\param slices the indices of the videos to update\n\t\t*/\n\t\tvoid updateGPU(const std::vector<sibr::VideoPlayer::Ptr> & videos, const std::vector<int> & slices) {\n\t\t\tint numVids = (int)videos.size();\n\t\t\tint numSlices = (int)slices.size();\n\n\t\t\tstd::vector<cv::Mat> frames(numVids);\n\t\t\tfor (int s = 0; s < numSlices; ++s) {\n\t\t\t\tif (std::is_same_v<T, uchar> && N == 3) {\n\t\t\t\t\tframes[slices[s]] = videos[slices[s]]->getCurrentFrame();\n\t\t\t\t} else {\n\t\t\t\t\tstd::vector<cv::Mat> cs;\n\t\t\t\t\tcv::split(videos[slices[s]]->getCurrentFrame(), cs);\n\t\t\t\t\tframes[slices[s]] = cs[0];\n\t\t\t\t}\n\t\t\t} \n\n\t\t\tif (!getLoadingTexArray().get()) {\n\t\t\t\tgetLoadingTexArray() = TexArrayPtr(new TexArray((uint)videos.size(), SIBR_GPU_LINEAR_SAMPLING));\n\t\t\t}\n\n\t\t\tCHECK_GL_ERROR;\n\t\t\tgetLoadingTexArray()->updateSlices(frames, slices);\n\t\t}\n\n\t};\n\n\n\t// --- TYPEDEFS ----------------\n\n\tusing PingPong4u = PingPongTexture<4>;\n\tusing PingPong3u = PingPongTexture<3>;\n\tusing PingPong1u = PingPongTexture<1>;\n\tusing MultipleVideoDecoder1u = MultipleVideoDecoder<uchar, 1>;\n\tusing MultipleVideoDecoder3u = MultipleVideoDecoder<uchar, 3>;\n\tusing MultipleVideoDecoderArray1u = MultipleVideoDecoderArray<uchar, 1>;\n\tusing MultipleVideoDecoderArray3u = MultipleVideoDecoderArray<uchar, 3>;\n\n\t// --- IMPLEMENTATION ----------------\n\n\ttemplate<uint N>\n\tstd::shared_ptr<sibr::Texture2D<uchar, N>> & PingPongTexture<N>::getLoadingTex()\n\t{\n\t\treturn loadingTex ? ping : pong;\n\t}\n\n\ttemplate<uint N>\n\tstd::shared_ptr<sibr::Texture2D<uchar,N>> & PingPongTexture<N>::getDisplayTex()\n\t{\n\t\treturn displayTex ? ping : pong;\n\t}\n\n\ttemplate<uint N> template<typename ImgType>\n\tvoid PingPongTexture<N>::update(const ImgType & frame)\n\t{\n\t\tif (first) {\n\t\t\tupdateGPU(frame);\n\t\t\tloadingTex = (loadingTex + 1) % 2;\n\t\t\tfirst = false;\n\t\t\treturn;\n\t\t}\n\n\t\tupdateGPU(frame);\n\n\t\tdisplayTex = (displayTex + 1) % 2;\n\t\tloadingTex = (loadingTex + 1) % 2;\n\t}\n\n\ttemplate<uint N> template<typename ImgType>\n\tvoid PingPongTexture<N>::updateGPU(const ImgType & frame)\n\t{\n\t\tif (getLoadingTex()) {\n\t\t\tgetLoadingTex()->update(frame);\n\t\t} else {\n\t\t\tgetLoadingTex() = TexPtr(new sibr::Texture2D<uchar, N>(frame, SIBR_GPU_LINEAR_SAMPLING));\n\t\t}\n\t}\n\n } // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/Video.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"Video.hpp\"\n#include <opencv2/videoio.hpp>\n// #include \"VideoUtils.hpp\"\n\nnamespace sibr\n{\n\tbool Video::load(const std::string & path)\n\t{\n\t\tcap = cv::VideoCapture(path);\n\t\tfilepath = path;\n\t\tloaded = cap.isOpened();\n\t\tif (loaded) {\n\t\t\tnFrames = (int)cap.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT);\n\t\t\tframeRate = (double)cap.get(cv::VideoCaptureProperties::CAP_PROP_FPS);\n\t\t\tresolution[0] = (int)cap.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH);\n\t\t\tresolution[1] = (int)cap.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT);\n\t\t\tcodec = (int)cap.get(cv::VideoCaptureProperties::CAP_PROP_FOURCC);\n\t\t\tSIBR_LOG << \"[Video] \" << path << \" loaded.\" << std::endl;\n\t\t}\n\t\treturn loaded;\n\t}\n\n\tconst sibr::Vector2i & Video::getResolution() { \n\t\tcheckLoad();  \n\t\treturn resolution; \n\t}\n\n\tcv::Size Video::getResolutionCV() { \n\t\tcheckLoad();   \n\t\treturn cv::Size(resolution[0], resolution[1]);\n\t}\n\n\tint Video::getCurrentFrameNumber() { \n\t\tcheckLoad();  \n\t\treturn (int)cap.get(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES); \n\t}\n\t\n\tvoid Video::setCurrentFrame(int i){ \n\t\tcheckLoad(); \n\t\tcap.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, i); \n\t}\n\t\n\tint Video::getNumFrames()  { \n\t\tcheckLoad();  \n\t\treturn nFrames; \n\t}\n\t\n\tdouble Video::getFrameRate() { \n\t\tcheckLoad(); \n\t\treturn frameRate; \n\t}\n\t\n\tconst Path & Video::getFilepath() const { \n\t\treturn filepath; \n\t}\n\t\n\tbool Video::isLoaded() { \n\t\treturn loaded; \n\t}\n\n\tint Video::getCodec() { \n\t\tcheckLoad(); \n\t\treturn codec; \n\t}\n\n\tvoid Video::release()\n\t{\n\t\tcap = cv::VideoCapture();\n\t\tloaded = false;\n\t}\n\n\tcv::Mat Video::getVolume(float time_skiped_begin, float time_skiped_end)\n\t{\n\t\tconst int starting_frame = (int)(time_skiped_begin * getFrameRate());\n\t\tconst int finishing_frame = getNumFrames() - (int)(time_skiped_end*getFrameRate()) - 1;\n\t\treturn getVolume(starting_frame, finishing_frame);\n\t}\n\n\tcv::Mat Video::getVolume(int starting_frame, int ending_frame)\n\t{\n\t\tcheckLoad();\n\n\t\tconst int w = getResolution()[0];\n\t\tconst int h = getResolution()[1];\n\t\tconst int nc = 3;\n\n\t\tconst int npixels = w * h;\n\t\tconst int N = npixels * nc;\n\t\tconst int L = ending_frame - starting_frame + 1;\n\n\t\tcv::Mat volume(L, N, CV_8UC1);\n\t\tsetCurrentFrame(starting_frame);\n\t\tfor (int i = 0; i < L; ++i) {\n\t\t\tcv::Mat mat = volume.row(i).reshape(3, h);\n\t\t\tcap >> mat;\n\t\t}\n\t\tsetCurrentFrame(0);\n\n\t\treturn volume;\n\t}\n\n\tcv::Mat Video::next()\n\t{\n\t\tcheckLoad();\n\t\tcv::Mat frame;\n\t\tcap >> frame;\n\t\treturn frame;\n\t}\n\n\tcv::VideoCapture & Video::getCVvideo()\n\t{\n\t\tcheckLoad();\n\t\treturn cap;\n\t}\n\n\tbool Video::exists() const\n\t{\n\t\treturn sibr::fileExists(getFilepath().string());\n\t}\n\n\tvoid Video::checkLoad()\n\t{\n\t\tif (!loaded) {\n\t\t\tif (!load(filepath.string())) {\n\t\t\t\tSIBR_ERR << \"[Video] Could not open video \" << filepath << std::endl;\n\t\t\t}\t\n\t\t}\n\t}\n\n\n\t//------------------------------------------------------------\n\n\tVideoPlayer::VideoPlayer(const std::string & filepath, const std::function<cv::Mat(cv::Mat)> & f) :\n\t\tVideo(filepath), transformation(f)\n\t{\n\t}\n\n\tbool VideoPlayer::load(const std::string & path) {\n\t\tVideoPlayer other;\n\t\tif (other.Video::load(path)) {\n\t\t\t*this = other;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tconst std::shared_ptr<sibr::Texture2DRGB> & VideoPlayer::getDisplayTex() const\n\t{\n\t\treturn displayTex ? ping : pong;\n\t}\n\n\tvoid VideoPlayer::update()\n\t{\n\t\tcheckLoad();\n\n\t\tif (first) {\n\t\t\tloadNext();\n\t\t\tloadingTex = (loadingTex + 1) % 2;\n\t\t\tfirst = false;\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode != PLAY) {\n\t\t\treturn;\n\t\t}\n\n\t\tloadNext();\n\n\t\tdisplayTex = (displayTex + 1) % 2;\n\t\tloadingTex = (loadingTex + 1) % 2;\n\t}\n\n\tvoid VideoPlayer::onGui(float ratio_display)\n\t{\n\t\tcheckLoad();\n\n\t\tif (mode == PAUSE){\n\t\t\tif (ImGui::Button(\"Play\")) {\n\t\t\t\tmode = PLAY;\n\t\t\t}\n\t\t} else if (mode == PLAY) {\n\t\t\tif (ImGui::Button(\"Pause\")) {\n\t\t\t\tmode = PAUSE;\n\t\t\t}\n\t\t}\n\t\tImGui::SameLine();\n\t\tImGui::Checkbox(\"Repeat when finished\", &repeat_when_end);\n\n\t\tcurrent_frame_slider = getCurrentFrameNumber();\n\t\tImGui::Separator();\n\t\tImGui::PushScaledItemWidth(500);\n\t\tif (ImGui::SliderInt(\"timeline\", &current_frame_slider, 1, getNumFrames())) {\n\t\t\tsetCurrentFrame(current_frame_slider);\n\t\t\tloadingTex = displayTex;\n\t\t\tfirst = true;\n\t\t}\n\t\tImGui::PopItemWidth();\n\n\t\tImGui::Separator();\n\n\t\tif (getDisplayTex() && getDisplayTex()->handle() ) {\n\t\t\tstd::string infos = \"size : \" + std::to_string((int)getDisplayTex()->w()) + \" \" + std::to_string((int)getDisplayTex()->h()) + \", framerate : \" + std::to_string(getFrameRate());\n\t\t\tImGui::Text(infos.c_str());\n\t\t\tsibr::Vector2f displayTexSize(getDisplayTex()->w(), getDisplayTex()->h());\n\t\t\tsibr::Vector2i viewResolution = (ratio_display*displayTexSize).cast<int>();\n\t\t\t\n\t\t\tsibr::ImageWithCallback(getDisplayTex()->handle(), viewResolution, callBackData, zoomData.topLeft(), zoomData.bottomRight());\n\n\t\t\tupdateZoom(displayTexSize);\n\t\t}\n\n\t}\n\n\tbool VideoPlayer::updateCPU()\n\t{\n\n\t\tcheckLoad();\n\n\t\tbool alreayEmpty = tmpFrame.empty();\n\t\ttmpFrame = next();\n\t\tif (!tmpFrame.empty()) {\n\t\t\ttmpFrame = transformation(tmpFrame);\n\t\t\treturn true;\n\t\t} else {\n\t\t\tif (alreayEmpty) {\n\t\t\t\tSIBR_WRG << \"[Video] Could not load next frames.\" << std::endl;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (repeat_when_end) {\n\t\t\t\tsetCurrentFrame(0);\n\t\t\t\treturn updateCPU();\n\t\t\t} else {\n\t\t\t\tmode = PAUSE;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\t\n\t}\n\n\tvoid VideoPlayer::updateGPU()\n\t{\n\t\tif (getLoadingTex().get()) {\n\t\t\tgetLoadingTex()->update(tmpFrame);\n\t\t} else {\n\t\t\tgetLoadingTex() = std::shared_ptr<sibr::Texture2DRGB>(new sibr::Texture2DRGB(tmpFrame));\n\t\t}\n\t}\n\n\tvoid VideoPlayer::loadNext()\n\t{\n\t\tif (updateCPU()) {\n\t\t\tupdateGPU();\n\t\t}\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/Video.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n\n#include <core/graphics/Texture.hpp>\n#include <core/graphics/GUI.hpp>\n#include <opencv2/opencv.hpp>\n\n// must install ffdshow \n#define CV_WRITER_CODEC cv::VideoWriter::fourcc('F','F','D','S')\n\n\nnamespace sibr\n{\n\n\t/** Video loaded from a file using OpenCV VideoCapture and FFMPEG.\n\t* \\ingroup sibr_video\n\t*/\n\tclass SIBR_VIDEO_EXPORT Video\n\t{\n\t\tSIBR_CLASS_PTR(Video);\n\t\t\n\tpublic:\n\n\t\t/** Constructor.\n\t\t\\param path the path to the video file\n\t\t\\note No loading will be performed at construction. Call load.\n\t\t*/\n\t\tVideo(const std::string & path = \"\") : filepath(path) {}\n\n\t\t/** Load from a given file on disk.\n\t\t\\param path path to the video\n\t\t\\return a success flag\n\t\t*/\n\t\tvirtual bool load(const std::string & path);\n\t\t\n\t\t/** \\return the video resolution. */\n\t\tconst sibr::Vector2i & getResolution();\n\t\t\n\t\t/** \\return the video resolution. */\n\t\tcv::Size getResolutionCV();\n\n\t\t/** \\return the current frame ID. */\n\t\tint getCurrentFrameNumber();\n\n\t\t/** Seek a specific frame.\n\t\t\\param i the frame ID to seek\n\t\t*/\n\t\tvoid setCurrentFrame(int i);\n\n\t\t/** \\return the total number of frames. */\n\t\tint getNumFrames();\n\n\t\t/** \\return the video framerate. */\n\t\tdouble getFrameRate();\n\n\t\t/** \\return the path to the video file on disk. */\n\t\tconst Path & getFilepath() const;\n\n\t\t/** \\return true if the video has been loaded. */\n\t\tbool isLoaded();\n\n\t\t/** \\return the ID of the codec used to decode the video. */\n\t\tint getCodec();\n\n\t\t/** Stop reading from the file. */\n\t\tvirtual void release();\n\n\t\t/** Read a section of the video and store it in a cv::Mat, where\n\t\teach row contains a frame, stored as RGBRGBRGB... linearly.\n\t\t\\param time_skiped_begin time to skip at the beginning of the video, in seconds\n\t\t\\param time_skiped_end time to skip at the end of the video, in seconds\n\t\t\\return the frames data stored as described above.\n\t\t*/\n\t\tcv::Mat getVolume(float time_skiped_begin = 0, float time_skiped_end = 0);\n\n\t\t/** Read a section of the video and store  otin a cv::Mat, where\n\t\teach row contains a frame, stored as RGBRGBRGB... linearly.\n\t\t\\param starting_frame index of the first frame to extract\n\t\t\\param ending_frame index of the last frame to extract\n\t\t\\return the frames data stored as described above.\n\t\t*/\n\t\tcv::Mat getVolume(int starting_frame, int ending_frame);\n\n\t\t/** \\return the next frame. */\n\t\tcv::Mat next();\n\n\t\t/** \\return the underlying VideoCapture object. */\n\t\tcv::VideoCapture & getCVvideo();\n\n\t\t/** \\return true if the video exists on disk. */\n\t\tbool exists() const;\n\n\tprotected:\n\t\t\n\t\t/** Check if the video is loaded. */\n\t\tvirtual void checkLoad();\n\n\t\tcv::VideoCapture cap; ///< Internal capture object.\n\n\t\tPath filepath; ///< The path to the video.\n\t\tsibr::Vector2i resolution; ///< Video resolution.\n\t\tint nFrames = 0; ///< Number of frames in the video.\n\t\tdouble frameRate = 0.0; ///< Video frame rate.\n\t\tint codec = 0; ///< Codec used to read the video.\n\t\tbool loaded = false; ///< Video loading status.\n\t};\n\n\n\t/** Load and display a video in a view, with playback options.\n\t* \\ingroup sibr_video\n\t*/\n\tclass SIBR_VIDEO_EXPORT VideoPlayer : public Video, public ZoomInterraction\n\t{\n\n\t\tSIBR_CLASS_PTR(VideoPlayer);\n\n\tpublic:\n\n\t\t/** Replay mode. */\n\t\tenum Mode { PAUSE, PLAY, SHOULD_CLOSE };\n\n\t\tusing Transformation = std::function<cv::Mat(cv::Mat)>; ///< Image processing function.\n\n\t\t/** Constructor.\n\t\t\\param filepath the path to the video file\n\t\t\\param f a function to apply to each frame\n\t\t\\note No loading will be performed at construction. Call load.\n\t\t*/\n\t\tVideoPlayer(const std::string & filepath = \"\", const std::function<cv::Mat(cv::Mat)>&  f = [](cv::Mat m) { return m; });\n\t\t\n\t\t/** Load a video from disk.\n\t\t\\param path the path to the video on disk\n\t\t\\return a success flag\n\t\t*/\n\t\tbool load(const std::string & path) override;\n\n\t\t/** Set a transformation function to apply to each frame.\n\t\t\\param f the new transformation\n\t\t*/\n\t\tvoid setTransformation(const Transformation & f) { transformation = f; }\n\t\t\n\t\t/** Set the playback mode.\n\t\t\\param _mode the new mode\n\t\t*/\n\t\tvoid setMode(Mode _mode) { mode = _mode; }\n\n\t\t/** \\return the current display texture on the GPU. */\n\t\tconst std::shared_ptr<sibr::Texture2DRGB> & getDisplayTex() const;\n\n\t\t/** Load the next frame, call once per rendering frame. \n\t\t\\note Internally calls updateCPU and updateGPU.\n\t\t*/\n\t\tvoid update();\n\n\t\t/** Display playback GUI.\n\t\t\\param ratio_display a scaling factor that determine the size of the video on screen based on the video intrinsic size.\n\t\t*/\n\t\tvoid onGui(float ratio_display);\n\t\t\n\t\t/** Load the next frame to the CPU.\n\t\t\\return a success flag\n\t\t*/\n\t\tbool updateCPU();\n\n\t\t/** Load the next frame to the GPU.\n\t\t\\note You should call updateCPU first.\n\t\t*/\n\t\tvoid updateGPU();\n\n\t\t/** \\return a reference to the current frame on the CPU. */\n\t\tconst cv::Mat & getCurrentFrame() const { return tmpFrame; }\n\n\tprotected:\n\t\t\n\t\t/** \\return the current loading texture on the GPU. */\n\t\tstd::shared_ptr<sibr::Texture2DRGB> & getLoadingTex() { return loadingTex ? ping : pong; }\n\t\t\n\t\t/// Load the next frame, on the CPU then the GPU.\n\t\tvoid loadNext();\n\n\t\tMode mode = PAUSE; ///< Play mode.\n\t\tbool first = true; ///< Are we at the first frame.\n\t\tbool repeat_when_end = true; ///< Loop when reaching the end.\n\t\tint displayTex = 1; ///< Index of the display texture.\n\t\tint loadingTex = 1; ///< Index of the loading texture.\n\t\tstd::shared_ptr<sibr::Texture2DRGB> ping,pong; ///< Double buffer textures.\n\t\tcv::Mat tmpFrame; ///< Scratch frame.\n\t\tTransformation transformation; ///< Transformation to apply to each frame.\n\t\tint current_frame_slider; ///< Slider position.\n\t};\n\n\t\n } // namespace sibr\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/VideoUtils.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"VideoUtils.hpp\"\n\n#include <core/graphics/Utils.hpp>\n#include <algorithm>\n\n\n#include <opencv2/ximgproc/edge_filter.hpp>\n#include <opencv2/optflow.hpp>\nnamespace sibr {\n\n\tstd::vector<cv::Mat> cvSplitChannels(cv::Mat mat) {\n\t\tstd::vector<cv::Mat> out;\n\t\tcv::split(mat, out);\n\t\treturn out;\n\t}\n\n\tVolume3u loadVideoVolume(const std::string & filepath) {\n\t\tVideo video(filepath);\n\t\tif (video.exists()) {\n\t\t\treturn loadVideoVolume(video);\n\t\t} else {\n\t\t\tSIBR_WRG << filepath << \" does not exists\" << std::endl;\n\t\t\treturn Volume3u();\n\t\t}\n\t}\n\n\tVolume3u loadVideoVolume(sibr::Video & video)\n\t{\n\t\tint currentFrame = video.getCurrentFrameNumber();\n\t\tvideo.setCurrentFrame(0);\n\t\tVolume3u volume(video.getNumFrames(), video.getResolution()[0], video.getResolution()[1]);\n\t\tfor (int t = 0; t < video.getNumFrames(); ++t) {\n\t\t\tcv::Mat mat = volume.frame(t);\n\t\t\tvideo.getCVvideo() >> mat;\n\t\t}\n\t\tvideo.setCurrentFrame(currentFrame);\n\t\treturn volume;\n\t}\n\n\tSIBR_VIDEO_EXPORT uint optimal_num_levels(uint length)\n\t{\n\t\tuint num_levels = 1;\n\t\twhile (length != 1) {\n\t\t\tlength = (length + 1) / 2;\n\t\t\t++num_levels;\n\t\t}\n\t\treturn num_levels;\n\t}\n\n\tSIBR_VIDEO_EXPORT std::vector<sibr::Volume3u> gaussianPyramid(const sibr::Volume3u & vid, uint num_levels)\n\t{\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::Volume3u> out(1, vid);\n\t\tfor (int i = 1; i < (int)num_levels; ++i) {\n\t\t\tout.push_back(out.back().pyrDown());\n\t\t}\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT std::vector<sibr::Volume3u> gaussianPyramidTemporal(const sibr::Volume3u & vid, uint num_levels)\n\t{\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::Volume3u> out(1, vid);\n\t\tfor (int i = 1; i < (int)num_levels; ++i) {\n\t\t\tout.push_back(out.back().pyrDownTemporal());\n\t\t}\n\t\treturn out;\n\t}\n\n\tstd::vector<sibr::Volume3u> laplacianPyramid(const sibr::Volume3u & vid, uint num_levels)\n\t{\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::Volume3u> out;\n\t\t\n\t\tsibr::Volume3f current_v = vid.convertTo<float>(), down, up;\n\t\tfor (int i = 0; i < (int)num_levels - 1; ++i) {\n\t\t\tdown = current_v.pyrDown();\n\t\t\tup = down.pyrUp(current_v.l, current_v.w, current_v.h);\n\t\t\t//current_v.play(30, { 1200,800 });\n\t\t\t//up.play(30, { 1200,800 });\n\t\t\tcurrent_v.substract(up);\n\t\t\tcurrent_v.shift(128);\n\t\t\tout.push_back(current_v.convertTo<uchar>());\n\t\t\tstd::swap(current_v, down);\n\t\t}\n\t\tout.push_back(current_v.convertTo<uchar>());\n\t\treturn out;\n\t}\n\n\tstd::vector<sibr::Volume3u> laplacianPyramidTemporalDouble(const sibr::Volume3u & vid, uint num_levels)\n\t{\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = 1;\n\t\t\tint length = vid.l;\n\t\t\twhile (length != 1) {\n\t\t\t\tlength = (length + 1) / 2;\n\t\t\t\tlength = (length + 1) / 2;\n\t\t\t\t++num_levels;\n\t\t\t}\n\t\t}\n\n\t\tstd::cout << \" num lvls : \" << num_levels << std::endl;\n\n\t\tstd::vector<sibr::Volume3u> out;\n\t\tsibr::Volume3f current_v = vid.convertTo<float>(), down, up;\n\t\tfor (int i = 0; i < (int)num_levels - 1; ++i) {\n\t\t\tdown = current_v.pyrDownTemporal().pyrDownTemporal();\n\t\t\tup = down.pyrUpTemporal((current_v.l + 1) / 2).pyrUpTemporal(current_v.l);\n\t\t\tcurrent_v.substract(up);\n\t\t\tcurrent_v.shift(128);\n\t\t\tout.push_back(current_v.convertTo<uchar>());\n\t\t\tstd::cout << i << \" : \" << current_v.l << std::endl;\n\t\t\tstd::swap(current_v, down);\n\t\t}\n\t\tout.push_back(current_v.convertTo<uchar>());\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT sibr::Volume3u collapseLaplacianPyramid(const std::vector<sibr::Volume3u>& pyr, double shift)\n\t{\n\t\tsibr::Volume3f v = pyr.back().convertTo<float>();\n\t\tfor (int i = (int)pyr.size() - 2; i >= 0; --i) {\n\t\t\tv = v.pyrUp(pyr[i].l, pyr[i].w, pyr[i].h);\n\t\t\tv.add(pyr[i]);\n\t\t\tif (shift != 0) {\n\t\t\t\tv.shift(shift);\n\t\t\t}\n\t\t}\n\t\treturn v.convertTo<uchar>();\n\t}\n\n\tSIBR_VIDEO_EXPORT sibr::Volume3u laplacianBlending(const sibr::Volume3u & vA, const sibr::Volume3u & vB, std::vector<sibr::Volume1u>& pyrM)\n\t{\n\t\tauto pyrA = laplacianPyramid(vA);\n\t\tauto pyrB = laplacianPyramid(vB);\n\n\t\tfor (int i = (int)pyrA.size() - 1; i >= 0; --i) {\n\t\t\tpyrA[i] = pyrA[i].applyMask(pyrM[i]);\n\t\t\tpyrM[i].toggle();\n\t\t\tpyrB[i] = pyrB[i].applyMask(pyrM[i]);\n\t\t\tpyrA[i].add(pyrB[i]);\n\t\t}\n\n\t\treturn collapseLaplacianPyramid(pyrA, -128);\n\t}\n\n\n\tint VideoUtils::codec_ffdshow = cv::VideoWriter::fourcc('F', 'F', 'D', 'S');\n\tint VideoUtils::codec_OpenH264 = cv::VideoWriter::fourcc('H', '2', '6', '4');\n\tint VideoUtils::codec_OpenH264_fallback = 0x31637661;\n\n\t// from https://stackoverflow.com/questions/7693561/opencv-displaying-a-2-channel-image-optical-flow\n\tcv::Mat VideoUtils::getFlowViz(const cv::Mat & flow) {\n\n\t\tcv::Mat xy[2]; //X,Y\n\t\tcv::split(flow, xy);\n\n\t\t//calculate angle and magnitude\n\t\tcv::Mat magnitude, angle;\n\t\tcv::cartToPolar(xy[0], xy[1], magnitude, angle, true);\n\n\t\t//translate magnitude to range [0;1]\n\t\tdouble mag_max;\n\t\tcv::minMaxLoc(magnitude, 0, &mag_max);\n\t\tmagnitude.convertTo(magnitude, -1, 1.0 / mag_max);\n\n\t\t//build hsv image\n\t\tcv::Mat _hsv[3], hsv;\n\t\t_hsv[0] = angle;\n\t\t_hsv[1] = magnitude;\n\t\t_hsv[2] = cv::Mat::ones(angle.size(), CV_32F);\n\t\tcv::merge(_hsv, 3, hsv);\n\n\t\t//convert to BGR and show\n\t\tcv::Mat bgr;//CV_32FC3 matrix\n\t\tcv::cvtColor(hsv, bgr, cv::COLOR_HSV2BGR);\n\n\t\treturn bgr;\n\t}\n\n\tcv::Mat VideoUtils::cropFromSize(const cv::Mat & mat, const sibr::Vector2i & size)\n\t{\n\t\tsibr::Vector2i currentSize(mat.cols, mat.rows);\n\t\tsibr::Vector2i targetSize = size.cwiseMin(currentSize);\n\t\tsibr::Vector2i topLeft = ((currentSize - targetSize).cast<float>() / 2.0).cast<int>();\n\t\tsibr::Vector2i bottomRight = ((currentSize + targetSize).cast<float>() / 2.0).cast<int>();\n\t\tcv::Rect roi(cv::Point(topLeft[0], topLeft[1]), cv::Point(bottomRight[0], bottomRight[1]));\n\n\t\t//std::cout << \"--------------\" << std::endl;\n\t\t//std::cout << currentSize << std::endl;\n\t\t//std::cout << targetSize << std::endl;\n\t\t//std::cout << roi << std::endl;\n\n\t\treturn mat(roi).clone();\n\t}\n\n\tvoid VideoUtils::getMeanVariance(cv::VideoCapture & cap, cv::Mat & outMean, cv::Mat & outVariance, const sibr::Vector2i & finalSize)\n\t{\n\t\tcap.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, 0);\n\t\tbool first = true;\n\t\tcv::Mat mean, meanSq, out;\n\t\tfloat sum = 0;\n\t\tint f_id = 0, current_seg = -1;\n\t\tbool doResize = (finalSize[0] != cap.get(cv::CAP_PROP_FRAME_WIDTH) || finalSize[1] != cap.get(cv::CAP_PROP_FRAME_HEIGHT));\n\t\twhile (true) {\n\t\t\tstd::cout << \".\" << std::flush;\n\t\t\tcv::Mat frame, frame_float;\n\t\t\tcap >> frame;\n\t\t\t++f_id;\n\t\t\tif (frame.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (doResize) {\n\t\t\t\tcv::resize(frame, frame, cv::Size(finalSize[0], finalSize[1]));\n\t\t\t}\n\n\t\t\tframe.convertTo(frame_float, CV_32FC3);\n\t\t\tif (first) {\n\t\t\t\tmean = frame_float;\n\t\t\t\tmeanSq = frame_float.mul(frame_float);\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tmean += frame_float;\n\t\t\t\tmeanSq += frame_float.mul(frame_float);\n\t\t\t}\n\t\t\tsum += 1;\n\t\t}\n\t\tif (first) {\n\t\t\treturn;\n\t\t}\n\n\t\tmean /= sum;\n\t\tcv::Mat var = cv::min(255.0f*255.0f, cv::max(0.0f, meanSq / sum - mean.mul(mean)));\n\t\tcv::sqrt(var, var);\n\t\tvar *= 5.0;\n\t\tmean.convertTo(outMean, CV_8UC3);\n\t\tvar.convertTo(outVariance, CV_8UC3);\n\n\t}\n\n\tvoid VideoUtils::getMeanVariance2(cv::VideoCapture & cap, cv::Mat & outMean, cv::Mat & outVariance, const sibr::Vector2i & finalSize, float starting_point_s)\n\t{\n\t\tint starting_frame = (int)(starting_point_s*cap.get(cv::CAP_PROP_FPS));\n\n\t\tcap.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, starting_frame);\n\t\tbool first = true;\n\t\tcv::Mat mean, meanSq, out;\n\t\tfloat sum = 0;\n\t\tint f_id = 0, current_seg = -1;\n\t\tbool doResize = (finalSize[0] != cap.get(cv::CAP_PROP_FRAME_WIDTH) || finalSize[1] != cap.get(cv::CAP_PROP_FRAME_HEIGHT));\n\t\twhile (true) {\n\t\t\tstd::cout << \".\" << std::flush;\n\t\t\tcv::Mat frame, frame_float;\n\t\t\tcap >> frame;\n\t\t\t++f_id;\n\t\t\tif (frame.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (doResize) {\n\t\t\t\tcv::resize(frame, frame, cv::Size(finalSize[0], finalSize[1]));\n\t\t\t}\n\n\t\t\tcv::GaussianBlur(frame, frame, cv::Size(3, 3), 0);\n\n\t\t\tframe.convertTo(frame_float, CV_32FC3);\n\t\t\tif (first) {\n\t\t\t\tmean = frame_float;\n\t\t\t\tmeanSq = frame_float.mul(frame_float);\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tmean += frame_float;\n\t\t\t\tmeanSq += frame_float.mul(frame_float);\n\t\t\t}\n\t\t\tsum += 1;\n\t\t}\n\t\tif (first) {\n\t\t\treturn;\n\t\t}\n\n\t\tmean /= sum;\n\t\tcv::Mat var = cv::min(255.0f*255.0f, cv::max(0.0f, meanSq / sum - mean.mul(mean)));\n\t\tcv::sqrt(var, var);\n\t\tvar *= 5.0;\n\t\tmean.convertTo(outMean, CV_8UC3);\n\t\tvar.convertTo(outVariance, CV_8UC3);\n\t}\n\n\tcv::Mat VideoUtils::getMedian(sibr::Video & vid, float time_skiped_begin, float time_skiped_end) {\n\n\t\tcv::Mat volume = vid.getVolume(time_skiped_begin, time_skiped_end);\n\n\t\t//std::cout << \"tranpose \";\n\t\t//volume = volume.t();\n\t\t//std::cout << t.deltaTimeFromLastTic<>() << std::endl;\n\n\t\t//cv::Mat volumeSorted;\n\n\t\t//std::cout << \"sort \";\n\t\t//cv::sort(volume, volume, CV_SORT_EVERY_COLUMN);\n\t\t//std::cout << t.deltaTimeFromLastTic<>() << std::endl;\n\n\t\tcv::Mat median(vid.getResolutionCV(), CV_8UC3);\n\n\t\tconst int L = volume.rows;\n\n#pragma omp parallel for\n\t\tfor (int i = 0; i < median.rows; ++i) {\n\n\t\t\tfor (int j = 0; j < median.cols; ++j) {\n\t\t\t\tcv::Vec3b medianColor;\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tstd::vector<uchar> values(L);\n\t\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\t\tvalues[t] = volume.at<uchar>(t, 3 * (i*median.cols + j) + c);\n\t\t\t\t\t}\n\t\t\t\t\t//std::sort(values.begin(), values.end());\n\t\t\t\t\t//medianColor[c] = values[values.size() / 2];\n\t\t\t\t\tstd::nth_element(values.begin(), values.begin() + L / 2, values.end());\n\t\t\t\t\tmedianColor[c] = values[L / 2];\n\t\t\t\t}\n\t\t\t\tmedian.at<cv::Vec3b>(i, j) = medianColor;\n\t\t\t}\n\n\n\t\t}\n\n\t\treturn median;\n\t}\n\n\tcv::Mat3b VideoUtils::getMedian(const std::string & path, float time_percentage_crop)\n\t{\n\n\t\tsibr::Video vid(path);\n\t\tVolume3u vol = loadVideoVolume(vid);\n\t\tcv::Mat3b median(vid.getResolutionCV(), CV_8UC3);\n\n\t\tint crop = (int)(vol.l*std::min(time_percentage_crop, 0.4f));\n\t\tint start = crop, end = vol.l - crop;\n\n\t\n#pragma omp parallel for\n\t\tfor (int i = 0; i < median.rows; ++i) {\n\t\t\tcv::Mat line = vol.video_line(i);\n\t\t\tstd::vector<uchar> values;\n\t\t\tfor (int j = 0; j < median.cols; ++j) {\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tline.col(3 * j + c).rowRange(start, end).copyTo(values);\n\t\t\t\t\tstd::nth_element(values.begin(), values.begin() + values.size() / 2, values.end());\n\t\t\t\t\tmedian(i, j)[c] = values[values.size() / 2];\n\t\t\t\t}\t\t\n\t\t\t}\n\t\t}\n\n\t\treturn median;\n\t}\n\n\tcv::Mat VideoUtils::getBackgroundImage(sibr::Video & vid, int numBins, float time_skip_begin, float time_skip_end) {\n\t\tcv::Mat volume = vid.getVolume(time_skip_begin, time_skip_end);\n\t\tvolume = volume.t();\n\t\treturn getBackgroundImage(volume, vid.getResolution()[0], vid.getResolution()[1], numBins);\n\t}\n\n\tcv::Mat VideoUtils::getBackgroundImage(const cv::Mat volume, int w, int h, int numBins)\n\t{\n\t\tcv::Mat bg = cv::Mat(h, w, CV_8UC3);\n\t\tconst int L = volume.cols;\n\n#pragma omp parallel for\n\t\tfor (int i = 0; i < bg.rows; ++i) {\n\t\t\tfor (int j = 0; j < bg.cols; ++j) {\n\n\t\t\t\tstd::vector<sibr::Vector3ub> values(L);\n\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\t\tvalues[t][c] = volume.at<uchar>(3 * (i*bg.cols + j) + c, t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tTimeHistogram histo = TimeHistogram(0, 255, numBins);\n\t\t\t\thisto.addValues(values);\n\n\t\t\t\tauto mode = histo.getBinMiddle(histo.getHMode());\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tbg.at<cv::Vec3b>(i, j)[c] = mode[c];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn bg;\n\t}\n\n\tvoid VideoUtils::getBackGroundVideo(sibr::Video & vid, PyramidLayer & out_mask, PyramidLayer & out_video, cv::Mat & out_img,\n\t\tconst sibr::ImageRGB & meanImg, int threshold, int numBins, float time_skip_begin, float time_skip_end)\n\t{\n\t\tcv::Mat volume = vid.getVolume(time_skip_begin, time_skip_end).t();\n\n\t\tconst int w = vid.getResolution()[0], h = vid.getResolution()[1], L = volume.cols;\n\t\tout_mask.w = w;\n\t\tout_mask.l = L;\n\t\tout_mask.h = h;\n\n\t\tout_video = out_mask;\n\n\t\tout_mask.volume = cv::Mat(L, 3 * w*h, CV_8UC1);\n\t\tout_video.volume = cv::Mat(L, 3 * w*h, CV_8UC1);\n\t\tout_img = cv::Mat(h, w, CV_8UC3);\n\n\t\tconst bool useMeanImg = !(meanImg.size()[0] == 0);\n\n\t\tstd::cout << w << \" \" << h << \" \" << L << \" use mean img \" << useMeanImg << std::endl;\n#pragma omp parallel for\n\t\tfor (int i = 0; i < h; ++i) {\n\t\t\tfor (int j = 0; j < w; ++j) {\n\n\t\t\t\tstd::vector<sibr::Vector3ub> values(L);\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\t\tvalues[t][c] = volume.at<uchar>(3 * (i*w + j) + c, t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tTimeHistogram histo = TimeHistogram(0, 255, numBins);\n\t\t\t\thisto.addValues(values);\n\t\t\t\t//histo.computeSortedBins();\n\n\t\t\t\tauto mode = histo.getHMode();\n\t\t\t\tauto mode_color = histo.getBinMiddle(histo.getHMode());\n\n\t\t\t\tout_img.at<cv::Vec3b>(i, j) = sibr::toOpenCV<uchar, uchar, 3>(mode_color);\n\n\t\t\t\tsibr::Vector3ub stdDev;\n\n\t\t\t\tif (useMeanImg) {\n\t\t\t\t\tconst int radius = 4;\n\t\t\t\t\tconst int diam = 2 * radius + 1;\n\t\t\t\t\tconst int num = diam * diam;\n\t\t\t\t\tsibr::Vector3f sumColor(0, 0, 0), sumColorSq(0, 0, 0);\n\t\t\t\t\tfor (int di = -radius; di <= radius; ++di) {\n\t\t\t\t\t\tint ii = sibr::clamp(i + di, 0, h - 1);\n\t\t\t\t\t\tfor (int dj = -radius; dj <= radius; ++dj) {\n\t\t\t\t\t\t\tint jj = sibr::clamp(j + dj, 0, w - 1);\n\t\t\t\t\t\t\tsumColor += meanImg(jj, ii).cast<float>();\n\t\t\t\t\t\t\tsumColorSq += meanImg(jj, ii).cast<float>().cwiseProduct(meanImg(jj, ii).cast<float>());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsumColor /= (float)(num);\n\t\t\t\t\tsumColorSq = sumColorSq / (float)num - sumColor.cwiseProduct(sumColor);\n\t\t\t\t\tstdDev = sumColorSq.cwiseSqrt().cast<uchar>();\n\t\t\t\t\tthreshold = 15 * stdDev.norm();\n\t\t\t\t}\n\n\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\tconst auto & color = values[t];\n\t\t\t\t\tauto bin = histo.whatBin(color);\n\n\t\t\t\t\t//float cdf = histo.sorted_bins[bin];\n\t\t\t\t\t//float outlier_prop = 1.0f - cdf;\n\t\t\t\t\t//auto viz_color = sibr::jetColor<uchar>(outlier_prop);\n\n\t\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\t\t//out_mask.volume.at<uchar>(t, 3 * (i*w + j) + c) = (cdf < 0.75f ? 0 : 255);\n\t\t\t\t\t\t//out_video.volume.at<uchar>(t, 3 * (i*w + j) + c) = viz_color[c];\n\n\t\t\t\t\t\tif ((color.cast<int>() - mode_color.cast<int>()).norm() < threshold) {\n\t\t\t\t\t\t\tout_mask.volume.at<uchar>(t, 3 * (i*w + j) + c) = 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tout_mask.volume.at<uchar>(t, 3 * (i*w + j) + c) = 255;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\tvolume = volume.t();\n\t\tout_video.volume = volume.mul((1.0 / 255)*out_mask.volume);\n\t}\n\n\tsibr::Volume1u VideoUtils::getBackgroundVolume(const sibr::Volume3u & volume, int threshold, int numBins)\n\t{\n\t\tconst int L = volume.l;\n\t\tsibr::Volume1u out_mask = sibr::Volume1u(L, volume.w, volume.h, 0);\n\n#pragma omp parallel for\n\t\tfor (int i = 0; i < volume.h; ++i) {\n\t\t\tfor (int j = 0; j < volume.w; ++j) {\n\t\t\t\tstd::vector<sibr::Vector3ub> values(L);\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\t\tvalues[t][c] = volume.valueAt(t, i, j, c);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tTimeHistogram histo = TimeHistogram(0, 255, numBins);\n\t\t\t\thisto.addValues(values);\n\n\t\t\t\tauto mode_color = histo.getBinMiddle(histo.getHMode());\n\n\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\tconst auto & color = values[t];\n\n\t\t\t\t\tif ((color.cast<int>() - mode_color.cast<int>()).norm() > threshold) {\n\t\t\t\t\t\tout_mask.pixelAt(t, i, j) = 255;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\treturn out_mask;\n\t}\n\n\tsibr::Volume1f VideoUtils::getBackgroundVolumeF(const sibr::Volume3u & volume, int numBins)\n\t{\n\t\tconst int L = volume.l;\n\t\tsibr::Volume1f out_mask = sibr::Volume1f(L, volume.w, volume.h);\n\n#pragma omp parallel for\n\t\tfor (int i = 0; i < volume.h; ++i) {\n\t\t\tfor (int j = 0; j < volume.w; ++j) {\n\t\t\t\tstd::vector<sibr::Vector3ub> values(L);\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\t\tvalues[t][c] = volume.valueAt(t, i, j, c);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tTimeHistogram histo = TimeHistogram(0, 255, numBins);\n\t\t\t\thisto.addValues(values);\n\n\t\t\t\tauto mode_color = histo.getBinMiddle(histo.getHMode());\n\n\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\tconst auto & color = values[t];\n\t\t\t\t\tout_mask.pixelAt(t, i, j) = (float)(color.cast<int>() - mode_color.cast<int>()).norm();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\treturn out_mask;\n\t}\n\n\tvoid VideoUtils::computeSaveSimpleFlow(sibr::Video & vid, bool show)\n\t{\n\t\tint layers = 5;\n\t\tint block_size = 3;\n\t\tint max_flow = 5;\n\n\t\tsibr::Volume3u vol = sibr::loadVideoVolume(vid);\n\t\tPath path = vid.getFilepath();\n\t\tstd::string folder = path.parent_path().string() + \"/flow/\";\n\t\tsibr::makeDirectory(folder);\n\n\t\tstd::string filepath = folder + \"/\" + path.stem().string() + \"_sflow_\" + std::to_string(layers) + \"_\" +\n\t\t\tstd::to_string(block_size) + + \"_\" + std::to_string(max_flow) + \".mp4\";\n\n\t\tsibr::FFVideoEncoder encoder(filepath, 30, { 2 * vol.w,2 * vol.h });\n\t\tfor (int t = 0; t < vol.l - 1; ++t) {\n\t\t\tcv::Mat flow;\n\t\t\tcv::optflow::calcOpticalFlowSF(vol.frame(t), vol.frame(t + 1), flow, layers, block_size, max_flow);\n\t\t\tcv::Mat viz = vol.frame(t).clone();\n\t\t\tint r = 10;\n\t\t\tfor (int i = 0; i < vol.h; i += r) {\n\t\t\t\tfor (int j = 0; j < vol.w; j += r) {\n\t\t\t\t\tauto f = flow.at<cv::Vec2f>(i, j);\n\t\t\t\t\tif (isfinite(f[0]) && isfinite(f[1])) {\n\t\t\t\t\t\tif (cv::norm(f) > 0.5) {\n\t\t\t\t\t\t\tcv::line(viz, cv::Point(j, i), cv::Point(int(j + f[1]), int(i + f[0])), { 255,0,255 }, 2);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcv::circle(viz, cv::Point(j, i), 3, { 0,0,0 }, 2);\n\t\t\t\t\t}\n\n\n\t\t\t\t}\n\t\t\t}\n\t\t\tcv::resize(viz, viz, cv::Size(2 * viz.cols, 2 * viz.rows), 0, 0, cv::INTER_NEAREST);\n\n\t\t\tif (show) {\n\t\t\t\tcv::imshow(\"flow\", viz);\n\t\t\t\tif (cv::waitKey() == 27) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tencoder << viz;\n\t\t\t}\n\t\t\tstd::cout << \".\" << std::flush;\n\t\t}\n\t\tstd::cout << \"done \" << std::endl;\n\t}\n\n\tvoid VideoUtils::computeSaveVideoMaskF(Video & vid, int threshold, bool viz)\n\t{\n\t\tsibr::Volume3u volume = sibr::loadVideoVolume(vid);\n\t\tsibr::Volume1f mask = sibr::VideoUtils::getBackgroundVolumeF(volume, 150);\n\n\t\tsibr::Volume1f bilateral_mask(volume.l, volume.w, volume.h);\n\t\tsibr::Volume1f bilateral_mask_median(volume.l, volume.w, volume.h);\n\t\tsibr::Volume1u median_bilateral_mask_binary(volume.l, volume.w, volume.h);\n\n\t\tconst int radius_bila = 21;\n\t\tconst double eps = 10;\n\n#pragma omp parallel for\n\t\tfor (int t = 0; t < volume.l; ++t) {\n\t\t\tcv::ximgproc::guidedFilter(volume.frame(t), mask.frame(t), bilateral_mask.frame(t), radius_bila, eps);\n\t\t\t//cv::medianBlur(mask.frame(t), median_mask.frame(t), 7);\n\t\t\tcv::medianBlur(bilateral_mask.frame(t), bilateral_mask_median.frame(t), 5);\n\t\t\tmedian_bilateral_mask_binary.frame(t) = bilateral_mask_median.frame(t) > threshold;\n\t\t}\n\n\t\tsibr::Volume3u video_masked_bilateral_bin = volume.applyMaskBinary(median_bilateral_mask_binary);\n\n\t\tif (viz) {\n\t\t\tbilateral_mask.play();\n\t\t\tbilateral_mask_median.play();\n\t\t\tmedian_bilateral_mask_binary.play();\n\t\t\tvideo_masked_bilateral_bin.play();\n\t\t}\n\n\t\tPath filepath = vid.getFilepath();\n\t\tconst std::string folder = filepath.parent_path().string() + \"/masks/bilateral/\";\n\t\tsibr::makeDirectory(folder);\n\n\t\tconst std::string basename = folder + filepath.stem().string() + \"_bila_\" + std::to_string(radius_bila) + \"_\" + std::to_string((int)(10 * eps));\n\t\tconst std::string extension = \".mp4\";\n\n\t\tbilateral_mask.saveToVideoFile(basename + \"_raw\" + extension);\n\t\tbilateral_mask_median.saveToVideoFile(basename + \"_median\" + extension);\n\t\tmedian_bilateral_mask_binary.saveToVideoFile(basename + \"_median_binary\" + extension);\n\t\tvideo_masked_bilateral_bin.saveToVideoFile(basename + \"_video\" + extension);\n\t}\n\n\tvoid VideoUtils::computeSaveVideoMaskBlur(Video & vid, int time_window)\n\t{\n\t\tconst Path & filepath = vid.getFilepath();\n\t\tconst std::string in_filename = filepath.parent_path().string() + \"/masks/bilateral/\" + filepath.stem().string() + \"_bila_21_100_median_binary.mp4\";\n\t\tconst std::string out_folder = filepath.parent_path().string() + \"/masks/bilateral_tblur/\";\n\t\tsibr::makeDirectory(out_folder);\n\t\tconst std::string out_filename = out_folder + \"/\" + filepath.stem().string() + \"_mask_tblur.mp4\";\n\n\t\tsibr::Volume3u volume = sibr::loadVideoVolume(in_filename);\n\t\tstd::cout << \"volume.mat.isContinuous() : \" << volume.mat.isContinuous() << std::endl;\n\t\tsibr::Volume3u out = sibr::Volume3u(volume.l, volume.w, volume.h, 0);\n\n\t\tint time_win = 10;\n\n\t\t//#pragma omp parallel for\n\t\tfor (int i = 0; i < out.h; ++i) {\n\t\t\tfor (int j = 0; j < out.w; ++j) {\n\t\t\t\tfor (int t = 0; t < out.l; ++t) {\n\t\t\t\t\tfor (int u = std::max(0, t - time_win); u < std::min(out.l - 1, t + time_win); ++u) {\n\t\t\t\t\t\tif (volume.valueAt(u, i, j, 0) > 128) {\n\t\t\t\t\t\t\tout.pixelAt(t, i, j) = cv::Vec3b(255, 255, 255);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tout.saveToVideoFile(out_filename);\n\t}\n\n\tcv::Mat VideoUtils::getTemporalSpatialRatio(sibr::Video & vid, PyramidLayer & out_ratio, const sibr::ImageRGB & spatial_ratio, int numBins, float time_skip_begin, float time_skip_end)\n\t{\n\t\tcv::Mat volume = vid.getVolume(time_skip_begin, time_skip_end).t();\n\n\t\tconst int w = vid.getResolution()[0], h = vid.getResolution()[1], L = volume.cols;\n\t\tout_ratio.w = w;\n\t\tout_ratio.l = L;\n\t\tout_ratio.h = h;\n\n\t\tout_ratio.volume = cv::Mat(L, 3 * w*h, CV_8UC1);\n\n#pragma omp parallel for\n\t\tfor (int i = 0; i < h; ++i) {\n\t\t\tfor (int j = 0; j < w; ++j) {\n\n\t\t\t\tstd::vector<sibr::Vector3ub> values(L);\n\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\t\tvalues[t][c] = volume.at<uchar>(3 * (i*w + j) + c, t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tTimeHistogram histo = TimeHistogram(0, 255, numBins);\n\t\t\t\thisto.addValues(values);\n\t\t\t\t//histo.computeSortedBins();\n\n\t\t\t\tauto mode = histo.getHMode();\n\t\t\t\tauto mode_color = histo.getBinMiddle(histo.getHMode());\n\n\t\t\t\tfor (int t = 0; t < L; ++t) {\n\t\t\t\t\tconst auto & color = values[t];\n\t\t\t\t\tauto bin = histo.whatBin(color);\n\n\t\t\t\t\tsibr::Vector3f norm_temporal = (color.cast<int>() - mode_color.cast<int>()).cwiseAbs().cast<float>();\n\t\t\t\t\tsibr::Vector3f norm_spatial = spatial_ratio(j, i).cwiseAbs().cast<float>().array() + 10;\n\t\t\t\t\tsibr::Vector3f ratios = norm_temporal.cwiseQuotient(norm_spatial);\n\n\n\t\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\t\tout_ratio.volume.at<uchar>(t, 3 * (i*w + j) + c) = sibr::clamp<uchar>((uchar)(128 * ratios[c]), 0, 255);\n\t\t\t\t\t\t//if (ratios.maxCoeff() > 0.5) {\n\t\t\t\t\t\t//\tout_ratio.volume.at<uchar>(t, 3 * (i*w + j) + c) = 255;\n\t\t\t\t\t\t//} else {\n\t\t\t\t\t\t//\tout_ratio.volume.at<uchar>(t, 3 * (i*w + j) + c) = 0;\n\t\t\t\t\t\t//}\n\t\t\t\t\t\t//out_ratio.volume.at<uchar>(t, 3 * (i*w + j) + c) = sibr::clamp<uchar>((uchar)(64*ratios[c]),0,255);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\treturn volume.t();\n\t}\n\n\tcv::Mat VideoUtils::getLaplacian(cv::Mat mat, int size, bool smooth, bool absolute)\n\t{\n\t\tcv::Mat grey, laplacian, abs;\n\t\tif (smooth) {\n\t\t\tcv::GaussianBlur(mat, mat, cv::Size(size, size), 0, 0, cv::BORDER_DEFAULT);\n\t\t}\n\t\tgrey = getGrey(mat);\n\t\tcv::Laplacian(grey, laplacian, CV_16S, size);\n\t\tif (absolute) {\n\t\t\tcv::convertScaleAbs(laplacian, abs);\n\t\t\treturn abs;\n\t\t}\n\t\treturn laplacian;\n\t}\n\n\tcv::Mat VideoUtils::getCanny(cv::Mat mat)\n\t{\n\t\tcv::Mat grey, canny;\n\t\tgrey = getGrey(mat);\n\t\tcv::Canny(grey, canny, 50, 150);\n\t\treturn canny;\n\t}\n\n\tint VideoUtils::rotationAngleFromMetadata(const std::string & videoPath)\n\t{\n\t\tnamespace bfs = boost::filesystem;\n\n\t\tPath vidPath = bfs::canonical(videoPath);\n\t\tstd::string parentAbs = bfs::canonical(vidPath.parent_path()).string();\n\t\tstd::string tmpFilePath = parentAbs + \"/\" + vidPath.stem().string() + \"_tmp.txt\";\n\n\t\tstd::string cmd = \"ffprobe -i \\\"\" + vidPath.string() + \"\\\" > \\\"\" + tmpFilePath + \"\\\" 2>&1\";\n\t\t//std::cout << cmd << std::endl;\n\n\t\tint cmd_status = std::system(cmd.c_str());\n\t\tif (cmd_status != EXIT_SUCCESS) {\n\t\t\tSIBR_WRG << \"getMetaData failed to call : \" << cmd << std::endl;\n\t\t}\n\n\t\tstd::ifstream file(tmpFilePath);\n\t\tif (!file.is_open()) {\n\t\t\tSIBR_WRG << \"getMetaData failed to open \" << tmpFilePath << std::endl;\n\t\t}\n\t\tstd::string line, tmp;\n\t\tstd::stringstream linestream;\n\n\t\tint angle = 0;\n\t\twhile (safeGetline(file, line)) {\n\t\t\tif (line.find(\"rotate\") != std::string::npos) {\n\t\t\t\tlinestream << line;\n\t\t\t\tlinestream >> tmp >> tmp >> angle;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfile.close();\n\n\t\tif (!boost::filesystem::remove(tmpFilePath)) {\n\t\t\tSIBR_WRG << \"getMetaData failed to remove \" << tmpFilePath << std::endl;\n\t\t}\n\n\t\treturn angle;\n\t}\n\n\tvoid VideoUtils::ECCtransform(cv::Mat matA, cv::Mat matB, cv::Mat & correctedB, cv::Mat & diff, int cvMotion)\n\t{\n\t\tcv::Mat greyA, greyB, warpBA;\n\t\tcv::cvtColor(matA, greyA, cv::COLOR_BGR2GRAY);\n\t\tcv::cvtColor(matB, greyB, cv::COLOR_BGR2GRAY);\n\t\ttry {\n\t\t\tcv::findTransformECC(greyA, greyB, warpBA, cvMotion);\n\t\t}\n\t\tcatch (const std::exception & e) { std::cout << e.what();  return; }\n\n\t\tif (cvMotion == cv::MOTION_HOMOGRAPHY) {\n\t\t\tcv::warpPerspective(matB, correctedB, warpBA, matB.size());\n\t\t} else if (cvMotion == cv::MOTION_AFFINE) {\n\t\t\tcv::warpAffine(matB, correctedB, warpBA, matB.size());\n\t\t}\n\n\t\tcv::absdiff(matA, correctedB, diff);\n\t}\n\n\tvoid VideoUtils::smallAlignmentVideo(sibr::Video & vid, const std::string & outputVidPath, bool viz)\n\t{\n\t\tstruct Match {\n\t\t\tcv::Point2f in, out;\n\t\t\tfloat error;\n\t\t};\n\n\t\t//cv::VideoWriter out(outputVidPath, codec_OpenH264, vid.getFrameRate(), cv::Size(vid.getResolution()[0], vid.getResolution()[1]));\n\t\tsibr::FFVideoEncoder out(outputVidPath, vid.getFrameRate(), vid.getResolution());\n\n\t\tif (!out.isFine()) {\n\t\t\tSIBR_WRG << \" cant write video \" << outputVidPath << std::endl;\n\t\t}\n\t\tvid.setCurrentFrame(0);\n\t\tcv::Mat initFrame = vid.next();\n\t\tcv::Mat initGray = VideoUtils::getGrey(initFrame);\n\n\t\tstd::vector<cv::Point2f> features, nextFeatures;\n\t\tstd::vector<uchar> status;\n\t\tstd::vector<float> errors;\n\n\t\tcv::Mat totalHomography = cv::Mat::eye(3, 3, CV_32FC1);\n\n\t\tconst double magic_expon = 1.6;\n\t\tconst double ratio = 0.5;\n\t\tconst double ransac_repro_error = 3.0;\n\t\tconst double features_to_track_quality = 0.1;\n\t\tconst double features_min_dist = 0.1; //10\n\t\tint nPixels = vid.getResolution().prod();\n\n\t\tint numFeatures = (int)pow(nPixels, 1.0 / magic_expon);\n\t\tstd::cout << \" num features \" << numFeatures << std::endl;\n\n\t\tcv::goodFeaturesToTrack(initGray, features, numFeatures, features_to_track_quality, 10);\n\n\t\tcv::Mat nextFrame, gray;\n\n\t\tfor (;;) {\n\t\t\tnextFrame = vid.next();\n\t\t\tif (nextFrame.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgray = VideoUtils::getGrey(nextFrame);\n\n\t\t\tcv::calcOpticalFlowPyrLK(initGray, gray, features, nextFeatures, status, errors, cv::Size(15, 15), 0);\n\n\t\t\tstd::vector<Match> matchs;\n\t\t\tfor (int i = 0; i < (int)status.size(); ++i) {\n\t\t\t\tif (status[i] == 1) {\n\t\t\t\t\tmatchs.push_back({ features[i] ,nextFeatures[i] ,errors[i] });\n\t\t\t\t}\n\t\t\t}\n\t\t\t//std::cout << matchs.size() / (double)status.size() << std::endl;\n\n\t\t\tstd::sort(matchs.begin(), matchs.end(), [](const Match & a, const Match & b) { return a.error < b.error; });\n\n\t\t\tint numBestMatch = (int)(ratio*matchs.size());\n\t\t\tstd::vector<cv::Point2f> inputFeatures(numBestMatch), outputFeatures(numBestMatch);\n\n\t\t\tfor (int i = 0; i < numBestMatch; ++i) {\n\t\t\t\tinputFeatures[i] = matchs[i].in;\n\t\t\t\toutputFeatures[i] = matchs[i].out;\n\t\t\t}\n\n\t\t\tif (viz) {\n\t\t\t\tcv::Mat corresp_viz = nextFrame.clone();\n\t\t\t\tfor (int i = 0; i < numBestMatch; ++i) {\n\t\t\t\t\tcv::circle(corresp_viz, matchs[i].in, 5, cv::Scalar(0, 255, 0), 2);\n\t\t\t\t\tcv::circle(corresp_viz, matchs[i].out, 5, cv::Scalar(255, 0, 0), 2);\n\t\t\t\t}\n\t\t\t\tcv::imshow(\"viz\", corresp_viz);\n\t\t\t\tif (cv::waitKey() == 27) {\n\t\t\t\t\tviz = false;\n\t\t\t\t\tcv::destroyAllWindows();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcv::Mat homography = cv::findHomography(inputFeatures, outputFeatures, cv::RANSAC, ransac_repro_error);\n\n\t\t\tcv::Mat correctedFrame;\n\t\t\tcv::warpPerspective(nextFrame, correctedFrame, homography.inv(), nextFrame.size());\n\n\t\t\tout << correctedFrame;\n\t\t\tstd::cout << \".\" << std::flush;\n\t\t}\n\n\t\tif (viz) {\n\t\t\tcv::destroyAllWindows();\n\t\t}\n\n\t\tout.close();\n\t\tstd::cout << \" done \" << std::endl;\n\t}\n\n\tvoid VideoUtils::smallAlignmentVideo2(sibr::Video & vid, const std::string & outputVidPath, bool viz)\n\t{\n\t\tstruct Match {\n\t\t\tcv::Point2f in, out;\n\t\t\tfloat error;\n\t\t};\n\n\t\t//cv::VideoWriter out(outputVidPath, codec_OpenH264, vid.getFrameRate(), cv::Size(vid.getResolution()[0], vid.getResolution()[1]));\n\t\tsibr::FFVideoEncoder out(outputVidPath, vid.getFrameRate(), vid.getResolution());\n\n\t\tif (!out.isFine()) {\n\t\t\tSIBR_WRG << \" cant write video \" << outputVidPath << std::endl;\n\t\t}\n\t\tvid.setCurrentFrame(0);\n\t\tcv::Mat initFrame = vid.next();\n\t\tcv::Mat initGray = VideoUtils::getGrey(initFrame);\n\n\t\tstd::vector<cv::Point2f> features, nextFeatures;\n\t\tstd::vector<uchar> status;\n\t\tstd::vector<float> errors;\n\n\t\tcv::Mat completeHomography = cv::Mat::eye(3, 3, CV_64FC1);\n\n\t\tconst double magic_expon = 2.0;\n\t\tconst double ratio = 0.5;\n\t\tconst double ransac_repro_error = 0.5;\n\t\tconst double features_to_track_quality = 0.1;\n\t\tconst double features_min_dist = 10; //10\n\t\tconst double max_displacement = 2;\n\t\tint nPixels = vid.getResolution().prod();\n\n\t\tint numFeatures = (int)pow(nPixels, 1.0 / magic_expon);\n\t\tstd::cout << \" num features \" << numFeatures << std::endl;\n\n\t\tcv::Mat nextFrame, gray;\n\n\t\tfor (;;) {\n\t\t\tnextFrame = vid.next();\n\t\t\tif (nextFrame.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgray = VideoUtils::getGrey(nextFrame);\n\n\t\t\tcv::GaussianBlur(gray, gray, cv::Size(3, 3), 0);\n\n\t\t\tcv::goodFeaturesToTrack(initGray, features, numFeatures, features_to_track_quality, features_min_dist);\n\n\t\t\tcv::calcOpticalFlowPyrLK(initGray, gray, features, nextFeatures, status, errors, cv::Size(5, 5), 0);\n\n\t\t\tstd::vector<Match> matchs;\n\t\t\tfor (int i = 0; i < (int)status.size(); ++i) {\n\t\t\t\tauto v = features[i] - nextFeatures[i];\n\n\t\t\t\tif (status[i] == 1 && cv::norm(cv::Vec2f(v.x, v.y), cv::NORM_INF) < max_displacement) {\n\t\t\t\t\tmatchs.push_back({ features[i] ,nextFeatures[i] ,errors[i] });\n\t\t\t\t}\n\t\t\t}\n\t\t\t//std::cout << matchs.size() / (double)status.size() << std::endl;\n\n\t\t\tstd::sort(matchs.begin(), matchs.end(), [](const Match & a, const Match & b) { return a.error < b.error; });\n\n\t\t\tint numBestMatch = (int)(ratio*matchs.size());\n\t\t\tstd::vector<cv::Point2f> inputFeatures(numBestMatch), outputFeatures(numBestMatch);\n\n\t\t\tfor (int i = 0; i < numBestMatch; ++i) {\n\t\t\t\tinputFeatures[i] = matchs[i].in;\n\t\t\t\toutputFeatures[i] = matchs[i].out;\n\t\t\t}\n\n\t\t\tif (viz) {\n\t\t\t\tcv::Mat corresp_viz = nextFrame.clone();\n\t\t\t\tfor (int i = 0; i < numBestMatch; ++i) {\n\t\t\t\t\tcv::circle(corresp_viz, matchs[i].in, 5, cv::Scalar(0, 255, 0), 2);\n\t\t\t\t\tcv::circle(corresp_viz, matchs[i].out, 5, cv::Scalar(255, 0, 0), 2);\n\t\t\t\t}\n\t\t\t\tcv::imshow(\"viz\", corresp_viz);\n\t\t\t\tif (cv::waitKey() == 27) {\n\t\t\t\t\tviz = false;\n\t\t\t\t\tcv::destroyAllWindows();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcv::Mat homography = cv::findHomography(inputFeatures, outputFeatures, cv::RANSAC, ransac_repro_error);\n\n\t\t\tcompleteHomography *= homography;\n\n\t\t\tcv::Mat correctedFrame;\n\t\t\tcv::warpPerspective(nextFrame, correctedFrame, completeHomography.inv(), nextFrame.size());\n\n\t\t\tinitGray = gray;\n\n\t\t\tout << correctedFrame;\n\t\t\tstd::cout << \".\" << std::flush;\n\t\t}\n\n\t\tif (viz) {\n\t\t\tcv::destroyAllWindows();\n\t\t}\n\n\t\tout.close();\n\t\tstd::cout << \" done \" << std::endl;\n\t}\n\n\tcv::Mat VideoUtils::applyFlow(const cv::Mat & prev, const cv::Mat & flow) {\n\t\tcv::Mat out, realFlow = flow;\n\t\tfor (int i = 0; i < prev.rows; ++i) {\n\t\t\tfor (int j = 0; j < prev.cols; ++j) {\n\t\t\t\trealFlow.at<cv::Vec2f>(i, j) += cv::Vec2f(j + 0.5f, i + 0.5f);\n\t\t\t}\n\t\t}\n\t\tcv::remap(prev, out, realFlow, cv::Mat(), cv::INTER_LINEAR);\n\t\treturn out;\n\t}\n\n\tvoid VideoUtils::simpleFlow(cv::VideoCapture & cap, float ratio,\n\t\tstd::function<bool(cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id)> f,\n\t\tstd::function<void(void)> end_function\n\t) {\n\t\tcap.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, 0);\n\t\tcv::Mat  prev, flow;\n\t\tint flow_id = 0;\n\t\twhile (true) {\n\n\t\t\tcv::Mat next;\n\t\t\tcap >> next;\n\t\t\tif (next.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcv::resize(next, next, cv::Size((int)(ratio*next.size().width), (int)(ratio*next.size().height)));\n\n\t\t\tif (!prev.empty()) {\n\t\t\t\tcv::optflow::calcOpticalFlowSF(prev, next, flow, 3, 2, 4);\n\n\t\t\t\tif (!f(prev, next, flow, flow_id)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t++flow_id;\n\t\t\t}\n\n\t\t\tprev = next;\n\t\t}\n\n\t\tend_function();\n\t}\n\n\tvoid VideoUtils::simpleFlowViz(cv::VideoCapture & cap, float ratio)\n\t{\n\t\tsimpleFlow(cap, ratio, [](cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id) {\n\t\t\tcv::Mat viz = getFlowViz(flow);\n\t\t\tcv::resize(viz, viz, cv::Size(2000, 1500));\n\t\t\tcv::Mat diff = VideoUtils::applyFlow(prev, flow);\n\n\t\t\tcv::imshow(\"simpleflow\", viz);\n\t\t\tcv::imshow(\"frame\", next);\n\t\t\tcv::imshow(\"applyFlow\", diff);\n\t\t\tint key = cv::waitKey(1);\n\t\t\tif (key == 27) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}, []() {\n\t\t\tcv::destroyAllWindows();\n\t\t}\n\t\t);\n\t}\n\n\tvoid VideoUtils::simpleFlowSave(cv::VideoCapture & cap, float ratio, std::function<std::string(int flow_id)> naming_f)\n\t{\n\t\tstd::cout << \" saving flow \" << std::flush;\n\t\tsimpleFlow(cap, ratio, [&](cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id) {\n\t\t\tstd::cout << \".\" << std::flush;\n\t\t\tcv::Mat viz = getFlowViz(flow);\n\t\t\tviz.convertTo(viz, CV_8UC3, 255.0);\n\t\t\treturn cv::imwrite(naming_f(flow_id), viz);\n\t\t});\n\t\tstd::cout << \"done\" << std::endl;\n\t}\n\n\tvoid VideoUtils::deepFlow(cv::VideoCapture & cap, float ratio,\n\t\tstd::function<bool(cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id)> f,\n\t\tstd::function<void(void)> end_function)\n\t{\n\t\tcap.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, 0);\n\n\t\tauto deepFlow = cv::optflow::createOptFlow_DeepFlow();\n\t\tcv::Mat flow, next, nextGrey, prevGrey;\n\t\tint flow_id = 0;\n\t\twhile (true) {\n\t\t\tcap >> next;\n\t\t\tif (next.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tauto size = cv::Size((int)(ratio*next.size().width), (int)(ratio*next.size().height));\n\t\t\tcv::resize(next, next, size);\n\t\t\tnextGrey = getGrey(next);\n\n\t\t\tif (!prevGrey.empty()) {\n\t\t\t\tdeepFlow->calc(prevGrey, nextGrey, flow);\n\n\t\t\t\tif (!f(prevGrey, nextGrey, flow, flow_id)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t++flow_id;\n\t\t\t}\n\n\t\t\tprevGrey = nextGrey.clone();\n\t\t}\n\n\t\tend_function();\n\n\t}\n\n\tvoid VideoUtils::deepFlowViz(cv::VideoCapture & cap, float ratio)\n\t{\n\t\tdeepFlow(cap, ratio, [](cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id) {\n\t\t\tcv::Mat viz = getFlowViz(flow);\n\t\t\tcv::Mat diff = applyFlow(prev, flow);\n\t\t\tcv::imshow(\"simpleflow\", viz);\n\t\t\tcv::imshow(\"frame\", prev);\n\t\t\tcv::imshow(\"applyFlow\", diff);\n\t\t\tint key = cv::waitKey(1);\n\t\t\tif (key == 27) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}, []() {\n\t\t\tcv::destroyAllWindows();\n\t\t}\n\t\t);\n\t}\n\n\tcv::Mat VideoUtils::getGrey(const cv::Mat & mat)\n\t{\n\t\tcv::Mat out;\n\t\tcv::cvtColor(mat, out, cv::COLOR_BGR2GRAY);\n\t\treturn out;\n\t}\n\n\tvoid PyramidLayer::show(int s) const\n\t{\n\t\tint slice_y = 0;\n\n\t\tstruct Data {\n\t\t\tPyramidLayer A;\n\t\t};\n\t\tData data = { *this };\n\n\t\tauto cb = [](int pos, void* userdata) -> void {\n\t\t\tData & d = *(Data*)userdata;\n\t\t\tcv::Mat sliceA = sibr::slice(d.A, 0, pos);\n\t\t\tcv::resize(sliceA, sliceA, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::imshow(\"sliceA\", sliceA);\n\t\t};\n\n\n\t\tint t = 0;\n\t\twhile (true) {\n\n\t\t\tcv::Mat slice;\n\t\t\tcv::Mat shifted = (volume.row(t) + 0 * 128.0f);\n\t\t\tshifted.reshape(3, h).convertTo(slice, CV_8UC3);\n\t\t\tcv::imshow(\"shpw\", slice);\n\t\t\tcv::createTrackbar(\"sy\", \"shpw\", &slice_y, w - 1, cb, &data);\n\t\t\tif (cv::waitKey(s) == 27) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t++t;\n\t\t\tif (t == l) {\n\t\t\t\tstd::cout << \".\" << std::flush;\n\t\t\t\tt = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tcv::Mat PyramidLayer::getRGB(int frame, bool centered) {\n\t\tcv::Mat out;\n\t\tif (centered) {\n\t\t\tcv::Mat shifted = (volume.row(frame) + 128.0f);\n\t\t\tshifted.reshape(3, h).convertTo(out, CV_8UC3);\n\t\t} else {\n\t\t\tvolume.row(frame).reshape(3, h).convertTo(out, CV_8UC3);\n\t\t}\n\n\t\treturn out;\n\t}\n\n\tvoid PyramidLayer::saveToVideoFile(const std::string & filename, double framerate)\n\t{\n\t\tsibr::FFVideoEncoder output(filename, framerate, { w,h });\n\t\tfor (int f = 0; f < l; ++f) {\n\t\t\tcv::Mat frame;\n\t\t\tvolume.row(f).reshape(3, h).convertTo(frame, CV_8UC3);\n\t\t\toutput << frame;\n\t\t}\n\t\toutput.close();\n\n\t}\n\n\tvoid PyramidLayer::show(PyramidLayer A, PyramidLayer B, int s) {\n\t\tint t = 0;\n\t\twhile (true) {\n\t\t\tcv::Mat sliceA = A.getRGB(t);\n\t\t\tcv::Mat sliceB = B.getRGB(t);\n\t\t\tcv::Mat top;\n\t\t\tcv::hconcat(sliceA, sliceB, top);\n\t\t\tcv::imshow(\"show duo\", top);\n\t\t\tif (cv::waitKey(s) == 27) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t++t;\n\t\t\tif (t == A.l) {\n\t\t\t\tstd::cout << \".\" << std::flush;\n\t\t\t\tt = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid PyramidLayer::show(PyramidLayer A, PyramidLayer B, PyramidLayer C, int s) {\n\t\tint slice_x = 0;\n\t\tint slice_y = 0;\n\t\tint t = 0;\n\n\t\tstruct Data {\n\t\t\tPyramidLayer A, B, C;\n\t\t};\n\t\tData data = { A,B,C };\n\n\t\tauto cb = [](int pos, void* userdata) -> void {\n\t\t\tData & d = *(Data*)userdata;\n\t\t\tcv::Mat sliceA = sibr::slice(d.A, 0, pos);\n\t\t\tcv::Mat sliceB = sibr::slice(d.B, 0, pos);\n\t\t\tcv::Mat sliceC = sibr::slice(d.C, 0, pos);\n\t\t\tcv::resize(sliceA, sliceA, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::resize(sliceB, sliceB, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::resize(sliceC, sliceC, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\n\t\t\tcv::imshow(\"sliceA\", sliceA);\n\t\t\tcv::imshow(\"sliceB\", sliceB);\n\t\t\tcv::imshow(\"sliceC\", sliceC);\n\t\t};\n\n\t\twhile (true) {\n\t\t\tcv::imshow(\"show A\", A.getRGB(t));\n\t\t\tcv::imshow(\"show B\", B.getRGB(t));\n\t\t\tcv::imshow(\"show C\", C.getRGB(t));\n\n\t\t\tcv::createTrackbar(\"sy\", \"show C\", &slice_y, A.w - 1, cb, &data);\n\n\t\t\tint k = cv::waitKey(s);\n\t\t\tif (k == 27) {\n\t\t\t\tbreak;\n\t\t\t} else if (k == 'c') {\n\t\t\t\tt = t > 0 ? t - 1 : A.l - 1;\n\t\t\t} else {\n\t\t\t\t++t;\n\t\t\t\tif (t == A.l) {\n\t\t\t\t\tstd::cout << \".\" << std::flush;\n\t\t\t\t\tt = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid PyramidLayer::show(PyramidLayer A, PyramidLayer B, PyramidLayer C, PyramidLayer D, int s, bool centered) {\n\t\tint slice_y = 0;\n\t\tint t = 0;\n\t\tstruct Data {\n\t\t\tPyramidLayer A, B, C, D;\n\t\t\tbool center;\n\t\t};\n\t\tData data = { A,B,C,D, centered };\n\n\t\tauto cb = [](int pos, void* userdata) -> void {\n\t\t\tData & d = *(Data*)userdata;\n\t\t\tcv::Mat sliceA = sibr::slice(d.A, 0, pos, true, d.center);\n\t\t\tcv::Mat sliceB = sibr::slice(d.B, 0, pos, true, d.center);\n\t\t\tcv::Mat sliceC = sibr::slice(d.C, 0, pos, true, d.center);\n\t\t\tcv::Mat sliceD = sibr::slice(d.D, 0, pos, true, d.center);\n\t\t\tcv::resize(sliceA, sliceA, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::resize(sliceB, sliceB, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::resize(sliceC, sliceC, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::resize(sliceD, sliceD, cv::Size(800, 800), 0, 0, cv::INTER_NEAREST);\n\t\t\tcv::imshow(\"sliceA\", sliceA);\n\t\t\tcv::imshow(\"sliceB\", sliceB);\n\t\t\tcv::imshow(\"sliceC\", sliceC);\n\t\t\tcv::imshow(\"sliceD\", sliceD);\n\t\t};\n\n\t\twhile (true) {\n\t\t\tcv::imshow(\"show A\", A.getRGB(t, data.center));\n\t\t\tcv::imshow(\"show B\", B.getRGB(t, data.center));\n\t\t\tcv::imshow(\"show C\", C.getRGB(t, data.center));\n\t\t\tcv::imshow(\"show D\", D.getRGB(t, data.center));\n\t\t\tcv::createTrackbar(\"sy\", \"show C\", &slice_y, A.w - 1, cb, &data);\n\n\t\t\tint k = cv::waitKey(s);\n\t\t\tif (k == 27) {\n\t\t\t\tstd::cout << \"clear\" << std::endl;\n\t\t\t\tcv::destroyAllWindows();\n\t\t\t\tbreak;\n\t\t\t} else if (k == 'c') {\n\t\t\t\tt = t > 0 ? t - 1 : A.l - 1;\n\t\t\t} else if (k == 'm') {\n\t\t\t\tdata.center = !data.center;\n\t\t\t} else {\n\t\t\t\t++t;\n\t\t\t\tif (t == A.l) {\n\t\t\t\t\tstd::cout << \".\" << std::flush;\n\t\t\t\t\tt = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid PyramidLayer::showDiff(PyramidLayer A, PyramidLayer B, int s)\n\t{\n\t\tint t = 0;\n\t\twhile (true) {\n\t\t\tcv::Mat sliceA = A.getRGB(t);\n\t\t\tcv::Mat sliceB = B.getRGB(t);\n\t\t\tcv::Mat diff;\n\t\t\tcv::absdiff(sliceA, sliceB, diff);\n\t\t\tcv::Mat top;\n\t\t\t//cv::hconcat(sliceB, 5 * diff, top);\n\t\t\t//cv::hconcat(sliceA, top, top);\n\t\t\tcv::imshow(\"show a\", sliceA);\n\t\t\tcv::imshow(\"show B\", sliceB);\n\n\t\t\tcv::imshow(\"show diff\", diff);\n\t\t\tint k = cv::waitKey(s);\n\t\t\tif (k == 27) {\n\t\t\t\tbreak;\n\t\t\t} else if (k == 'c') {\n\t\t\t\tt = t > 0 ? t - 1 : A.l - 1;\n\t\t\t} else {\n\t\t\t\t++t;\n\t\t\t\tif (t == A.l) {\n\t\t\t\t\tstd::cout << \".\" << std::flush;\n\t\t\t\t\tt = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tPyramidLayer PyramidLayer::operator+(const PyramidLayer & other)\n\t{\n\t\t//assert(w == other.w && h == other.w && l = other.l);\n\n\t\tPyramidLayer out;\n\t\tout.w = w;\n\t\tout.h = h;\n\t\tout.l = l;\n\t\tout.volume = volume + other.volume;\n\t\treturn out;\n\t}\n\n\tPyramidLayer PyramidLayer::operator-(const PyramidLayer & other)\n\t{\n\t\t//assert(w == other.w && h == other.w && l = other.l);\n\n\t\tPyramidLayer out;\n\t\tout.w = w;\n\t\tout.h = h;\n\t\tout.l = l;\n\t\tout.volume = volume - other.volume;\n\t\treturn out;\n\t}\n\n\tPyramidLayer blur(const PyramidLayer & layer, const PyramidParameters &  params)\n\t{\n\t\tPyramidLayer out(layer.w, layer.h, layer.l);\n\n#pragma omp parallel for\n\t\tfor (int t = 0; t < layer.l; ++t) {\n\t\t\tcv::Mat sliceIn = layer.volume.row(t).reshape(3, layer.h);\n\t\t\tcv::Mat sliceOut = out.volume.row(t).reshape(3, out.h);\n\t\t\tif (params.splacialDS) {\n\t\t\t\tcv::GaussianBlur(sliceIn, sliceOut, cv::Size(2 * params.spatial_radius + 1, 2 * params.spatial_radius + 1), 0);\n\t\t\t} else {\n\t\t\t\tsliceIn.copyTo(sliceOut);\n\t\t\t}\n\t\t}\n\n\t\ttemporalBlur(out, params);\n\n\t\treturn out;\n\t}\n\n\t//\tPyramidLayer temporalBlur(const PyramidLayer & layer, const PyramidParameters &  params, float scaling)\n\t//\t{\n\t//\t\t//cv::Mat kernel = cv::Mat::ones(cv::Size(2 * params.temporal_radius + 1, 1), CV_32FC1);\n\t//\t\t//kernel = kernel / cv::norm(kernel, cv::NORM_L1);\n\t//\n\t//\t\tconst cv::Mat kernel = (scaling / 16.0f)*(cv::Mat_<float>(1, 5) << 1, 4, 6, 4, 1);\n\t//\n\t//\t\t//layer.show(0);\n\t//\n\t//\t\tcv::Mat vol = layer.volume.t();\n\t//\n\t//\t\t//std::cout << vol.row(3 * (0 * layer.w + 0) + 0) << std::endl;\n\t//\t\t//std::cout << vol.row(3 * (0 * layer.w + 0) + 1) << std::endl;\n\t//\t\t//std::cout << vol.row(3 * (0 * layer.w + 0) + 2) << std::endl;\n\t//\n\t//\t\tcv::filter2D(vol, vol, -1, kernel, { 2,0 }, 0, cv::BORDER_DEFAULT);\n\t//\n\t////#pragma omp parallel for\n\t////\t\tfor (int i = 0; i < layer.h; ++i) {\n\t////\t\t\tfor (int j = 0; j < layer.w; ++j) {\n\t////\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t////\t\t\t\t\tcv::Mat slice = vol.row(3 * (i*layer.w + j) + c);\n\t////\t\t\t\t\tcv::filter2D(slice, slice, -1, kernel, { 2,0 }, 0, cv::BORDER_DEFAULT); // cv::Point(-1,-1) BORDER_ISOLATED  cv::BORDER_REPLICATE\n\t////\t\t\t\t}\n\t////\t\t\t}\n\t////\t\t}\n\t//\n\t//\t\t//std::cout << vol.row(3 * (0 * layer.w + 0) + 0) << std::endl;\n\t//\t\t//std::cout << vol.row(3 * (0 * layer.w + 0) + 1) << std::endl;\n\t//\t\t//std::cout << vol.row(3 * (0 * layer.w + 0) + 2) << std::endl;\n\t//\n\t//\t\tPyramidLayer out(vol.t(), layer.w, layer.h);\n\t//\t\t//out.cout();\n\t//\t\t//out.show(0);\n\t//\n\t//\t\treturn out;\n\t//\t}\n\n\tPyramidLayer temporalBlur(const PyramidLayer & layer, const PyramidParameters &  params, float scaling)\n\t{\n\t\tconst cv::Mat kernel = (scaling / 16.0f)*(cv::Mat_<float>(5, 1) << 1, 4, 6, 4, 1);\n\t\tcv::Mat vol = layer.volume.clone();\n\t\tcv::filter2D(vol, vol, -1, kernel, { -1,-1 }, 0.0, cv::BORDER_DEFAULT);\n\t\treturn PyramidLayer(vol, layer.w, layer.h);\n\t}\n\n\tvoid temporalBlurInPlace(PyramidLayer & layer, const PyramidParameters & params, float scaling)\n\t{\n\t\tconst cv::Mat kernel = (scaling / 16.0f)*(cv::Mat_<float>(5, 1) << 1, 4, 6, 4, 1);\n\t\tcv::filter2D(layer.volume, layer.volume, -1, kernel, { -1,-1 }, 0.0, cv::BORDER_DEFAULT);\n\t}\n\n\tPyramidLayer decimate(const PyramidLayer & layer, const PyramidParameters &  params)\n\t{\n\n\t\tPyramidLayer out((layer.w + 1) / 2, (layer.h + 1) / 2, (layer.l + 1) / 2);\n\n#pragma omp parallel for\n\t\tfor (int t = 0; t < out.l; ++t) {\n\t\t\tcv::Mat sliceCurrent = layer.volume.row(2 * t).reshape(3, layer.h);\n\t\t\tcv::Mat sliceDecimated = out.volume.row(t).reshape(3, out.h);\n\n\t\t\tcv::pyrDown(sliceCurrent, sliceDecimated);\n\t\t\t//cv::resize(sliceCurrent, sliceDecimated, sliceDecimated.size(), 0, 0, CV_INTER_NN);\n\n\t\t}\n\n\t\treturn out;\n\n\t}\n\n\tPyramidLayer upscale(const PyramidLayer & layerUp, const PyramidLayer & layerDown, const PyramidParameters &  params)\n\t{\n\t\t//std::cout << layerUp.w << \" \" << layerUp.h << \" \" << layerUp.l << std::endl;\n\t\t//std::cout << layerDown.w << \" \" << layerDown.h << \" \" << layerDown.l << std::endl;\n\n\t\tPyramidLayer out(layerUp.w, layerUp.h, layerUp.l);\n\t\t//#pragma omp parallel for\n\t\tfor (int t = 0; t < layerDown.l; ++t) {\n\t\t\t//if (2 * t + 1 >= layerUp.l) {\n\t\t\t//\tcontinue;\n\t\t\t//}\n\t\t\t//cv::Mat sliceUp = out.volume.row(2 * t + 1).reshape(3, layerUp.h);\n\t\t\tcv::Mat sliceUp = out.volume.row(2 * t).reshape(3, layerUp.h);\n\t\t\tcv::Mat sliceDown = layerDown.volume.row(t).reshape(3, layerDown.h);\n\n\t\t\tif (params.splacialDS) {\n\t\t\t\tcv::pyrUp(sliceDown, sliceUp, sliceUp.size());\n\t\t\t} else {\n\t\t\t\tsliceDown.copyTo(sliceUp);\n\t\t\t}\n\n\n\t\t\t//\tif (2 * t + 1 < layerUp.l) {\n\t\t\t//\t\tsliceUp.copyTo(out.volume.row(2 * t + 1).reshape(3, layerUp.h));\n\t\t\t//\t}\n\t\t}\n\t\ttemporalBlurInPlace(out, params, 2.0f);\n\t\treturn out;\n\t}\n\n\tPyramidLayer downscale(const PyramidLayer & layer, const PyramidParameters &  params)\n\t{\n\n\t\tPyramidLayer blured = temporalBlur(layer, params);\n\t\t//std::cout << \" temporal blur \" << std::endl;\n\t\t//blured.show();\n\t\t//std::cout << \" temporal blur end\" << std::endl;\n\n\t\tPyramidLayer out;\n\n\t\tif (params.splacialDS) {\n\t\t\tout = PyramidLayer((layer.w + 1) / 2, (layer.h + 1) / 2, (layer.l + 1) / 2);\n\t\t} else {\n\t\t\tout = PyramidLayer(layer.w, layer.h, (layer.l + 1) / 2);\n\t\t}\n\n\t\t//#pragma omp parallel for\n\t\tfor (int t = 0; t < out.l; ++t) {\n\t\t\t//if (2 * t + 1 >= layer.l) {\n\t\t\t//\tcontinue;\n\t\t\t//}\n\t\t\t//cv::Mat sliceCurrent = blured.volume.row(2 * t + 1).reshape(3, layer.h);\n\t\t\tcv::Mat sliceCurrent = blured.volume.row(2 * t).reshape(3, layer.h);\n\t\t\tcv::Mat sliceDecimated = out.volume.row(t).reshape(3, out.h);\n\n\t\t\tif (params.splacialDS) {\n\t\t\t\tcv::pyrDown(sliceCurrent, sliceDecimated);\n\t\t\t} else {\n\t\t\t\tsliceCurrent.copyTo(sliceDecimated);\n\t\t\t}\n\n\t\t}\n\n\t\t//PyramidLayer blur (layer.w, layer.h, (layer.l + 1) / 2);\n\n\t\t//for (int i = 0; i < blur.volume.rows; ++i) {\n\t\t//\tcv::pyrDown(layer.volume.row(i), blur.volume.row(i), blur.volume.row(i).size());\n\t\t//}\n\n\t\t//PyramidLayer out;\n\t\t//if (params.splacialDS) {\n\t\t//\tout = PyramidLayer((layer.w + 1) / 2, (layer.h + 1) / 2, blur.l);\n\t\t//} else {\n\t\t//\tout = PyramidLayer(layer.w, layer.h, blur.l);\n\t\t//}\n\n\t\t//for (int t = 0; t < out.l; ++t) {\n\t\t//\tif (2 * t + 1 >= layer.l) {\n\t\t//\t\tcontinue;\n\t\t//\t}\n\t\t//\tcv::Mat sliceCurrent = blur.volume.row(2 * t + 1).reshape(3, layer.h);\n\t\t//\tcv::Mat sliceDecimated = out.volume.row(t).reshape(3, out.h);\n\n\t\t//\tif (params.splacialDS) {\n\t\t//\t\tcv::pyrDown(sliceCurrent, sliceDecimated);\n\t\t//\t} else {\n\t\t//\t\tsliceCurrent.copyTo(sliceDecimated);\n\t\t//\t}\n\t\t//}\n\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT cv::Mat slice(const PyramidLayer & layer, int i, int j, bool vertical, bool center)\n\t{\n\t\tcv::Mat out;\n\t\tif (vertical) {\n\t\t\tout = cv::Mat(layer.l, layer.h, CV_8UC3);\n\n\t\t\tfor (int t = 0; t < layer.l; ++t) {\n\t\t\t\tfor (int i = 0; i < layer.h; ++i) {\n\t\t\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\t\t\tout.at<cv::Vec3b>(t, i)[c] = (uchar)sibr::clamp((int)layer.volume.at<float>(t, 3 * (i*layer.w + j) + c) + (center ? 128 : 0), 0, 255);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//cv::vconcat(out, out, out);\n\t\tout = out.t();\n\t\treturn out;\n\t}\n\n\tPyramidLayer VideoLaplacianPyramid::collapse() const\n\t{\n\t\tPyramidLayer out = layers.back();\n\t\tfor (int i = (int)layers.size() - 2; i >= 0; --i) {\n\t\t\tPyramidLayer up = upscale(layers[i], out, params);\n\t\t\tout = up + layers[i];\n\t\t}\n\t\treturn out;\n\t}\n\n\tVideoGaussianPyramid buildVideoGaussianPyramid(const cv::Mat & volume, int w, int h, int nLevels, const PyramidParameters & params, bool show)\n\t{\n\t\tVideoGaussianPyramid out;\n\t\tout.params = params;\n\t\tPyramidLayer currentLayer(volume, w, h);\n\t\tout.layers.push_back(currentLayer);\n\n\t\tfor (int i = 1; i < nLevels; ++i) {\n\t\t\tPyramidLayer down = downscale(currentLayer, params);\n\t\t\tout.layers.push_back(down);\n\t\t\tcurrentLayer = down;\n\t\t\tif (show) {\n\t\t\t\tcurrentLayer.show();\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\tVideoGaussianPyramid buildVideoGaussianPyramid(sibr::Video & vid, int nLevels, const PyramidParameters & params, bool show)\n\t{\n\t\treturn buildVideoGaussianPyramid(vid.getVolume(), vid.getResolution()[0], vid.getResolution()[1], nLevels, params, show);\n\t}\n\n\tSIBR_VIDEO_EXPORT VideoLaplacianPyramid buildVideoLaplacianPyramid(PyramidLayer vid, int nLevels, const PyramidParameters & params, bool show)\n\t{\n\t\tVideoLaplacianPyramid out;\n\t\tout.params = params;\n\n\t\tPyramidLayer currentLayer = vid;\n\t\tcurrentLayer.volume.convertTo(currentLayer.volume, CV_32FC1);\n\n\t\tfor (int i = 0; i < nLevels - 1; ++i) {\n\t\t\tPyramidLayer down = downscale(currentLayer, params);\n\t\t\tPyramidLayer up = upscale(currentLayer, down, params);\n\t\t\tif (show) {\n\t\t\t\tup.show();\n\t\t\t}\n\t\t\tout.layers.push_back(currentLayer - up);\n\t\t\tcurrentLayer = down;\n\t\t}\n\n\t\tout.layers.push_back(currentLayer);\n\n\t\treturn out;\n\t}\n\n\tVideoLaplacianPyramid buildVideoLaplacianPyramid(sibr::Video & vid, int nLevels, const PyramidParameters & params, bool show) {\n\t\tPyramidLayer layer(vid.getVolume(), vid.getResolution()[0], vid.getResolution()[1]);\n\t\treturn buildVideoLaplacianPyramid(layer, nLevels, params, show);\n\t}\n\n\tVideoLaplacianPyramid buildVideoLaplacianPyramidFullyReduced(PyramidLayer vid, int nLevels, const PyramidParameters & params, bool show)\n\t{\n\t\tVideoLaplacianPyramid standardPyramid = buildVideoLaplacianPyramid(vid, nLevels, params, show);\n\n\t\tVideoLaplacianPyramid out;\n\t\tout.params = params;\n\n\t\tout.layers.push_back(standardPyramid.layers[0]);\n\n\t\tfor (int i = 1; i < nLevels; ++i) {\n\n\t\t\tPyramidLayer diff = standardPyramid.layers[i].clone();\n\t\t\tfor (int k = i - 1; k >= 0; --k) {\n\t\t\t\tdiff = upscale(standardPyramid.layers[k], diff, params);\n\t\t\t}\n\t\t\tstd::cout << \" layer \" << i << \" : \";\n\t\t\tdiff.cout();\n\t\t\tout.layers.push_back(diff);\n\t\t}\n\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT void convertReducedVideoPyramidTo128(VideoLaplacianPyramid & vid)\n\t{\n\t\tint nLayers = (int)vid.layers.size();\n\t\tfor (int l = 0; l < nLayers - 1; ++l) {\n\t\t\tvid.layers[l].volume += 128.0;\n\t\t}\n\t}\n\n\tPyramidLayer videoLaplacianBlending(sibr::Video & vidA, sibr::Video & vidB, PyramidLayer mask_volume)\n\t{\n\t\tint num_lvls = 6;\n\t\tauto pyrA = sibr::buildVideoLaplacianPyramid(vidA, num_lvls);\n\t\tauto pyrB = sibr::buildVideoLaplacianPyramid(vidB, num_lvls);\n\t\tauto pyrM = sibr::buildVideoGaussianPyramid(mask_volume.volume, mask_volume.w, mask_volume.h, num_lvls);\n\n\t\tVideoLaplacianPyramid out;\n\t\tfor (int l = 0; l < num_lvls; ++l) {\n\t\t\tsibr::PyramidLayer layer;\n\t\t\tlayer.w = pyrA.layers[l].w;\n\t\t\tlayer.h = pyrA.layers[l].h;\n\t\t\tlayer.l = pyrA.layers[l].l;\n\n\t\t\tcv::Mat A_lvl = pyrA.layers[l].volume;\n\t\t\tcv::Mat B_lvl = pyrB.layers[l].volume;\n\t\t\tcv::Mat M_lvl = pyrM.layers[l].volume;\n\n\t\t\tlayer.volume = A_lvl.mul(M_lvl) + B_lvl.mul(1.0f - M_lvl);\n\t\t\tout.layers.push_back(layer);\n\t\t}\n\n\t\treturn out.collapse();\n\t}\n\n\tPyramidLayer videoLaplacianBlending(PyramidLayer vidA, PyramidLayer vidB, PyramidLayer mask_volume, PyramidParameters params, bool show)\n\t{\n\t\tint num_lvls = params.num_levels;\n\n\t\tauto pyrA = sibr::buildVideoLaplacianPyramid(vidA, num_lvls, params, show);\n\t\tauto pyrB = sibr::buildVideoLaplacianPyramid(vidB, num_lvls, params, show);\n\t\tauto pyrM = sibr::buildVideoGaussianPyramid(mask_volume.volume, mask_volume.w, mask_volume.h, num_lvls, params, show);\n\n\t\tVideoLaplacianPyramid out;\n\t\tout.params = params;\n\t\tfor (int l = 0; l < num_lvls; ++l) {\n\n\t\t\tstd::cout << l << std::endl;\n\n\t\t\tsibr::PyramidLayer layer;\n\t\t\tlayer.w = pyrA.layers[l].w;\n\t\t\tlayer.h = pyrA.layers[l].h;\n\t\t\tlayer.l = pyrA.layers[l].l;\n\n\t\t\tcv::Mat A_lvl = pyrA.layers[l].volume;\n\t\t\tcv::Mat B_lvl = pyrB.layers[l].volume;\n\t\t\tcv::Mat M_lvl = pyrM.layers[l].volume;\n\n\n\t\t\tcv::Mat rev_Mask = 255 - M_lvl;\n\t\t\tsibr::PyramidLayer test(rev_Mask, pyrM.layers[l].w, pyrM.layers[l].h);\n\n\t\t\t//pyrA.layers[l].show();\n\t\t\t//pyrB.layers[l].show();\n\t\t\t//pyrM.layers[l].show();\n\n\t\t\t//test.show();\n\n\t\t\tcv::Mat normalized_mask = (1 / 255.0)*M_lvl;\n\t\t\tcv::Mat normalized_mask_r = (1 / 255.0)*test.volume;\n\n\t\t\tlayer.volume = A_lvl.mul(normalized_mask) + B_lvl.mul(normalized_mask_r);\n\t\t\t//layer.show();\n\n\t\t\tout.layers.push_back(layer);\n\t\t}\n\n\t\treturn out.collapse();\n\t}\n\n\tstd::vector<FullContribData> videoLaplacianBlendingContrib(PyramidLayer vidA, PyramidLayer vidB, PyramidLayer mask_volume, PyramidParameters params)\n\t{\n\t\tint num_lvls = params.num_levels;\n\n\t\tauto pyrA = sibr::buildVideoLaplacianPyramid(vidA, num_lvls, params);\n\t\tauto pyrB = sibr::buildVideoLaplacianPyramid(vidB, num_lvls, params);\n\t\tauto pyrM = sibr::buildVideoGaussianPyramid(mask_volume.volume, mask_volume.w, mask_volume.h, num_lvls, params);\n\n\t\tstd::vector<FullContribData> out(num_lvls);\n\n\t\tVideoLaplacianPyramid l_out;\n\t\tl_out.params = params;\n\t\tl_out.layers.resize(num_lvls);\n\n#pragma omp parallel for\n\t\tfor (int l = 0; l < num_lvls; ++l) {\n\n\t\t\tFullContribData data;\n\t\t\tContribData & data_s = data.scaled;\n\t\t\tContribData & data_ns = data.notScaled;\n\n\t\t\tsibr::PyramidLayer layer;\n\n\t\t\tlayer.w = pyrA.layers[l].w;\n\t\t\tlayer.h = pyrA.layers[l].h;\n\t\t\tlayer.l = pyrA.layers[l].l;\n\n\t\t\tcv::Mat A_lvl = pyrA.layers[l].volume;\n\t\t\tcv::Mat B_lvl = pyrB.layers[l].volume;\n\t\t\tcv::Mat M_lvl = pyrM.layers[l].volume;\n\n\t\t\tcv::Mat rev_Mask = 255 - M_lvl;\n\t\t\tsibr::PyramidLayer test(rev_Mask, pyrM.layers[l].w, pyrM.layers[l].h);\n\n\t\t\tcv::Mat normalized_mask = (1 / 255.0)*M_lvl;\n\t\t\tcv::Mat normalized_mask_r = (1 / 255.0)*test.volume;\n\n\t\t\tlayer.volume = A_lvl.mul(normalized_mask) + B_lvl.mul(normalized_mask_r);\n\t\t\tl_out.layers[l] = layer;\n\n\t\t\tPyramidLayer mask = pyrM.layers[l];\n\t\t\tPyramidLayer partA = pyrA.layers[l];\n\t\t\tPyramidLayer partB = pyrB.layers[l];\n\n\t\t\tdata_ns.contrib = layer;\n\t\t\tdata_ns.mask = mask;\n\t\t\tdata_ns.partA = partA;\n\t\t\tdata_ns.partB = partB;\n\n\t\t\tfor (int j = l - 1; j >= 0; --j) {\n\t\t\t\tlayer = upscale(pyrA.layers[j], layer, params);\n\t\t\t\tmask = upscale(pyrA.layers[j], mask, params);\n\t\t\t\tpartA = upscale(pyrA.layers[j], partA, params);\n\t\t\t\tpartB = upscale(pyrA.layers[j], partB, params);\n\t\t\t}\n\n\t\t\tdata_s.contrib = layer;\n\t\t\tdata_s.mask = mask;\n\t\t\tdata_s.partA = partA;\n\t\t\tdata_s.partB = partB;\n\n\t\t\tout[l] = data;\n\t\t}\n\n\t\tout[0].result = l_out.collapse();\n\n\t\treturn out;\n\t}\n\n\tvoid videoLaplacianBlendingDebug(PyramidLayer vidA, PyramidLayer vidB, PyramidLayer mask_volume, PyramidParameters params)\n\t{\n\t\tint num_lvls = params.num_levels;\n\n\t\tauto pyrA = sibr::buildVideoLaplacianPyramid(vidA, num_lvls, params);\n\t\tauto pyrB = sibr::buildVideoLaplacianPyramid(vidB, num_lvls, params);\n\t\tauto pyrM = sibr::buildVideoGaussianPyramid(mask_volume.volume, mask_volume.w, mask_volume.h, num_lvls, params);\n\n\t\tVideoLaplacianPyramid out;\n\n\t\t//struct LayerData {\n\t\t//\tint i;\n\t\t//} data;\n\n\n\t\tfor (int l = 0; l < num_lvls; ++l) {\n\t\t\tsibr::PyramidLayer layer;\n\t\t\tlayer.w = pyrA.layers[l].w;\n\t\t\tlayer.h = pyrA.layers[l].h;\n\t\t\tlayer.l = pyrA.layers[l].l;\n\n\t\t\tcv::Mat A_lvl = pyrA.layers[l].volume;\n\t\t\tcv::Mat B_lvl = pyrB.layers[l].volume;\n\t\t\tcv::Mat M_lvl = pyrM.layers[l].volume;\n\n\t\t\tlayer.volume = A_lvl.mul(M_lvl) + B_lvl.mul(1.0f - M_lvl);\n\t\t\tout.layers.push_back(layer);\n\t\t}\n\n\t\tauto final_res = out.collapse();\n\n\t}\n\n\t//int TimeHistogram::getModeId() const {\n\t//\treturn  std::distance(bins.begin(), std::max_element(bins.begin(), bins.end()));\n\t//}\n\t\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/VideoUtils.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"Config.hpp\"\n#include <opencv2/opencv.hpp>\n#include <functional>\n#include \"FFmpegVideoEncoder.hpp\"\n\nnamespace sibr {\n\t\n\tSIBR_VIDEO_EXPORT std::vector<cv::Mat> cvSplitChannels(cv::Mat mat);\n\n\ttemplate<typename T, uint N>\n\tstruct CV_Assign {\n\t\tstatic void assignValue(uint c, const T & val, cv::Vec<T, N> & vec) {\n\t\t\tvec[c] = val;\n\t\t}\n\t};\n\ttemplate<typename T>\n\tstruct CV_Assign<T,1> {\n\t\tstatic void assignValue(uint c, const T & val, T & vec) {\n\t\t\tvec = val;\n\t\t}\n\t};\n\n\ttemplate<typename T, uint N>\n\tcv::Mat cvConvertMatTo(cv::Mat mat, double scale = 1.0) {\n\t\tif (mat.type() == getOpenCVtype<T, N>) {\n\t\t\treturn mat;\n\t\t}\n\n\t\tcv::Mat m, out;\n\t\tconst int nc = mat.channels();\n\t\tif (nc == N) {\n\t\t\tm = mat;\n\t\t} else  {\n\t\t\tstd::vector<cv::Mat> in_channels = cvSplitChannels(mat), out_channels(N);\n\t\t\tfor (int i = 0; i < N; ++i) {\n\t\t\t\t in_channels[i < nc ? i : (nc - 1)].copyTo(out_channels[i]);\n\t\t\t}\n\t\t\tcv::merge(out_channels, m);\n\t\t}\n\n\t\tm.convertTo(out, getOpenCVtype<T, N>, scale);\n\t\treturn out;\n\t}\n\n\ttemplate<typename T, uint N = 3>\n\tclass VideoVolume;\n\n\tusing Volume3f = VideoVolume<float, 3>;\n\tusing Volume1f = VideoVolume<float, 1>;\n\tusing Volume4u = VideoVolume<uchar, 4>;\n\tusing Volume3u = VideoVolume<uchar, 3>;\n\tusing Volume1u = VideoVolume<uchar, 1>;\n\n\t/**\n\t* \\addtogroup sibr_video\n\t* @{\n\t*/\n\ttemplate<typename T, uint N>\n\tclass VideoVolume {\n\tpublic:\n\t\tusing CVpixel = std::conditional_t<N == 1, T, cv::Vec<T, N>>;\n\t\tstatic const uint cv_type = getOpenCVtype<T, N>;\n\n\t\t// data\n\t\tint w = 0, h = 0, l = 0;\n\t\tcv::Mat_<T> mat;\n\n\t\t// medthods\n\t\tVideoVolume() {}\n\n\t\tVideoVolume(int _l, int _w, int _h) : l(_l), w(_w), h(_h) {\n\t\t\tmat = cv::Mat_<T>(l, w*h*N);\n\t\t}\n\n\t\tVideoVolume(int _l, int _w, int _h, double value) : l(_l), w(_w), h(_h) {\n\t\t\tmat = cv::Mat_<T>(l, w*h*N, static_cast<T>(value));\n\t\t}\n\n\t\tVideoVolume(cv::Mat other_volume, int _w, int _h) : w(_w), h(_h), l(other_volume.rows) {\n\t\t\tif (other_volume.channels() * other_volume.cols * other_volume.rows != l*w*h*N) {\n\t\t\t\tSIBR_ERR << l << \" \" << w << \" \" << h << \" \" << N << \" : \" << \n\t\t\t\t\tother_volume.channels()  << \" \" << other_volume.rows << \" \" << other_volume.cols << std::endl;\n\t\t\t}\n\t\t\tmat = other_volume;\n\t\t}\n\n\t\tVideoVolume(const VideoVolume & other) : l(other.l) , w(other.w), h(other.h) {\n\t\t\tmat = other.mat;\n\t\t}\n\n\t\ttemplate<typename U, uint M>\n\t\tvoid setupFrom(const VideoVolume<U, M> & other) {\n\t\t\t*this = VideoVolume(other.l, other.w, other.h);\n\t\t}\n\n\t\tVideoVolume clone() const {\n\t\t\treturn VideoVolume(mat.clone(), w, h);\n\t\t}\n\n\t\ttemplate<typename U, uint M = N>\n\t\tVideoVolume<U, M> convertTo() const {\n\t\t\tVideoVolume<U, M> out(l, w, h);\n\t\t\tfor (int f = 0; f < l; ++f) {\n\t\t\t\tcvConvertMatTo<U, M>(frame(f)).copyTo(out.frame(f));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tvoid toggle(double d = 255) {\n\t\t\tmat = d - mat;\n\t\t}\n\n\t\tvoid shift(double d) {\n\t\t\tmat += d;\n\t\t}\n\t\tvoid scale(double d) {\n\t\t\tmat *= d;\n\t\t}\n\t\ttemplate<typename U>\n\t\tvoid add(const VideoVolume<U,N> & other) {\n\t\t\tcv::add(mat, other.mat, mat, cv::noArray(), cv_type);\n\t\t}\n\n\t\ttemplate<typename U>\n\t\tvoid substract(const VideoVolume<U, N> & other) {\n\t\t\tcv::subtract(mat, other.mat, mat, cv::noArray(), cv_type);\n\t\t}\n\n\t\ttemplate<typename U, uint M>\n\t\tvoid multiply(const VideoVolume<U,M> & other) {\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tcv::multiply(cvConvertMatTo<float, N>(frame(t)), cvConvertMatTo<float, N>(other.frame(t)), frame(t));\n\t\t\t}\n\t\t}\n\n\t\tVideoVolume concat(const VideoVolume & other) const {\n\t\t\tVideoVolume out(l + other.l, w, h);\n\t\t\tmat.copyTo(out.mat.rowRange(0, l));\n\t\t\tother.mat.copyTo(out.mat.rowRange(l, l + other.l));\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume concatH(const VideoVolume & other) const {\n\t\t\tVideoVolume out(l, w + other.w, h);\n#pragma omp parallel for\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tcv::hconcat(std::vector<cv::Mat_<CVpixel>>{frame(t), other.frame(t)}, out.frame(t));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume applyMaskBinary(const Volume1u & mask) {\n\t\t\tVideoVolume out = VideoVolume(l, w, h, 0);\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tframe(t).copyTo(out.frame(t), mask.frame(t));\n\t\t\t}\n\t\t\treturn  out;\n\t\t}\t\n\n\t\tVideoVolume swapRBchannels() const {\n\t\t\tstatic_assert(N >= 3, \"need 3 channels\");\n\t\t\tVideoVolume out = VideoVolume(l, w, h, 0);\n\n#pragma omp parallel for\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tcv::cvtColor(frame(t), out.frame(t), cv::COLOR_BGR2RGB);\n\t\t\t}\n\t\t\treturn  out;\n\t\t}\n\n\t\ttemplate<uint M>\n\t\tVideoVolume applyMask(const VideoVolume<uchar,M> & mask) {\n\t\t\tVideoVolume out = VideoVolume(l, w, h);\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tcv::multiply(cvConvertMatTo<float, N>(frame(t)), cvConvertMatTo<float, N>(mask.frame(t)), out.frame(t), 1 / 255.0);\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\ttemplate<typename U, uint M>\n\t\tvoid applyMaskInPlace(const VideoVolume<U, M> & mask) {\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tcv::multiply(cvConvertMatTo<float, N>(frame(t)), cvConvertMatTo<float, N>(mask.frame(t)), frame(t), 1 / 255.0);\n\t\t\t}\n\t\t}\n\n\t\tVideoVolume cutFrames(int numBefore, int numAfter) {\n\t\t\tint diff = l - (numBefore + numAfter);\n\t\t\tassert(diff >= 0);\n\n\t\t\tVideoVolume out(diff, w, h);\n\t\t\tmat(cv::Rect(0, numBefore, w*h*N, diff)).copyTo(out.mat);\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume<uchar, 3> colorMapped(int colormap, double scaling = 1.0) {\n\t\t\t\n\t\t\tint custom_colormap_size;\n\t\t\t/* \n\t\t\t//twilight\n\t\t\tstatic const float r[] = { 0.88575015840754434f, 0.88378520195539056f, 0.88172231059285788f, 0.8795410528270573f, 0.87724880858965482f, 0.87485347508575972f, 0.87233134085124076f, 0.86970474853509816f, 0.86696015505333579f, 0.86408985081463996f, 0.86110245436899846f, 0.85798259245670372f, 0.85472593189256985f, 0.85133714570857189f, 0.84780710702577922f, 0.8441261828674842f, 0.84030420805957784f, 0.83634031809191178f, 0.83222705712934408f, 0.82796894316013536f, 0.82357429680252847f, 0.81904654677937527f, 0.81438982121143089f, 0.8095999819094809f, 0.80469164429814577f, 0.79967075421267997f, 0.79454305089231114f, 0.78931445564608915f, 0.78399101042764918f, 0.77857892008227592f, 0.77308416590170936f, 0.76751108504417864f, 0.76186907937980286f, 0.75616443584381976f, 0.75040346765406696f, 0.74459247771890169f, 0.73873771700494939f, 0.73284543645523459f, 0.72692177512829703f, 0.72097280665536778f, 0.71500403076252128f, 0.70902078134539304f, 0.7030297722540817f, 0.6970365443886174f, 0.69104641009309098f, 0.68506446154395928f, 0.67909554499882152f, 0.67314422559426212f, 0.66721479803752815f, 0.6613112930078745f, 0.65543692326454717f, 0.64959573004253479f, 0.6437910831099849f, 0.63802586828545982f, 0.6323027138710603f, 0.62662402022604591f, 0.62099193064817548f, 0.61540846411770478f, 0.60987543176093062f, 0.60439434200274855f, 0.5989665814482068f, 0.59359335696837223f, 0.58827579780555495f, 0.58301487036932409f, 0.5778116438998202f, 0.5726668948158774f, 0.56758117853861967f, 0.56255515357219343f, 0.55758940419605174f, 0.55268450589347129f, 0.54784098153018634f, 0.54305932424018233f, 0.53834015575176275f, 0.53368389147728401f, 0.529090861832473f, 0.52456151470593582f, 0.52009627392235558f, 0.5156955988596057f, 0.51135992541601927f, 0.50708969576451657f, 0.5028853540415561f, 0.49874733661356069f, 0.4946761847863938f, 0.49067224938561221f, 0.4867359599430568f, 0.4828677867260272f, 0.47906816236197386f, 0.47533752394906287f, 0.47167629518877091f, 0.46808490970531597f, 0.46456376716303932f, 0.46111326647023881f, 0.45773377230160567f, 0.45442563977552913f, 0.45118918687617743f, 0.44802470933589172f, 0.44493246854215379f, 0.44191271766696399f, 0.43896563958048396f, 0.43609138958356369f, 0.43329008867358393f, 0.43056179073057571f, 0.42790652284925834f, 0.42532423665011354f, 0.42281485675772662f, 0.42037822361396326f, 0.41801414079233629f, 0.4157223260454232f, 0.41350245743314729f, 0.41135414697304568f, 0.4092768899914751f, 0.40727018694219069f, 0.40533343789303178f, 0.40346600333905397f, 0.40166714010896104f, 0.39993606933454834f, 0.3982719152586337f, 0.39667374905665609f, 0.39514058808207631f, 0.39367135736822567f, 0.39226494876209317f, 0.39092017571994903f, 0.38963580160340855f, 0.38841053300842432f, 0.38724301459330251f, 0.38613184178892102f, 0.38507556793651387f, 0.38407269378943537f, 0.38312168084402748f, 0.38222094988570376f, 0.38136887930454161f, 0.38056380696565623f, 0.37980403744848751f, 0.37908789283110761f, 0.378413635091359f, 0.37777949753513729f, 0.37718371844251231f, 0.37662448930806297f, 0.37610001286385814f, 0.37560846919442398f, 0.37514802505380473f, 0.37471686019302231f, 0.37431313199312338f, 0.37393499330475782f, 0.3735806215098284f, 0.37324816143326384f, 0.37293578646665032f, 0.37264166757849604f, 0.37236397858465387f, 0.37210089702443822f, 0.3718506155898596f, 0.37161133234400479f, 0.37138124223736607f, 0.37115856636209105f, 0.37094151551337329f, 0.37072833279422668f, 0.37051738634484427f, 0.37030682071842685f, 0.37009487130772695f, 0.36987980329025361f, 0.36965987626565955f, 0.36943334591276228f, 0.36919847837592484f, 0.36895355306596778f, 0.36869682231895268f, 0.36842655638020444f, 0.36814101479899719f, 0.36783843696531082f, 0.36751707094367697f, 0.36717513650699446f, 0.36681085540107988f, 0.36642243251550632f, 0.36600853966739794f, 0.36556698373538982f, 0.36509579845886808f, 0.36459308890125008f, 0.36405693022088509f, 0.36348537610385145f, 0.36287643560041027f, 0.36222809558295926f, 0.36153829010998356f, 0.36080493826624654f, 0.36002681809096376f, 0.35920088560930186f, 0.35832489966617809f, 0.35739663292915563f, 0.35641381143126327f, 0.35537415306906722f, 0.35427534960663759f, 0.35311574421123737f, 0.35189248608873791f, 0.35060304441931012f, 0.34924513554955644f, 0.34781653238777782f, 0.34631507175793091f, 0.34473901574536375f, 0.34308600291572294f, 0.34135411074506483f, 0.33954168752669694f, 0.33764732090671112f, 0.33566978565015315f, 0.33360804901486002f, 0.33146154891145124f, 0.32923005203231409f, 0.3269137124539796f, 0.32451307931207785f, 0.32202882276069322f, 0.31946262395497965f, 0.31681648089023501f, 0.31409278414755532f, 0.31129434479712365f, 0.30842444457210105f, 0.30548675819945936f, 0.30248536364574252f, 0.29942483960214772f, 0.29631000388905288f, 0.29314593096985248f, 0.28993792445176608f, 0.28669151388283165f, 0.28341239797185225f, 0.28010638576975472f, 0.27677939615815589f, 0.27343739342450812f, 0.27008637749114051f, 0.26673233211995284f, 0.26338121807151404f, 0.26003895187439957f, 0.25671191651083902f, 0.25340685873736807f, 0.25012845306199383f, 0.24688226237958999f, 0.24367372557466271f, 0.24050813332295939f, 0.23739062429054825f, 0.23433055727563878f, 0.23132955273021344f, 0.2283917709422868f, 0.22552164337737857f, 0.22272706739121817f, 0.22001251100779617f, 0.21737845072382705f, 0.21482843531473683f, 0.21237411048541005f, 0.21001214221188125f, 0.2077442377448806f, 0.20558051999470117f, 0.20352007949514977f, 0.20156133764129841f, 0.19971571438603364f, 0.19794834061899208f, 0.1960826032659409f, 0.19410351363791453f, 0.19199449184606268f, 0.18975853639094634f, 0.18739228342697645f, 0.18488035509396164f, 0.18774482037046955f, 0.19049578401722037f, 0.1931548636579131f, 0.19571853588267552f, 0.19819343656336558f, 0.20058760685133747f, 0.20290365333558247f, 0.20531725273301316f, 0.20785704662965598f, 0.21052882914958676f, 0.2133313859647627f, 0.21625279838647882f, 0.21930503925136402f, 0.22247308588973624f, 0.2257539681670791f, 0.22915620278592841f, 0.23266299920501882f, 0.23627495835774248f, 0.23999586188690308f, 0.24381149720247919f, 0.24772092990501099f, 0.25172899728289466f, 0.25582135547481771f, 0.25999463887892144f, 0.26425512207060942f, 0.26859095948172862f, 0.27299701518897301f, 0.27747150809142801f, 0.28201746297366942f, 0.28662309235899847f, 0.29128515387578635f, 0.2960004726065818f, 0.30077276812918691f, 0.30559226007249934f, 0.31045520848595526f, 0.31535870009205808f, 0.32029986557994061f, 0.32527888860401261f, 0.33029174471181438f, 0.33533353224455448f, 0.34040164359597463f, 0.34549355713871799f, 0.35060678246032478f, 0.35573889947341125f, 0.36088752387578377f, 0.36605031412464006f, 0.37122508431309342f, 0.3764103053221462f, 0.38160247377467543f, 0.38679939079544168f, 0.39199887556812907f, 0.39719876876325577f, 0.40239692379737496f, 0.40759120392688708f, 0.41277985630360303f, 0.41796105205173684f, 0.42313214269556043f, 0.42829101315789753f, 0.4334355841041439f, 0.43856378187931538f, 0.44367358645071275f, 0.44876299173174822f, 0.45383005086999889f, 0.45887288947308297f, 0.46389102840284874f, 0.46888111384598413f, 0.473841437035254f, 0.47877034239726296f, 0.48366628618847957f, 0.48852847371852987f, 0.49335504375145617f, 0.49814435462074153f, 0.50289524974970612f, 0.50760681181053691f, 0.51227835105321762f, 0.51690848800544464f, 0.52149652863229956f, 0.52604189625477482f, 0.53054420489856446f, 0.5350027976174474f, 0.53941736649199057f, 0.54378771313608565f, 0.54811370033467621f, 0.55239521572711914f, 0.55663229034969341f, 0.56082499039117173f, 0.56497343529017696f, 0.56907784784011428f, 0.57313845754107873f, 0.57715550812992045f, 0.58112932761586555f, 0.58506024396466882f, 0.58894861935544707f, 0.59279480536520257f, 0.59659918109122367f, 0.60036213010411577f, 0.60408401696732739f, 0.60776523994818654f, 0.6114062072731884f, 0.61500723236391375f, 0.61856865258877192f, 0.62209079821082613f, 0.62557416500434959f, 0.62901892016985872f, 0.63242534854210275f, 0.6357937104834237f, 0.6391243387840212f, 0.642417577481186f, 0.64567349382645434f, 0.64889230169458245f, 0.65207417290277303f, 0.65521932609327127f, 0.6583280801134499f, 0.66140037532601781f, 0.66443632469878844f, 0.66743603766369131f, 0.67039959547676198f, 0.67332725564817331f, 0.67621897924409746f, 0.67907474028157344f, 0.68189457150944521f, 0.68467850942494535f, 0.68742656435169625f, 0.6901389321505248f, 0.69281544846764931f, 0.69545608346891119f, 0.6980608153581771f, 0.70062962477242097f, 0.70316249458814151f, 0.70565951122610093f, 0.70812059568420482f, 0.7105456546582587f, 0.71293466839773467f, 0.71528760614847287f, 0.71760444908133847f, 0.71988521490549851f, 0.7221299918421461f, 0.72433865647781592f, 0.72651122900227549f, 0.72864773856716547f, 0.73074820754845171f, 0.73281270506268747f, 0.73484133598564938f, 0.73683422173585866f, 0.73879140024599266f, 0.74071301619506091f, 0.7425992159973317f, 0.74445018676570673f, 0.74626615789163442f, 0.74804739275559562f, 0.74979420547170472f, 0.75150685045891663f, 0.75318566369046569f, 0.75483105066959544f, 0.75644341577140706f, 0.75802325538455839f, 0.75957111105340058f, 0.7610876378057071f, 0.76257333554052609f, 0.76402885609288662f, 0.76545492593330511f, 0.76685228950643891f, 0.76822176599735303f, 0.7695642334401418f, 0.77088091962302474f, 0.77217257229605551f, 0.77344021829889886f, 0.77468494746063199f, 0.77590790730685699f, 0.7771103295521099f, 0.77829345807633121f, 0.77945862731506643f, 0.78060774749483774f, 0.78174180478981836f, 0.78286225264440912f, 0.78397060836414478f, 0.78506845019606841f, 0.78615737132332963f, 0.78723904108188347f, 0.78831514045623963f, 0.78938737766251943f, 0.79045776847727878f, 0.79152832843475607f, 0.79260034304237448f, 0.79367559698664958f, 0.79475585972654039f, 0.79584292379583765f, 0.79693854719951607f, 0.79804447815136637f, 0.7991624518501963f, 0.80029415389753977f, 0.80144124292560048f, 0.80260531146112946f, 0.80378792531077625f, 0.80499054790810298f, 0.80621460526927058f, 0.8074614045096935f, 0.80873219170089694f, 0.81002809466520687f, 0.81135014011763329f, 0.81269922039881493f, 0.81407611046993344f, 0.81548146627279483f, 0.81691575775055891f, 0.81837931164498223f, 0.81987230650455289f, 0.8213947205565636f, 0.82294635110428427f, 0.8245268129450285f, 0.82613549710580259f, 0.8277716072353446f, 0.82943407816481474f, 0.83112163529096306f, 0.83283277185777982f, 0.8345656905566583f, 0.83631898844737929f, 0.83809123476131964f, 0.83987839884120874f, 0.84167750766845151f, 0.84348529222933699f, 0.84529810731955113f, 0.84711195507965098f, 0.84892245563117641f, 0.85072697023178789f, 0.85251907207708444f, 0.85429219611470464f, 0.85604022314725403f, 0.85775662943504905f, 0.8594346370300241f, 0.86107117027565516f, 0.86265601051127572f, 0.86418343723941027f, 0.86564934325605325f, 0.86705314907048503f, 0.86839954695818633f, 0.86969131502613806f, 0.87093846717297507f, 0.87215331978454325f, 0.87335171360916275f, 0.87453793320260187f, 0.87571458709961403f, 0.87687848451614692f, 0.87802298436649007f, 0.87913244240792765f, 0.88019293315695812f, 0.88119169871341951f, 0.88211542489401606f, 0.88295168595448525f, 0.88369127145898041f, 0.88432713054113543f, 0.88485138159908572f, 0.88525897972630474f, 0.88554714811952384f, 0.88571155122845646f };\n\t\t\tstatic const float g[] = { 0.85000924943067835f, 0.85072940540310626f, 0.85127594077653468f, 0.85165675407495722f, 0.85187028338870274f, 0.85191526123023187f, 0.85180165478080894f, 0.85152403004797894f, 0.8510896085314068f, 0.85050391167507788f, 0.84976754857001258f, 0.84888934810281835f, 0.84787488124672816f, 0.84672735796116472f, 0.8454546229209523f, 0.84406482711037389f, 0.8425605950855084f, 0.84094796518951942f, 0.83923490627754482f, 0.83742600751395202f, 0.83552487764795436f, 0.8335364929949034f, 0.83146558694197847f, 0.82931896673505456f, 0.82709838780560663f, 0.82480781812080928f, 0.82245116226304615f, 0.82003213188702007f, 0.81755426400533426f, 0.81502089378742548f, 0.81243524735466011f, 0.8098007598713145f, 0.80711949387647486f, 0.80439408733477935f, 0.80162699008965321f, 0.79882047719583249f, 0.79597665735031009f, 0.79309746468844067f, 0.7901846863592763f, 0.78723995923452639f, 0.78426487091581187f, 0.78126088716070907f, 0.77822904973358131f, 0.77517050008066057f, 0.77208629460678091f, 0.7689774029354699f, 0.76584472131395898f, 0.76268908733890484f, 0.7595112803730375f, 0.75631202708719025f, 0.75309208756768431f, 0.74985201221941766f, 0.7465923800833657f, 0.74331376714033193f, 0.74001672160131404f, 0.73670175403699445f, 0.73336934798923203f, 0.73001995232739691f, 0.72665398759758293f, 0.7232718614323369f, 0.71987394892246725f, 0.7164606049658685f, 0.71303214646458135f, 0.70958887676997473f, 0.70613106157153982f, 0.7026589535425779f, 0.69917279302646274f, 0.69567278381629649f, 0.69215911458254054f, 0.68863194515166382f, 0.68509142218509878f, 0.68153767253065878f, 0.67797081129095405f, 0.67439093705212727f, 0.67079812302806219f, 0.66719242996142225f, 0.66357391434030388f, 0.65994260812897998f, 0.65629853981831865f, 0.65264172403146448f, 0.64897216734095264f, 0.6452898684900934f, 0.64159484119504429f, 0.63788704858847078f, 0.63416646251100506f, 0.6304330455306234f, 0.62668676251860134f, 0.62292757283835809f, 0.61915543242884641f, 0.61537028695790286f, 0.61157208822864151f, 0.607760777169989f, 0.60393630046586455f, 0.60009859503858665f, 0.59624762051353541f, 0.59238331452146575f, 0.5885055998308617f, 0.58461441100175571f, 0.58070969241098491f, 0.57679137998186081f, 0.57285941625606673f, 0.56891374572457176f, 0.5649543060909209f, 0.56098104959950301f, 0.55699392126996583f, 0.55299287158108168f, 0.54897785421888889f, 0.54494882715350401f, 0.54090574771098476f, 0.53684857765005933f, 0.53277730177130322f, 0.52869188011057411f, 0.52459228174983119f, 0.52047847653840029f, 0.51635044969688759f, 0.51220818143218516f, 0.50805166539276136f, 0.50388089053847973f, 0.49969585326377758f, 0.49549655777451179f, 0.49128300332899261f, 0.48705520251223039f, 0.48281316715123496f, 0.47855691131792805f, 0.47428645933635388f, 0.4700018340988123f, 0.46570306719930193f, 0.46139018782416635f, 0.45706323581407199f, 0.45272225034283325f, 0.44836727669277859f, 0.44399837208633719f, 0.43961558821222629f, 0.43521897612544935f, 0.43080859411413064f, 0.4263845142616835f, 0.42194680223454828f, 0.41749553747893614f, 0.41303079952477062f, 0.40855267638072096f, 0.4040612609993941f, 0.3995566498711684f, 0.39503894828283309f, 0.39050827529375831f, 0.38596474386057539f, 0.38140848555753937f, 0.37683963835219841f, 0.37225835004836849f, 0.36766477862108266f, 0.36305909736982378f, 0.35844148285875221f, 0.3538121372967869f, 0.34917126878479027f, 0.34451911410230168f, 0.33985591488818123f, 0.33518193808489577f, 0.33049741244307851f, 0.32580269697872455f, 0.3210981375964933f, 0.31638410101153364f, 0.31166098762951971f, 0.30692923551862339f, 0.30218932176507068f, 0.29744175492366276f, 0.29268709856150099f, 0.28792596437778462f, 0.28315901221182987f, 0.27838697181297761f, 0.27361063317090978f, 0.26883085667326956f, 0.26404857724525643f, 0.25926481158628106f, 0.25448043878086224f, 0.24969683475296395f, 0.24491536803550484f, 0.24013747024823828f, 0.23536470386204195f, 0.23059876218396419f, 0.22584149293287031f, 0.22109488427338303f, 0.21636111429594002f, 0.21164251793458128f, 0.20694122817889948f, 0.20226037920758122f, 0.197602942459778f, 0.19297208197842461f, 0.18837119869242164f, 0.18380392577704466f, 0.17927413271618647f, 0.17478570377561287f, 0.17034320478524959f, 0.16595129984720861f, 0.16161477763045118f, 0.15733863511152979f, 0.15312802296627787f, 0.14898820589826409f, 0.14492465359918028f, 0.1409427920655632f, 0.13704801896718169f, 0.13324562282438077f, 0.12954074251271822f, 0.12593818301005921f, 0.12244245263391232f, 0.11905764321981127f, 0.1157873496841953f, 0.11263459791730848f, 0.10960114111258401f, 0.10668879882392659f, 0.10389861387653518f, 0.10123077676403242f, 0.098684771934052201f, 0.096259385340577736f, 0.093952764840823738f, 0.091761187397303601f, 0.089682253716750038f, 0.087713250960463951f, 0.085850656889620708f, 0.08409078829085731f, 0.082429873848480689f, 0.080864153365499375f, 0.079389994802261526f, 0.078003941033788216f, 0.076702800237496066f, 0.075483675584275545f, 0.074344018028546205f, 0.073281657939897077f, 0.072294781043362205f, 0.071380106242082242f, 0.070533582926851829f, 0.069758206429106989f, 0.069053639449204451f, 0.068419855150922693f, 0.067857103814855602f, 0.067365888050555517f, 0.066935599661639394f, 0.066576186939090592f, 0.06628997924139618f, 0.066078173119395595f, 0.065933790675651943f, 0.065857918918907604f, 0.065859661233562045f, 0.065940385613778491f, 0.066085024661758446f, 0.066308573918947178f, 0.06661453200418091f, 0.066990462397868739f, 0.067444179612424215f, 0.067983271026200248f, 0.068592710553704722f, 0.069314066071660657f, 0.070321227242423623f, 0.071608304856891569f, 0.073182830649273306f, 0.075019861862143766f, 0.077102096899588329f, 0.079425730279723883f, 0.077251588468039312f, 0.075311278416787641f, 0.073606819040117955f, 0.072157781039602742f, 0.070974625252738788f, 0.070064576149984209f, 0.069435248580458964f, 0.068919592266397572f, 0.068484398797025281f, 0.06812195249816172f, 0.067830148426026665f, 0.067616330270516389f, 0.067465786362940039f, 0.067388214053092838f, 0.067382132300147474f, 0.067434730871152565f, 0.067557104388479783f, 0.06774359820987802f, 0.067985029964779953f, 0.068289851529011875f, 0.068653337909486523f, 0.069064630826035506f, 0.06953231029187984f, 0.070053855603861875f, 0.070616595622995437f, 0.071226716277922458f, 0.071883555446163511f, 0.072582969899254779f, 0.073315693214040967f, 0.074088460826808866f, 0.074899049847466703f, 0.075745336000958424f, 0.076617824336164764f, 0.077521963107537312f, 0.078456871676182177f, 0.079420997315243186f, 0.080412994737554838f, 0.081428390076546092f, 0.08246763389003825f, 0.083532434119003962f, 0.084622236191702671f, 0.085736654965126335f, 0.08687555176033529f, 0.088038974350243354f, 0.089227194362745205f, 0.090440685427697898f, 0.091679997480262732f, 0.092945198093777909f, 0.094238731263712183f, 0.09556181960083443f, 0.09691583650296684f, 0.098302320968278623f, 0.099722930314950553f, 0.10117945586419633f, 0.1026734006932461f, 0.10420644885760968f, 0.10578120994917611f, 0.1073997763055258f, 0.1090642347484701f, 0.11077667828375456f, 0.11253912421257944f, 0.11435355574622549f, 0.11622183788331528f, 0.11814571137706886f, 0.12012561256850712f, 0.12216445576414045f, 0.12426354237989065f, 0.12642401401409453f, 0.12864679022013889f, 0.13093210934893723f, 0.13328091630401023f, 0.13569380302451714f, 0.13817086581280427f, 0.14071192654913128f, 0.14331656120063752f, 0.14598463068714407f, 0.14871544765633712f, 0.15150818660835483f, 0.15436183633886777f, 0.15727540775107324f, 0.16024769309971934f, 0.16327738551419116f, 0.1663630904279047f, 0.16950338809328983f, 0.17269677158182117f, 0.17594170887918095f, 0.17923664950367169f, 0.18258004462335425f, 0.18597036007065024f, 0.18940601489760422f, 0.19288548904692518f, 0.19640737049066315f, 0.19997020971775276f, 0.20357251410079796f, 0.207212956082026f, 0.21089030138947745f, 0.21460331490206347f, 0.21835070166659282f, 0.22213124697023234f, 0.22594402043981826f, 0.22978799249179921f, 0.2336621873300741f, 0.23756535071152696f, 0.24149689191922535f, 0.24545598775548677f, 0.24944185818822678f, 0.25345365461983138f, 0.257490519876798f, 0.26155203161615281f, 0.26563755336209077f, 0.26974650525236699f, 0.27387826652410152f, 0.27803210957665631f, 0.28220778870555907f, 0.28640483614256179f, 0.29062280081258873f, 0.29486126309253047f, 0.29911962764489264f, 0.30339762792450425f, 0.30769497879760166f, 0.31201133280550686f, 0.31634634821222207f, 0.32069970535138104f, 0.32507091815606004f, 0.32945984647042675f, 0.33386622163232865f, 0.33828976326048621f, 0.34273019305341756f, 0.34718723719597999f, 0.35166052978120937f, 0.35614985523380299f, 0.36065500290840113f, 0.36517570519856757f, 0.36971170225223449f, 0.37426272710686193f, 0.37882848839337313f, 0.38340864508963057f, 0.38800301593162145f, 0.3926113126792577f, 0.39723324476747235f, 0.401868526884681f, 0.4065168468778026f, 0.41117787004519513f, 0.41585125850290111f, 0.42053672992315327f, 0.4252339389526239f, 0.42994254036133867f, 0.43466217184617112f, 0.43939245044973502f, 0.44413297780351974f, 0.44888333481548809f, 0.45364314496866825f, 0.45841199172949604f, 0.46318942799460555f, 0.46797501437948458f, 0.4727682731566229f, 0.47756871222057079f, 0.48237579130289127f, 0.48718906673415824f, 0.49200802533379656f, 0.49683212909727231f, 0.5016608471009063f, 0.50649362371287909f, 0.5113298901696085f, 0.51616892643469103f, 0.5210102658711383f, 0.52585332093451564f, 0.53069749384776732f, 0.53554217882461186f, 0.54038674910561235f, 0.54523059488426595f, 0.55007308413977274f, 0.55491335744890613f, 0.55975098052594863f, 0.56458533111166875f, 0.56941578326710418f, 0.5742417003617839f, 0.5790624629815756f, 0.58387743744557208f, 0.58868600173562435f, 0.5934875421745599f, 0.59828134277062461f, 0.60306670593147205f, 0.60784322087037024f, 0.61261029334072192f, 0.61736734400220705f, 0.62211378808451145f, 0.62684905679296699f, 0.63157258225089552f, 0.63628379372029187f, 0.64098213306749863f, 0.64566703459218766f, 0.65033793748103852f, 0.65499426549472628f, 0.65963545027564163f, 0.66426089585282289f, 0.6688700095398864f, 0.67346216702194517f, 0.67803672673971815f, 0.68259301546243389f, 0.68713033714618876f, 0.69164794791482131f, 0.69614505508308089f, 0.70062083014783982f, 0.70507438189635097f, 0.70950474978787481f, 0.7139109141951604f, 0.71829177331290062f, 0.72264614312088882f, 0.72697275518238258f, 0.73127023324078089f, 0.7355371221572935f, 0.73977184647638616f, 0.74397271817459876f, 0.7481379479992134f, 0.75226548952875261f, 0.75635314860808633f, 0.76039907199779677f, 0.76440101200982946f, 0.76835660399870176f, 0.77226338601044719f, 0.77611880236047159f, 0.77992021407650147f, 0.78366457342383888f, 0.78734936133548439f, 0.79097196777091994f, 0.79452963601550608f, 0.79801963142713928f, 0.8014392309950078f, 0.80478517909812231f, 0.80805523804261525f, 0.81124644224653542f, 0.81435544067514909f, 0.81737804041911244f, 0.82030875512181523f, 0.82314158859569164f, 0.82586857889438514f, 0.82848052823709672f, 0.83096715251272624f, 0.83331972948645461f, 0.8355302318472394f, 0.83759238071186537f, 0.83950165618540074f, 0.84125554884475906f, 0.84285224824778615f, 0.84429066717717349f, 0.84557007254559347f, 0.84668970275699273f, 0.84764891761519268f, 0.84844741572055415f, 0.84908426422893801f, 0.84955892810989209f, 0.84987174283631584f, 0.85002186115856315f };\n\t\t\tstatic const float b[] = { 0.8879736506427196f, 0.88723222096949894f, 0.88638056925514819f, 0.8854143767924102f, 0.88434120381311432f, 0.88316926967613829f, 0.88189704355001619f, 0.88053883390003362f, 0.87909766977173343f, 0.87757925784892632f, 0.87599242923439569f, 0.87434038553446281f, 0.8726282980930582f, 0.87086081657350445f, 0.86904036783694438f, 0.86716973322690072f, 0.865250882410458f, 0.86328528001070159f, 0.86127563500427884f, 0.85922399451306786f, 0.85713191328514948f, 0.85500206287010105f, 0.85283759062147024f, 0.85064441601050367f, 0.84842449296974021f, 0.84618210029578533f, 0.84392184786827984f, 0.8416486380471222f, 0.83936747464036732f, 0.8370834463093898f, 0.83480172950579679f, 0.83252816638059668f, 0.830266486168872f, 0.82802138994719998f, 0.82579737851082424f, 0.82359867586156521f, 0.82142922780433014f, 0.81929263384230377f, 0.81719217466726379f, 0.81513073920879264f, 0.81311116559949914f, 0.81113591855117928f, 0.80920618848056969f, 0.80732335380063447f, 0.80548841690679074f, 0.80370206267176914f, 0.8019646617300199f, 0.80027628545809526f, 0.79863674654537764f, 0.7970456043491897f, 0.79550271129031047f, 0.79400674021499107f, 0.79255653201306053f, 0.79115100459573173f, 0.78978892762640429f, 0.78846901316334561f, 0.78718994624696581f, 0.78595022706750484f, 0.78474835732694714f, 0.78358295593535587f, 0.78245259899346642f, 0.78135588237640097f, 0.78029141405636515f, 0.77925781820476592f, 0.77825345121025524f, 0.77727702680911992f, 0.77632748534275298f, 0.77540359142309845f, 0.7745041337932782f, 0.7736279426902245f, 0.77277386473440868f, 0.77194079697835083f, 0.77112734439057717f, 0.7703325054879735f, 0.76955552292313134f, 0.76879541714230948f, 0.76805119403344102f, 0.76732191489596169f, 0.76660663780645333f, 0.76590445660835849f, 0.76521446718174913f, 0.76453578734180083f, 0.76386719002130909f, 0.76320812763163837f, 0.76255780085924041f, 0.76191537149895305f, 0.76128000375662419f, 0.76065085571817748f, 0.76002709227883047f, 0.75940789891092741f, 0.75879242623025811f, 0.75817986436807139f, 0.75756936901859162f, 0.75696013660606487f, 0.75635120643246645f, 0.75574176474107924f, 0.7551311041857901f, 0.75451838884410671f, 0.75390276208285945f, 0.7532834105961016f, 0.75265946532566674f, 0.75203008099312696f, 0.75139443521914839f, 0.75075164989005116f, 0.75010086988227642f, 0.7494412559451894f, 0.74877193167001121f, 0.74809204459000522f, 0.74740073297543086f, 0.74669712855065784f, 0.74598030635707824f, 0.74524942637581271f, 0.74450365836708132f, 0.74374215223567086f, 0.7429640345324835f, 0.74216844571317986f, 0.74135450918099721f, 0.74052138580516735f, 0.73966820211715711f, 0.738794102296364f, 0.73789824784475078f, 0.73697977133881254f, 0.73603782546932739f, 0.73507157641157261f, 0.73408016787854391f, 0.7330627749243106f, 0.73201854033690505f, 0.73094665432902683f, 0.72984626791353258f, 0.72871656144003782f, 0.72755671317141346f, 0.72636587045135315f, 0.72514323778761092f, 0.72388798691323131f, 0.72259931993061044f, 0.72127639993530235f, 0.71991841524475775f, 0.71852454736176108f, 0.71709396919920232f, 0.71562585091587549f, 0.7141193695725726f, 0.71257368516500463f, 0.71098796522377461f, 0.70936134293478448f, 0.70769297607310577f, 0.70598200974806036f, 0.70422755780589941f, 0.7024287314570723f, 0.70058463496520773f, 0.69869434615073722f, 0.69675695810256544f, 0.69477149919380887f, 0.69273703471928827f, 0.69065253586464992f, 0.68851703379505125f, 0.68632948169606767f, 0.68408888788857214f, 0.68179411684486679f, 0.67944405399056851f, 0.67703755438090574f, 0.67457344743419545f, 0.67205052849120617f, 0.66946754331614522f, 0.66682322089824264f, 0.66411625298236909f, 0.66134526910944602f, 0.65850888806972308f, 0.65560566838453704f, 0.65263411711618635f, 0.64959272297892245f, 0.64647991652908243f, 0.64329409140765537f, 0.64003361803368586f, 0.63669675187488584f, 0.63328173520055586f, 0.62978680155026101f, 0.62621013451953023f, 0.62254988622392882f, 0.61880417410823019f, 0.61497112346096128f, 0.61104880679640927f, 0.60703532172064711f, 0.60292845431916875f, 0.5987265295935138f, 0.59442768517501066f, 0.59003011251063131f, 0.5855320765920552f, 0.58093191431832802f, 0.57622809660668717f, 0.57141871523555288f, 0.56650284911216653f, 0.56147964703993225f, 0.55634837474163779f, 0.55110853452703257f, 0.5457599924248665f, 0.54030245920406539f, 0.53473704282067103f, 0.52906500940336754f, 0.52328797535085236f, 0.51740807573979475f, 0.51142807215168951f, 0.50535164796654897f, 0.49918274588431072f, 0.49292595612342666f, 0.48658646495697461f, 0.48017007211645196f, 0.47368494725726878f, 0.46713728801395243f, 0.46053414662739794f, 0.45388335612058467f, 0.44719313715161618f, 0.44047194882050544f, 0.43372849999361113f, 0.42697404043749887f, 0.42021619665853854f, 0.41346259134143476f, 0.40672178082365834f, 0.40000214725256295f, 0.39331182532243375f, 0.38665868550105914f, 0.38005028528138707f, 0.37349382846504675f, 0.36699616136347685f, 0.36056376228111864f, 0.35420276066240958f, 0.34791888996380105f, 0.3417175669546984f, 0.33560648984600089f, 0.3295945757321303f, 0.32368100685760637f, 0.31786993834254956f, 0.31216524050888372f, 0.30657054493678321f, 0.30108922184065873f, 0.29574009929867601f, 0.29051361067988485f, 0.28541074411068496f, 0.28043398847505197f, 0.27559714652053702f, 0.27090279994325861f, 0.26634209349669508f, 0.26191675992376573f, 0.25765165093569542f, 0.2535289048041211f, 0.24954644291943817f, 0.24572497420147632f, 0.24205576625191821f, 0.23852974228695395f, 0.23517094067076993f, 0.23194647381302336f, 0.22874673279569585f, 0.22558727307410353f, 0.22243385243433622f, 0.2193005075652994f, 0.21618875376309582f, 0.21307651648984993f, 0.21387448578597812f, 0.2146562337112265f, 0.21542362939081539f, 0.21617499187076789f, 0.21690975060032436f, 0.21762721310371608f, 0.21833167885096033f, 0.21911516689288835f, 0.22000133917653536f, 0.22098759107715404f, 0.22207043213024291f, 0.22324568672294431f, 0.22451023616807558f, 0.22585960379408354f, 0.22728984778098055f, 0.22879681433956656f, 0.23037617493752832f, 0.23202360805926608f, 0.23373434258507808f, 0.23550427698321885f, 0.2373288009471749f, 0.23920260612763083f, 0.24112190491594204f, 0.24308218808684579f, 0.24507758869355967f, 0.24710443563450618f, 0.24915847093232929f, 0.25123493995942769f, 0.25332800295084507f, 0.25543478673717029f, 0.25755101595750435f, 0.25967245030364566f, 0.26179294097819672f, 0.26391006692119662f, 0.2660200572779356f, 0.26811904076941961f, 0.27020322893039511f, 0.27226772884656186f, 0.27430929404579435f, 0.27632534356790039f, 0.27831254595259397f, 0.28026769921081435f, 0.28218770540182386f, 0.2840695897279818f, 0.28591050458531014f, 0.2877077458811747f, 0.28945865397633169f, 0.29116024157313919f, 0.29281107506269488f, 0.29440901248173756f, 0.29595212005509081f, 0.29743856476285779f, 0.29886674369733968f, 0.30023519507728602f, 0.30154226437468967f, 0.30278652039631843f, 0.3039675809469457f, 0.30508479060294547f, 0.30613767928289148f, 0.30712600062348083f, 0.30804973095465449f, 0.30890905921943196f, 0.30970441249844921f, 0.31043636979038808f, 0.31110343446582983f, 0.31170911458932665f, 0.31225470169927194f, 0.31274172735821959f, 0.31317188565991266f, 0.31354553695453014f, 0.31386561956734976f, 0.314135190862664f, 0.31435662153833671f, 0.31453200120082569f, 0.3146630922831542f, 0.31475407592280041f, 0.31480767954534428f, 0.31482653406646727f, 0.31481299789187128f, 0.31477085207396532f, 0.31470295028655965f, 0.31461204226295625f, 0.31450102990914708f, 0.31437291554615371f, 0.31423043195101424f, 0.31407639883970623f, 0.3139136046337036f, 0.31374440956796529f, 0.31357126868520002f, 0.31339704333572083f, 0.31322399394183942f, 0.31305401163732732f, 0.31288922211590126f, 0.31273234839304942f, 0.31258523031121233f, 0.31244934410414688f, 0.31232652641170694f, 0.31221903291870201f, 0.31212881396435238f, 0.31205680685765741f, 0.31200463838728931f, 0.31197383273627388f, 0.31196698314912269f, 0.31198447195645718f, 0.31202765974624452f, 0.31209793953300591f, 0.31219689612063978f, 0.31232631707560987f, 0.31248673753935263f, 0.31267941819570189f, 0.31290560605819168f, 0.3131666792687211f, 0.3134643447952643f, 0.31379912926498488f, 0.31417223403606975f, 0.31458483752056837f, 0.31503813956872212f, 0.31553372323982209f, 0.3160724937230589f, 0.31665545668946665f, 0.31728380489244951f, 0.31795870784057567f, 0.31868137622277692f, 0.31945332332898302f, 0.3202754315314667f, 0.32114884306985791f, 0.32207478855218091f, 0.32305449047765694f, 0.32408913679491225f, 0.32518014084085567f, 0.32632861885644465f, 0.32753574162788762f, 0.3288027427038317f, 0.3301308728723546f, 0.33152138620958932f, 0.33297555200245399f, 0.33449469983585844f, 0.33607995965691828f, 0.3377325942005665f, 0.33945384341064017f, 0.3412449533046818f, 0.34310715173410822f, 0.34504169470809071f, 0.34704978520758401f, 0.34913260148542435f, 0.35129130890802607f, 0.35352709245374592f, 0.35584108091122535f, 0.35823439142300639f, 0.36070813602540136f, 0.36326337558360278f, 0.36590112443835765f, 0.36862236642234769f, 0.3714280448394211f, 0.37431909037543515f, 0.37729635531096678f, 0.380360657784311f, 0.38351275723852291f, 0.38675335037837993f, 0.39008308392311997f, 0.39350254000115381f, 0.39701221751773474f, 0.40061257089416885f, 0.40430398069682483f, 0.40808667584648967f, 0.41196089987122869f, 0.41592679539764366f, 0.41998440356963762f, 0.42413367909988375f, 0.42837450371258479f, 0.432706647838971f, 0.43712979856444761f, 0.44164332426364639f, 0.44624687186865436f, 0.45093985823706345f, 0.45572154742892063f, 0.46059116206904965f, 0.46554778281918402f, 0.47059039582133383f, 0.47571791879076081f, 0.48092913815357724f, 0.48622257801969754f, 0.49159667021646397f, 0.49705020621532009f, 0.50258161291269432f, 0.50818921213102985f, 0.51387124091909786f, 0.5196258425240281f, 0.52545108144834785f, 0.53134495942561433f, 0.53730535185141037f, 0.5433300863249918f, 0.54941691584603647f, 0.55556350867083815f, 0.56176745110546977f, 0.56802629178649788f, 0.57433746373459582f, 0.58069834805576737f, 0.58710626908082753f, 0.59355848909050757f, 0.60005214820435104f, 0.6065843782630862f, 0.61315221209322646f, 0.61975260637257923f, 0.62638245478933297f, 0.63303857040067113f, 0.63971766697672761f, 0.6464164243818421f, 0.65313137915422603f, 0.65985900156216504f, 0.66659570204682972f, 0.67333772009301907f, 0.68008125203631464f, 0.68682235874648545f, 0.69355697649863846f, 0.70027999028864962f, 0.70698561390212977f, 0.71367147811129228f, 0.72033299387284622f, 0.72696536998972039f, 0.73356368240541492f, 0.74012275762807056f, 0.74663719293664366f, 0.7530974636118285f, 0.7594994148789691f, 0.76583801477914104f, 0.77210610037674143f, 0.77829571667247499f, 0.78439788751383921f, 0.79039529663736285f, 0.796282666437655f, 0.80204612696863953f, 0.80766972324164554f, 0.81313419626911398f, 0.81841638963128993f, 0.82350476683173168f, 0.82838497261149613f, 0.8330486712880828f, 0.83748851001197089f, 0.84171925358069011f, 0.84575537519027078f, 0.84961373549150254f, 0.85330645352458923f, 0.85685572291039636f, 0.86027399927156634f, 0.86356595168669881f, 0.86673765046233331f, 0.86979617048190971f, 0.87274147101441557f, 0.87556785228242973f, 0.87828235285372469f, 0.88088414794024839f, 0.88336206121170946f, 0.88572538990087124f };\n\t\t\t custom_colormap_size = 510;\n\t\t\t*/\n\n\t\t\t//inferno\n\t\t\tstatic const float r[] = { 0.001462f, 0.002267f, 0.003299f, 0.004547f, 0.006006f, 0.007676f, 0.009561f, 0.011663f, 0.013995f, 0.016561f, 0.019373f, 0.022447f, 0.025793f, 0.029432f, 0.033385f, 0.037668f, 0.042253f, 0.046915f, 0.051644f, 0.056449f, 0.061340f, 0.066331f, 0.071429f, 0.076637f, 0.081962f, 0.087411f, 0.092990f, 0.098702f, 0.104551f, 0.110536f, 0.116656f, 0.122908f, 0.129285f, 0.135778f, 0.142378f, 0.149073f, 0.155850f, 0.162689f, 0.169575f, 0.176493f, 0.183429f, 0.190367f, 0.197297f, 0.204209f, 0.211095f, 0.217949f, 0.224763f, 0.231538f, 0.238273f, 0.244967f, 0.251620f, 0.258234f, 0.264810f, 0.271347f, 0.277850f, 0.284321f, 0.290763f, 0.297178f, 0.303568f, 0.309935f, 0.316282f, 0.322610f, 0.328921f, 0.335217f, 0.341500f, 0.347771f, 0.354032f, 0.360284f, 0.366529f, 0.372768f, 0.379001f, 0.385228f, 0.391453f, 0.397674f, 0.403894f, 0.410113f, 0.416331f, 0.422549f, 0.428768f, 0.434987f, 0.441207f, 0.447428f, 0.453651f, 0.459875f, 0.466100f, 0.472328f, 0.478558f, 0.484789f, 0.491022f, 0.497257f, 0.503493f, 0.509730f, 0.515967f, 0.522206f, 0.528444f, 0.534683f, 0.540920f, 0.547157f, 0.553392f, 0.559624f, 0.565854f, 0.572081f, 0.578304f, 0.584521f, 0.590734f, 0.596940f, 0.603139f, 0.609330f, 0.615513f, 0.621685f, 0.627847f, 0.633998f, 0.640135f, 0.646260f, 0.652369f, 0.658463f, 0.664540f, 0.670599f, 0.676638f, 0.682656f, 0.688653f, 0.694627f, 0.700576f, 0.706500f, 0.712396f, 0.718264f, 0.724103f, 0.729909f, 0.735683f, 0.741423f, 0.747127f, 0.752794f, 0.758422f, 0.764010f, 0.769556f, 0.775059f, 0.780517f, 0.785929f, 0.791293f, 0.796607f, 0.801871f, 0.807082f, 0.812239f, 0.817341f, 0.822386f, 0.827372f, 0.832299f, 0.837165f, 0.841969f, 0.846709f, 0.851384f, 0.855992f, 0.860533f, 0.865006f, 0.869409f, 0.873741f, 0.878001f, 0.882188f, 0.886302f, 0.890341f, 0.894305f, 0.898192f, 0.902003f, 0.905735f, 0.909390f, 0.912966f, 0.916462f, 0.919879f, 0.923215f, 0.926470f, 0.929644f, 0.932737f, 0.935747f, 0.938675f, 0.941521f, 0.944285f, 0.946965f, 0.949562f, 0.952075f, 0.954506f, 0.956852f, 0.959114f, 0.961293f, 0.963387f, 0.965397f, 0.967322f, 0.969163f, 0.970919f, 0.972590f, 0.974176f, 0.975677f, 0.977092f, 0.978422f, 0.979666f, 0.980824f, 0.981895f, 0.982881f, 0.983779f, 0.984591f, 0.985315f, 0.985952f, 0.986502f, 0.986964f, 0.987337f, 0.987622f, 0.987819f, 0.987926f, 0.987945f, 0.987874f, 0.987714f, 0.987464f, 0.987124f, 0.986694f, 0.986175f, 0.985566f, 0.984865f, 0.984075f, 0.983196f, 0.982228f, 0.981173f, 0.980032f, 0.978806f, 0.977497f, 0.976108f, 0.974638f, 0.973088f, 0.971468f, 0.969783f, 0.968041f, 0.966243f, 0.964394f, 0.962517f, 0.960626f, 0.958720f, 0.956834f, 0.954997f, 0.953215f, 0.951546f, 0.950018f, 0.948683f, 0.947594f, 0.946809f, 0.946392f, 0.946403f, 0.946903f, 0.947937f, 0.949545f, 0.951740f, 0.954529f, 0.957896f, 0.961812f, 0.966249f, 0.971162f, 0.976511f, 0.982257f, 0.988362f };\n\t\t\tstatic const float g[] = { 0.000466f, 0.001270f, 0.002249f, 0.003392f, 0.004692f, 0.006136f, 0.007713f, 0.009417f, 0.011225f, 0.013136f, 0.015133f, 0.017199f, 0.019331f, 0.021503f, 0.023702f, 0.025921f, 0.028139f, 0.030324f, 0.032474f, 0.034569f, 0.036590f, 0.038504f, 0.040294f, 0.041905f, 0.043328f, 0.044556f, 0.045583f, 0.046402f, 0.047008f, 0.047399f, 0.047574f, 0.047536f, 0.047293f, 0.046856f, 0.046242f, 0.045468f, 0.044559f, 0.043554f, 0.042489f, 0.041402f, 0.040329f, 0.039309f, 0.038400f, 0.037632f, 0.037030f, 0.036615f, 0.036405f, 0.036405f, 0.036621f, 0.037055f, 0.037705f, 0.038571f, 0.039647f, 0.040922f, 0.042353f, 0.043933f, 0.045644f, 0.047470f, 0.049396f, 0.051407f, 0.053490f, 0.055634f, 0.057827f, 0.060060f, 0.062325f, 0.064616f, 0.066925f, 0.069247f, 0.071579f, 0.073915f, 0.076253f, 0.078591f, 0.080927f, 0.083257f, 0.085580f, 0.087896f, 0.090203f, 0.092501f, 0.094790f, 0.097069f, 0.099338f, 0.101597f, 0.103848f, 0.106089f, 0.108322f, 0.110547f, 0.112764f, 0.114974f, 0.117179f, 0.119379f, 0.121575f, 0.123769f, 0.125960f, 0.128150f, 0.130341f, 0.132534f, 0.134729f, 0.136929f, 0.139134f, 0.141346f, 0.143567f, 0.145797f, 0.148039f, 0.150294f, 0.152563f, 0.154848f, 0.157151f, 0.159474f, 0.161817f, 0.164184f, 0.166575f, 0.168992f, 0.171438f, 0.173914f, 0.176421f, 0.178962f, 0.181539f, 0.184153f, 0.186807f, 0.189501f, 0.192239f, 0.195021f, 0.197851f, 0.200728f, 0.203656f, 0.206636f, 0.209670f, 0.212759f, 0.215906f, 0.219112f, 0.222378f, 0.225706f, 0.229097f, 0.232554f, 0.236077f, 0.239667f, 0.243327f, 0.247056f, 0.250856f, 0.254728f, 0.258674f, 0.262692f, 0.266786f, 0.270954f, 0.275197f, 0.279517f, 0.283913f, 0.288385f, 0.292933f, 0.297559f, 0.302260f, 0.307038f, 0.311892f, 0.316822f, 0.321827f, 0.326906f, 0.332060f, 0.337287f, 0.342586f, 0.347957f, 0.353399f, 0.358911f, 0.364492f, 0.370140f, 0.375856f, 0.381636f, 0.387481f, 0.393389f, 0.399359f, 0.405389f, 0.411479f, 0.417627f, 0.423831f, 0.430091f, 0.436405f, 0.442772f, 0.449191f, 0.455660f, 0.462178f, 0.468744f, 0.475356f, 0.482014f, 0.488716f, 0.495462f, 0.502249f, 0.509078f, 0.515946f, 0.522853f, 0.529798f, 0.536780f, 0.543798f, 0.550850f, 0.557937f, 0.565057f, 0.572209f, 0.579392f, 0.586606f, 0.593849f, 0.601122f, 0.608422f, 0.615750f, 0.623105f, 0.630485f, 0.637890f, 0.645320f, 0.652773f, 0.660250f, 0.667748f, 0.675267f, 0.682807f, 0.690366f, 0.697944f, 0.705540f, 0.713153f, 0.720782f, 0.728427f, 0.736087f, 0.743758f, 0.751442f, 0.759135f, 0.766837f, 0.774545f, 0.782258f, 0.789974f, 0.797692f, 0.805409f, 0.813122f, 0.820825f, 0.828515f, 0.836191f, 0.843848f, 0.851476f, 0.859069f, 0.866624f, 0.874129f, 0.881569f, 0.888942f, 0.896226f, 0.903409f, 0.910473f, 0.917399f, 0.924168f, 0.930761f, 0.937159f, 0.943348f, 0.949318f, 0.955063f, 0.960587f, 0.965896f, 0.971003f, 0.975924f, 0.980678f, 0.985282f, 0.989753f, 0.994109f, 0.998364f };\n\t\t\tstatic const float b[] = { 0.013866f, 0.018570f, 0.024239f, 0.030909f, 0.038558f, 0.046836f, 0.055143f, 0.063460f, 0.071862f, 0.080282f, 0.088767f, 0.097327f, 0.105930f, 0.114621f, 0.123397f, 0.132232f, 0.141141f, 0.150164f, 0.159254f, 0.168414f, 0.177642f, 0.186962f, 0.196354f, 0.205799f, 0.215289f, 0.224813f, 0.234358f, 0.243904f, 0.253430f, 0.262912f, 0.272321f, 0.281624f, 0.290788f, 0.299776f, 0.308553f, 0.317085f, 0.325338f, 0.333277f, 0.340874f, 0.348111f, 0.354971f, 0.361447f, 0.367535f, 0.373238f, 0.378563f, 0.383522f, 0.388129f, 0.392400f, 0.396353f, 0.400007f, 0.403378f, 0.406485f, 0.409345f, 0.411976f, 0.414392f, 0.416608f, 0.418637f, 0.420491f, 0.422182f, 0.423721f, 0.425116f, 0.426377f, 0.427511f, 0.428524f, 0.429425f, 0.430217f, 0.430906f, 0.431497f, 0.431994f, 0.432400f, 0.432719f, 0.432955f, 0.433109f, 0.433183f, 0.433179f, 0.433098f, 0.432943f, 0.432714f, 0.432412f, 0.432039f, 0.431594f, 0.431080f, 0.430498f, 0.429846f, 0.429125f, 0.428334f, 0.427475f, 0.426548f, 0.425552f, 0.424488f, 0.423356f, 0.422156f, 0.420887f, 0.419549f, 0.418142f, 0.416667f, 0.415123f, 0.413511f, 0.411829f, 0.410078f, 0.408258f, 0.406369f, 0.404411f, 0.402385f, 0.400290f, 0.398125f, 0.395891f, 0.393589f, 0.391219f, 0.388781f, 0.386276f, 0.383704f, 0.381065f, 0.378359f, 0.375586f, 0.372748f, 0.369846f, 0.366879f, 0.363849f, 0.360757f, 0.357603f, 0.354388f, 0.351113f, 0.347777f, 0.344383f, 0.340931f, 0.337424f, 0.333861f, 0.330245f, 0.326576f, 0.322856f, 0.319085f, 0.315266f, 0.311399f, 0.307485f, 0.303526f, 0.299523f, 0.295477f, 0.291390f, 0.287264f, 0.283099f, 0.278898f, 0.274661f, 0.270390f, 0.266085f, 0.261750f, 0.257383f, 0.252988f, 0.248564f, 0.244113f, 0.239636f, 0.235133f, 0.230606f, 0.226055f, 0.221482f, 0.216886f, 0.212268f, 0.207628f, 0.202968f, 0.198286f, 0.193584f, 0.188860f, 0.184116f, 0.179350f, 0.174563f, 0.169755f, 0.164924f, 0.160070f, 0.155193f, 0.150292f, 0.145367f, 0.140417f, 0.135440f, 0.130438f, 0.125409f, 0.120354f, 0.115272f, 0.110164f, 0.105031f, 0.099874f, 0.094695f, 0.089499f, 0.084289f, 0.079073f, 0.073859f, 0.068659f, 0.063488f, 0.058367f, 0.053324f, 0.048392f, 0.043618f, 0.039050f, 0.034931f, 0.031409f, 0.028508f, 0.026250f, 0.024661f, 0.023770f, 0.023606f, 0.024202f, 0.025592f, 0.027814f, 0.030908f, 0.034916f, 0.039886f, 0.045581f, 0.051750f, 0.058329f, 0.065257f, 0.072489f, 0.079990f, 0.087731f, 0.095694f, 0.103863f, 0.112229f, 0.120785f, 0.129527f, 0.138453f, 0.147565f, 0.156863f, 0.166353f, 0.176037f, 0.185923f, 0.196018f, 0.206332f, 0.216877f, 0.227658f, 0.238686f, 0.249972f, 0.261534f, 0.273391f, 0.285546f, 0.298010f, 0.310820f, 0.323974f, 0.337475f, 0.351369f, 0.365627f, 0.380271f, 0.395289f, 0.410665f, 0.426373f, 0.442367f, 0.458592f, 0.474970f, 0.491426f, 0.507860f, 0.524203f, 0.540361f, 0.556275f, 0.571925f, 0.587206f, 0.602154f, 0.616760f, 0.631017f, 0.644924f };\n\t\t\tcustom_colormap_size = 256;\n\n\t\t\tstatic cv::Mat custom_colormap;\n\t\t\tstatic bool first = true;\n\n\t\t\tif (first) {\n\t\t\t\tcv::Mat red(custom_colormap_size, 1, CV_32FC1, (void*)r),\n\t\t\t\t\tgreen(custom_colormap_size, 1, CV_32FC1, (void*)g),\n\t\t\t\t\tblue(custom_colormap_size, 1, CV_32FC1, (void*)b);\n\n\t\t\t\tcv::merge(std::vector<cv::Mat>{red, green, blue}, custom_colormap);\n\t\t\t\tcv::resize(custom_colormap, custom_colormap, cv::Size(1, 256));\n\t\t\t\tcustom_colormap.convertTo(custom_colormap, CV_8UC3, 255.0);\n\n\t\t\t\tfirst = false;\n\t\t\t}\t\n\n\t\t\tVideoVolume<uchar, 3> out(l, w, h);\n\t\t\tfor (int f = 0; f < l; ++f) {\n\t\t\t\tcv::Mat ff = 4.0*(frame(f) - 128) + 128;\n\t\t\t\t//cv::applyColorMap(cvConvertMatTo<uchar, N>(ff, scaling), out.frame(f), custom_colormap);\n\t\t\t\tcvConvertMatTo<uchar, N>(ff, scaling).copyTo(out.frame(f));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tvoid temporalBlur(float scaling = 1.0f) {\n\t\t\tconst cv::Mat1f kernel = (scaling / 16.0f)*(cv::Mat1f(5, 1) << 1, 4, 6, 4, 1);\n\t\t\tcv::filter2D(mat, mat, -1, kernel, cv::Point(-1, -1), 0.0, cv::BORDER_DEFAULT);\n\t\t}\n\n\t\tvoid temporalBlurBoundaryConditions(float scaling, double left, double right) {\n\t\t\tconst cv::Mat1f kernel = (scaling / 16.0f)*(cv::Mat1f(5, 1) << 1, 4, 6, 4, 1);\n\n\t\t\tVideoVolume tmp(l, w, h);\n\t\t\tfor (int i = 0; i < h; ++i) {\n\t\t\t\tfor (int j = 0; j < w; ++j) {\n\t\t\t\t\tfor (int c = 0; c < N; ++c) {\t\t\t\t\t\n\t\t\t\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\t\t\t\tdouble res = 0;\n\t\t\t\t\t\t\tfor (int dt = -2; dt <= 2; ++dt) {\n\t\t\t\t\t\t\t\tint u = t + dt;\n\t\t\t\t\t\t\t\tdouble w = kernel(2 + dt), val;\n\t\t\t\t\t\t\t\tif (u < 0) {\n\t\t\t\t\t\t\t\t\tval = left;\n\t\t\t\t\t\t\t\t} else if (u >= l) {\n\t\t\t\t\t\t\t\t\tval = right;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tval = valueAt(u, i, j, c);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tres += w * val;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttmp.valueAt(t, i, j, c) = cv::saturate_cast<T>(res) ;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t\tstd::swap(mat, tmp.mat);\n\t\t}\n\n\t\tvoid extendedTemporalBlur() {\n\t\t\t//const cv::Mat kernel = (1 / 5.0f)*(cv::Mat_<float>(5, 1) << 1, 1, 1, 1, 1);\n\t\t\t//cv::filter2D(mat, mat, -1, kernel, cv::Point(-1, -1), cv::BORDER_REPLICATE);\n\t\t\tcv::boxFilter(mat, mat, -1, cv::Size(1, 25), cv::Point(-1, -1), true, cv::BORDER_REPLICATE);\n\t\t}\n\n\t\tVideoVolume pyrDownSpacial() const {\n\t\t\tVideoVolume out(l, (w + 1) / 2, (h + 1) / 2);\n\t\t\tfor (int f = 0; f < l; ++f) {\n\t\t\t\tcv::pyrDown(frame(f), out.frame(f));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume pyrDownTemporalModif() {\n\t\t\ttemporalBlur();\n\t\t\tVideoVolume out((l + 1) / 2, w, h);\n\t\t\tfor (int f = 0; f < out.l; ++f) {\n\t\t\t\tframe(2 * f).copyTo(out.frame(f));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume pyrDownTemporal() const {\n\t\t\tVideoVolume tmp(mat.clone(), w, h);\n\t\t\treturn tmp.pyrDownTemporalModif();\n\t\t}\n\n\t\tVideoVolume pyrDownTemporalBox() const {\n\t\t\tVideoVolume tmp(l, w, h);\n\t\t\tconst cv::Mat kernel = (1.0 / 16.0f)*(cv::Mat_<float>(5, 1) << 1, 4, 6, 4, 1);\n\t\t\tcv::filter2D(mat, tmp.mat, -1, kernel, cv::Point(-1, -1), 0.0, cv::BORDER_REPLICATE);\n\t\t\t//cv::boxFilter(mat, tmp.mat, -1, cv::Size(1, 5), { -1,-1 }, true, cv::BORDER_REPLICATE);\n\t\t\tVideoVolume out((l + 1) / 2, w, h);\n\t\t\tcv::resize(tmp.mat, out.mat, out.mat.size(), 0, 0, cv::INTER_LINEAR);\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume extendedDownScaleTemporal() const {\n\t\t\tVideoVolume tmp(mat.clone(), w, h);\n\t\t\ttmp.extendedTemporalBlur();\n\t\t\tVideoVolume out((l + 1) / 2, w, h);\n\t\t\tfor (int f = 0; f < out.l; ++f) {\n\t\t\t\ttmp.frame(2 * f).copyTo(out.frame(f));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume pyrDown() const {\n\t\t\treturn pyrDownSpacial().pyrDownTemporalModif();\n\t\t}\n\n\t\tVideoVolume pyrUpSpacial(int _w, int _h) const {\n\t\t\tVideoVolume out(l, _w, _h);\n\t\t\tfor (int f = 0; f < l; ++f) {\n\t\t\t\tcv::pyrUp(frame(f), out.frame(f), cv::Size(_w, _h));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume pyrUpTemporal(int _l) const {\n\t\t\tVideoVolume out(_l, w, h, 0);\n\t\t\t//for (int f = 0; f < l; ++f) {\n\t\t\t//\tframe(f).copyTo(out.frame(2 * f));\n\t\t\t//}\n\t\t\t//out.temporalBlur(2.0f);\n\t\t\tcv::resize(mat, out.mat, out.mat.size(), 0, 0, cv::INTER_LINEAR);\n\t\t\tout.temporalBlur(1.0f);\n\n\t\t\treturn out;\n\t\t}\n\t\tVideoVolume pyrUpTemporalBoundaryConditions(int _l, double left, double right) const {\n\t\t\tVideoVolume out(_l, w, h, 0);\n\t\t\tcv::resize(mat, out.mat, out.mat.size(), 0, 0, cv::INTER_LINEAR);\n\t\t\tout.temporalBlurBoundaryConditions(1.0f, left, right);\n\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume pyrDownBoundaryConditions(double left, double right) const {\n\t\t\tVideoVolume tmp = clone();\n\t\t\ttmp.temporalBlurBoundaryConditions(1.0f, left, right);\n\t\t\tVideoVolume out((l + 1) / 2, w, h);\n\t\t\tcv::resize(tmp.mat, out.mat, out.mat.size(), 0, 0, cv::INTER_LINEAR);\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume pyrUp(int _l, int _w, int _h) const {\n\t\t\treturn pyrUpTemporal(_l).pyrUpSpacial(_w, _h);\n\t\t}\n\n\t\tvoid play(int delay = 30, const sibr::Vector2i & res = { -1,-1 }, double scale = 1.0) const {\n\t\t\tbool playing = true;\n\t\t\tint t = 0;\n\n\t\t\tconst std::string win_name = \"playing\";\n\t\t\tauto disp_frame = [&] {\n\t\t\t\tif ((res.array() >= 0).all()) {\n\t\t\t\t\tcv::Mat m;\n\t\t\t\t\tcv::resize(cvConvertMatTo<uchar, 3>(frame(t), scale), m, cv::Size(res[0],res[1]), 0,0, cv::INTER_NEAREST);\n\t\t\t\t\tcv::imshow(win_name, m);\n\t\t\t\t} else {\n\t\t\t\t\tcv::imshow(win_name, cvConvertMatTo<uchar, 3>(frame(t), scale));\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\tauto true_cb = [&](int new_t) {\n\t\t\t\tt = new_t;\n\t\t\t\tdisp_frame();\n\t\t\t};\n\t\t\tauto cb_wrapper = [](int new_t, void * arg) { (*static_cast<decltype(true_cb)*>(arg))(new_t); };\n\n\t\t\twhile (playing) {\t\t\t\t\t\n\t\t\t\tdisp_frame(); \n\t\t\t\tcv::createTrackbar(\"timestamp\", win_name, &t, l - 1, cb_wrapper, &true_cb);\t\n\t\t\t\tplaying = (cv::waitKey(delay) != 27);\n\t\t\t\tt = (t + 1) % l;\n\t\t\t}\n\n\t\t\tcv::destroyWindow(win_name);\n\t\t}\n\n\t\tvoid playStd(double scale = 1.0) const {\n\t\t\tplay(30, { 800,600 }, scale);\n\t\t}\n\n\t\tvoid saveToVideoFile(const std::string & filepath, double framerate = 30.0) const {\n\t\t\tPath file = filepath;\n\t\t\tmakeDirectory(file.parent_path().string());\n\n\t\t\tsibr::FFVideoEncoder output(filepath, framerate, { w,h });\n\t\t\tfor (int f = 0; f < l; ++f) {\n\t\t\t\toutput << sibr::cvConvertMatTo<uchar,3>(frame(f));\n\t\t\t}\n\t\t\toutput.close();\n\t\t}\n\n\t\tcv::Mat_<CVpixel> frame(int t) {\n\t\t\treturn mat.row(t).reshape(N, h);\n\t\t}\n\t\tconst cv::Mat_<CVpixel> frame(int t) const {\n\t\t\treturn mat.row(t).reshape(N, h);\n\t\t}\n\n\t\tcv::Mat_<T> time_sequence(int i, int j, int c = 0) const {\n\t\t\treturn mat.col(N*(w*i + j) + c);\n\t\t}\n\t\tcv::Mat_<CVpixel> time_sequence_pixels(int i, int j) const {\n\t\t\treturn mat.colRange(N*(w*i + j), N*(w*i + j + 1)).reshape(N, 0);\n\t\t}\n\n\t\tVideoVolume time_sequence_volume(int i, int j) const {\n\t\t\treturn VideoVolume(time_sequence_pixels(i,j).clone(), 1, 1);\n\t\t}\n\n\t\tcv::Mat video_line(int i) const {\n\t\t\treturn mat.colRange(N*w*i, N*w*(i + 1));\n\t\t}\n\n\t\tVideoVolume subVolumeRef(int t_start, int t_end) {\n\t\t\treturn VideoVolume(mat.rowRange(t_start, t_end), w, h);\n\t\t}\n\n\t\tVideoVolume subVolume(int t_start, int t_end) const {\n\t\t\treturn VideoVolume(mat.rowRange(t_start, t_end).clone(), w, h);\n\t\t}\n\n\t\tVideoVolume subVolumeSpatial(int x, int y, int w, int h) const {\n\t\t\tVideoVolume out(l, w, h);\n\t\t\tcv::Rect rec(x, y, w, h);\n#pragma omp parallel for\n\t\t\tfor (int t = 0; t < l; ++t) {\n\t\t\t\tframe(t)(rec).copyTo(out.frame(t));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tVideoVolume subVolume_i(int i_start, int i_end) const {\n\t\t\treturn VideoVolume(mat.colRange(N*w*i_start, N*w*i_end), w, i_end - i_start);\n\t\t}\n\n\t\tVideoVolume subVolume_i_copy(int i_start, int i_end) const {\n\t\t\treturn VideoVolume(mat.colRange(N*w*i_start, N*w*i_end).clone(), w, i_end - i_start);\n\t\t}\n\n\t\tVideoVolume duplicateTime(int num) const {\n\t\t\tVideoVolume out(l*num, w, h);\n\t\t\tfor (int i = 0; i < num; ++i) {\n\t\t\t\tmat.copyTo(out.mat.rowRange(l*i, l*(i + 1)));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tcv::Mat_<CVpixel> spatialSlice(int i_start, int j_start, int i_end, int j_end) {\n\t\t\tVector2i start = { i_start, j_start }, end = { i_end, j_end };\n\t\t\tVector2i diff = end - start;\n\t\t\tint num = diff.cwiseAbs().maxCoeff();\n\t\t\t\n\t\t\tcv::Mat_<CVpixel> out(l, num + 1);\n\t\t\tfor (int s = 0; s <= num; ++s) {\n\t\t\t\tVector2i p = (start.cast<float>() + (s / (float)num)*diff.cast<float>()).cast<int>();\n\n\t\t\t\ttime_sequence_pixels(p[0], p[1]).copyTo(out.col(s));\n\t\t\t\t//for (int c = 0; c < N; ++c) {\n\t\t\t\t//\tout.col(c + i * N) = time_sequence(p[0], p[1], c);\n\t\t\t\t//}\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tcv::Mat_<CVpixel> median_frame() const {\n\t\t\tcv::Mat_<CVpixel> out_median(h, w);\n\n#pragma omp parallel for\n\t\t\tfor (int i = 0; i < h; ++i) {\n\t\t\t\tfor (int j = 0; j < w; ++j) {\t\t\t\t\n\t\t\t\t\tfor (int c = 0; c < N; ++c) {\n\t\t\t\t\t\tstd::vector<T> vals_vec;\n\t\t\t\t\t\ttime_sequence(i, j, c).copyTo(vals_vec);\n\t\t\t\t\t\tstd::nth_element(vals_vec.begin(), vals_vec.begin() + vals_vec.size() / 2, vals_vec.end());\n\t\t\t\t\t\tCV_Assign<T, N>::assignValue(c, vals_vec[vals_vec.size() / 2], out_median(i, j));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out_median;\n\t\t}\n\n\t\tT & valueAt(int t, int i, int j, int c = 0) {\n\t\t\treturn mat(t, N*(w*i + j) + c);\n\t\t}\n\t\tconst T & valueAt(int t, int i, int j, int c = 0) const {\n\t\t\treturn mat(t, N*(w*i + j) + c);\n\t\t}\n\n\t\tCVpixel & pixelAt(int t, int i, int j) {\n\t\t\treturn frame(t)(i, j);\n\t\t}\n\t\tconst CVpixel & pixelAt(int t, int i, int j) const {\n\t\t\treturn frame(t)(i, j);\n\t\t}\n\n\t\tbool isValid() const {\n\t\t\treturn !mat.empty();\n\t\t}\n\n\t\tvoid cout() const {\n\t\t\tstd::cout << l << \" x \" << w <<  \" x \" << h << std::endl;\n\t\t}\n\n\t\tVideoVolume spatialRescale(float f) const {\n\t\t\tVector2i size = (f * Vector2f(w, h) + Vector2f(0.5f, 0.5f)).cast<int>();\n\n\t\t\tVideoVolume out(l, size[0], size[1]);\n\t\t\tfor (int f = 0; f < l; ++f) {\n\t\t\t\tcv::resize(frame(f), out.frame(f), cv::Size(size[0], size[1]), 0, 0, cv::INTER_LINEAR);\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\n\t};\n\n\tSIBR_VIDEO_EXPORT Volume3u loadVideoVolume(const std::string & filepath);\n\tSIBR_VIDEO_EXPORT Volume3u loadVideoVolume(sibr::Video & vid);\n\t\n\ttemplate<typename T, uint N, uint M> \n\tVideoVolume<T, N + M>  concatVolumesChannels(const sibr::VideoVolume<T, N> & A, const sibr::VideoVolume<T, M> & B) {\n\t\tVideoVolume<T, N + M> out(A.l, A.w, A.h);\n\n#pragma omp parallel for\n\t\tfor (int t = 0; t < A.l; ++t) {\n\t\t\tstd::vector<cv::Mat> A_cs, B_cs;\n\t\t\tcv::split(A.frame(t), A_cs);\n\t\t\tcv::split(B.frame(t), B_cs);\n\t\t\tA_cs.insert(A_cs.end(), B_cs.begin(), B_cs.end());\n\t\t\tcv::merge(A_cs, out.frame(t));\n\t\t}\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT uint optimal_num_levels(uint length);\n\n\ttemplate<uint M>\n\tstd::vector<sibr::VideoVolume<uchar, M>> extendedBlurPyramid(const sibr::VideoVolume<uchar, M> & vid, uint num_levels = 0) {\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::VideoVolume<uchar, M>> out(1, vid);\n\t\tfor (uint i = 1; i < num_levels; ++i) {\n\t\t\tout.push_back(out.back().extendedDownScaleTemporal());\n\t\t}\n\t\treturn out;\n\t}\n\n\ttemplate<uint M>\n\tstd::vector<sibr::VideoVolume<uchar, M>> gaussianPyramid(const sibr::VideoVolume<uchar, M> & vid, uint num_levels = 0) {\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::VideoVolume<uchar, M>> out(1, vid);\n\t\tfor (uint i = 1; i < num_levels; ++i) {\n\t\t\tout.push_back(out.back().pyrDown());\n\t\t}\n\t\treturn out;\n\t}\n\n\ttemplate<typename T, uint M>\n\tstd::vector<sibr::VideoVolume<T, M>> gaussianPyramidTemporal (const sibr::VideoVolume<T, M> & vid, uint num_levels = 0) {\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::VideoVolume<T, M>> out(1, vid);\n\t\tfor (uint i = 1; i < num_levels; ++i) {\n\t\t\tout.push_back(out.back().pyrDownTemporal());\n\t\t}\n\t\treturn out;\n\t}\n\n\ttemplate<typename T, uint M>\n\tstd::vector<sibr::VideoVolume<T, M>> gaussianPyramidTemporalBox(const sibr::VideoVolume<T, M> & vid, uint num_levels = 0) {\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<sibr::VideoVolume<T, M>> out(1, vid);\n\t\tfor (uint i = 1; i < num_levels; ++i) {\n\t\t\tout.push_back(out.back().pyrDownTemporalBox());\n\t\t}\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT std::vector<sibr::Volume3u> laplacianPyramid(const sibr::Volume3u & vid, uint num_levels = 0);\n\t//SIBR_VIDEO_EXPORT std::vector<sibr::Volume3u> laplacianPyramidTemporal(const sibr::Volume3u & vid, uint num_levels = 0);\n\tSIBR_VIDEO_EXPORT std::vector<sibr::Volume3u> laplacianPyramidTemporalDouble(const sibr::Volume3u & vid, uint num_levels = 0);\n\n\n\ttemplate< typename U, typename T = U>\n\tstd::vector<VideoVolume<T,3>> laplacianPyramidTemporal(const VideoVolume<U, 3>& vid, uint num_levels = 0)\n\t{\n\t\tif (num_levels == 0) {\n\t\t\tnum_levels = optimal_num_levels(vid.l);\n\t\t}\n\n\t\tstd::vector<VideoVolume<T, 3>> out;\n\n\t\tsibr::Volume3f current_v = vid.template convertTo<float>(), down, up;\n\t\tfor (int i = 0; i < (int)num_levels - 1; ++i) {\n\t\t\t//std::cout << i << \" \" << current_v.l << std::endl;\n\t\t\tdown = current_v.pyrDownTemporal();\n\t\t\tup = down.pyrUpTemporal(current_v.l);\n\t\t\t//current_v.play(30, { 1200,800 });\n\t\t\t//up.play(30, { 1200,800 });\n\t\t\tcurrent_v.substract(up);\n\t\t\tcurrent_v.shift(128);\n\t\t\tout.push_back(current_v.convertTo<T>());\n\t\t\tstd::swap(current_v, down);\n\t\t}\n\t\tout.push_back(current_v.convertTo<T>());\n\t\treturn out;\n\t}\n\n\tSIBR_VIDEO_EXPORT sibr::Volume3u collapseLaplacianPyramid(const std::vector<sibr::Volume3u> & pyr, double shift = 0);\n\n\ttemplate<typename T>\n\tsibr::VideoVolume<T,3> collapseLaplacianPyramidTemporal(const std::vector<sibr::VideoVolume<T, 3>>& pyr, double shift,\n\t\tbool debug = false)\n\t{\n\t\tsibr::Volume3f v = (VideoVolume<T, 3>) pyr.back().convertTo();\n\t\tfor (int i = (int)pyr.size() - 2; i >= 0; --i) {\n\t\t\tif (debug) {\n\t\t\t\tv.play();\n\t\t\t}\n\t\t\tv = v.pyrUpTemporal(pyr[i].l);\n\t\t\tif (debug) {\n\t\t\t\tv.play();\n\t\t\t}\n\t\t\tv.add(pyr[i]);\n\t\t\tif (shift != 0) {\n\t\t\t\tv.shift(shift);\n\t\t\t\tif (debug) {\n\t\t\t\t\tv.play();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn v.convertTo<T>();\n\t}\n\n\t//SIBR_VIDEO_EXPORT sibr::Volume3u collapseLaplacianPyramidTemporal(const std::vector<sibr::Volume3u> & pyr, double shift = 0);\n\n\t//SIBR_VIDEO_EXPORT sibr::Volume3u laplacianBlendingTemporal(const sibr::Volume3u & vA, const sibr::Volume3u & vB, std::vector<sibr::Volume1u> & pyrM);\n\tSIBR_VIDEO_EXPORT sibr::Volume3u laplacianBlending(const sibr::Volume3u & vA, const sibr::Volume3u & vB, std::vector<sibr::Volume1u> & pyrM);\n\n\ttemplate<typename T_V, typename T_M>\n\tsibr::VideoVolume<T_V, 3> laplacianBlendingTemporal(\n\t\tconst sibr::VideoVolume<T_V,3> & vA,\n\t\tconst sibr::VideoVolume<T_V, 3> & vB,\n\t\tstd::vector<sibr::VideoVolume<T_M, 1>> & pyrM)\n\t{\n\t\tuint num_levels = (uint)pyrM.size();\n\n\t\tauto pyrA = laplacianPyramidTemporal(vA, num_levels);\n\n\t\tfor (const auto & l : pyrA) {\n\t\t\t//l.play();\n\t\t}\n\n\t\tauto pyrB = laplacianPyramidTemporal(vB, num_levels);\n\n\t\t//auto pyrM = gaussianPyramidTemporal(vM);\n\n\t\tfor (int i = (int)pyrA.size() - 1; i >= 0; --i) {\n\t\t\t//pyrA[i].playStd();\n\t\t\t//pyrM[i].playStd();\n\t\t\tpyrA[i].applyMaskInPlace(pyrM[i]);\n\t\t\t//pyrA[i].playStd();\n\t\t\tpyrM[i].toggle();\n\t\t\t//pyrM[i].playStd();\n\t\t\t//pyrB[i].playStd();\n\t\t\tpyrB[i].applyMaskInPlace(pyrM[i]);\n\t\t\t//pyrB[i].playStd();\n\t\t\tpyrA[i].add(pyrB[i]);\n\t\t\t//pyrA[i].playStd();\n\t\t}\n\n\t\treturn collapseLaplacianPyramidTemporal(pyrA, -128);\n\t}\n\n\ttemplate<typename T, uint N, typename Pix = typename VideoVolume<float, N>::CVpixel>\n\tcv::Mat_<Pix> totalVariation(const VideoVolume<T, N> & v) {\n\t\tcv::Mat_<Pix> total_vars(v.h, v.w);\n\n#pragma omp parallel for\n\t\tfor (int i = 0; i < v.h; ++i) {\n\t\t\tfor (int j = 0; j < v.w; ++j) {\n\t\t\t\tfor (int c = 0; c < N; ++c) {\n\t\t\t\t\tdouble total_var = 0;\n\t\t\t\t\tauto seq = v.time_sequence(i, j, c).clone();\n\t\t\t\t\tfor (int t = 0; t < v.l - 1; ++t) {\n\t\t\t\t\t\ttotal_var += std::abs((double)seq(t) - (double)seq(t + 1));\n\t\t\t\t\t}\n\t\t\t\t\tCV_Assign<float, N>::assignValue(c, (float)total_var, total_vars(i, j));\n\t\t\t\t}\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\treturn total_vars;\n\t}\n\n\tstruct SIBR_VIDEO_EXPORT PyramidLayer {\n\t\tPyramidLayer() {}\n\t\tPyramidLayer(int _w, int _h, int _l, int cv_type = CV_32FC1) : l(_l), w(_w), h(_h) {\n\t\t\tvolume = cv::Mat(l, 3 * w*h, cv_type);\n\t\t}\n\t\tPyramidLayer(const cv::Mat & _volume, int _w, int _h) : w(_w), h(_h), l(_volume.rows) {\n\t\t\t_volume.convertTo(volume, CV_32FC1);\n\t\t}\n\n\t\tPyramidLayer operator+(const PyramidLayer & other);\n\t\tPyramidLayer operator-(const PyramidLayer & other);\n\n\t\tPyramidLayer clone() const {\n\t\t\tPyramidLayer out = *this;\n\t\t\tvolume.copyTo(out.volume);\n\t\t\treturn out;\n\t\t}\n\n\t\tcv::Mat getRGB(int frame, bool centered = false);\n\n\t\tvoid saveToVideoFile(const std::string & filename, double framerate);\n\n\t\tvoid copySizeFrom(const PyramidLayer & other) {\n\t\t\th = other.h;\n\t\t\tw = other.w;\n\t\t\tl = other.l;\n\t\t}\n\n\t\tvoid cout() const {\n\t\t\tstd::cout << l << \" \" << w << \" \" << h << std::endl;\n\t\t}\n\n\t\tvoid show(int s = 50) const;\n\t\tstatic void show(PyramidLayer A, PyramidLayer B, int s = 50);\n\t\tstatic void show(PyramidLayer A, PyramidLayer B, PyramidLayer C, int s = 50);\n\t\tstatic void show(PyramidLayer A, PyramidLayer B, PyramidLayer C, PyramidLayer D, int s = 50, bool centered = false);\n\t\tstatic void showDiff(PyramidLayer A, PyramidLayer B, int s = 50);\n\n\t\tcv::Mat volume;\n\t\tint w, h, l;\n\t};\n\n\n\tstruct SIBR_VIDEO_EXPORT PyramidParameters {\n\t\tPyramidParameters(int nlevels = 5, int temporal = 3, int spatial = 2, bool spatial_ds = true) :\n\t\t\tnum_levels(nlevels), temporal_radius(temporal), spatial_radius(spatial), splacialDS(spatial_ds) {}\n\t\tint num_levels; \n\t\tint temporal_radius;\n\t\tint spatial_radius;\n\t\tbool splacialDS = true;\n\t};\n\n\tSIBR_VIDEO_EXPORT PyramidLayer blur(const PyramidLayer & layer, const PyramidParameters &  params );\n\tSIBR_VIDEO_EXPORT PyramidLayer temporalBlur(const PyramidLayer & volume, const PyramidParameters & params, float scaling = 1);\n\tSIBR_VIDEO_EXPORT void temporalBlurInPlace(PyramidLayer & volume, const PyramidParameters & params, float scaling = 1);\n\n\tSIBR_VIDEO_EXPORT PyramidLayer decimate(const PyramidLayer & layer, const PyramidParameters &  params);\n\tSIBR_VIDEO_EXPORT PyramidLayer upscale(const PyramidLayer & layerUp, const PyramidLayer & layerDown, const PyramidParameters &  params);\n\tSIBR_VIDEO_EXPORT PyramidLayer downscale(const PyramidLayer & layer, const PyramidParameters &  params);\n\n\tSIBR_VIDEO_EXPORT cv::Mat slice(const PyramidLayer & layer, int i, int j, bool vertical = true, bool center = false);\n\n\t\n\tclass SIBR_VIDEO_EXPORT VideoGaussianPyramid {\n\tpublic:\n\t\tPyramidParameters params;\n\t\tstd::vector<PyramidLayer> layers;\n\n\t};\n\n\tclass SIBR_VIDEO_EXPORT VideoLaplacianPyramid {\n\tpublic:\n\n\tpublic:\n\t\tVideoLaplacianPyramid() {}\n\n\tpublic:\n\n\t\tPyramidParameters params;\n\n\t\tPyramidLayer collapse() const;\n\n\t\tstd::vector<PyramidLayer> layers;\n\t\t\n\t};\n\n\t\n\tSIBR_VIDEO_EXPORT VideoGaussianPyramid buildVideoGaussianPyramid(sibr::Video & vid, int nLevels, const PyramidParameters & params = {}, bool show = false);\n\tSIBR_VIDEO_EXPORT VideoGaussianPyramid buildVideoGaussianPyramid(const cv::Mat & volume, int w, int h, int nLevels, const PyramidParameters & params = {}, bool show = false);\n\n\tSIBR_VIDEO_EXPORT VideoLaplacianPyramid buildVideoLaplacianPyramid(PyramidLayer vid, int nLevels, const PyramidParameters & params = {}, bool show = false);\n\tSIBR_VIDEO_EXPORT VideoLaplacianPyramid buildVideoLaplacianPyramid(sibr::Video & vid, int nLevels, const PyramidParameters & params = {}, bool show = false);\n\n\tSIBR_VIDEO_EXPORT VideoLaplacianPyramid buildVideoLaplacianPyramidFullyReduced(PyramidLayer vid, int nLevels, const PyramidParameters & params = {}, bool show = false);\n\tSIBR_VIDEO_EXPORT void convertReducedVideoPyramidTo128(VideoLaplacianPyramid & vid);\n\n\tSIBR_VIDEO_EXPORT PyramidLayer videoLaplacianBlending(sibr::Video & vidA, sibr::Video & vidB, PyramidLayer mask_volume );\n\tSIBR_VIDEO_EXPORT PyramidLayer videoLaplacianBlending(PyramidLayer vidA, PyramidLayer vidB, PyramidLayer mask_volume, PyramidParameters params = {}, bool show = false);\n\n\tstruct SIBR_VIDEO_EXPORT ContribData {\n\t\tPyramidLayer contrib, mask, partA, partB;\n\t};\n\n\tstruct SIBR_VIDEO_EXPORT FullContribData {\n\t\tContribData scaled;\n\t\tContribData notScaled;\n\t\tPyramidLayer result, inputA, inputB;\n\t};\n\n\tSIBR_VIDEO_EXPORT std::vector<FullContribData> videoLaplacianBlendingContrib(PyramidLayer vidA, PyramidLayer vidB, PyramidLayer mask_volume, PyramidParameters params = {});\n\n\tSIBR_VIDEO_EXPORT void videoLaplacianBlendingDebug(PyramidLayer vidA, PyramidLayer vidB, PyramidLayer mask_volume, PyramidParameters params = {});\n\n\ttemplate<typename T, uint N> \n\tclass Histogram {\n\t\tusing Value = Vector<T, N>;\n\t\tusing Indice = Vector<uint, N>;\n\t\tusing Range = Vector<double, N>;\n\n\tpublic:\n\t\tHistogram(const Value & _min, const Value & _max, uint _numBins = 100)\n\t\t\t: min(_min.template cast<double>()), max(_max.template cast<double>()), numBins(_numBins) {\n\t\t\tRange diff = max - min;\n\t\t\tscaling = numBins * diff.cwiseInverse();\n\t\t\tbin_range = diff / numBins;\n\t\t}\n\n\t\tIndice whatBin(const Value & value) {\n\t\t\tIndice bin;\n\t\t\tfor (int c = 0; c < N; ++c) {\n\t\t\t\tbin[c] = sibr::clamp((int)(scaling[c]*(value[c] - min[c])), 0, (int)numBins - 1);\n\t\t\t}\n\t\t\treturn bin;\n\t\t}\n\n\t\tvoid addValue(const Value & value) {\n\t\t\t++bins[whatBin(value)];\n\t\t}\n\t\tvoid addValues(const std::vector<Value> & values) {\n\t\t\tfor (const Value & v : values) {\n\t\t\t\taddValue(v);\n\t\t\t}\n\t\t}\n\n\t\tIndice getModeIndice() const {\n\t\t\tIndice mode;\n\t\t\tuint mode_size = 0;\n\t\t\tfor (const auto & key_val : bins) {\n\t\t\t\tif (key_val.second > mode_size) {\n\t\t\t\t\tmode_size = key_val.second;\n\t\t\t\t\tmode = key_val.first;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mode;\n\t\t}\n\n\t\tValue getBinMiddle(const Indice & bin) const {\n\t\t\tValue out;\n\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\tout[c] = static_cast<T>(min[c] + bin_range[c] * (bin[c] + 0.5));\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\tprotected:\n\t\tstd::map<Indice, uint> bins;\n\t\tRange max, min, scaling, bin_range;\n\t\tuint numBins = 50;\n\t};\n\n\ttemplate<typename T> \n\tclass Histogram<T, 1> {\n\t\tSIBR_CLASS_PTR(Histogram);\n\n\tpublic:\n\t\tHistogram(double _min, double _max, int _numBins = 100) : min(_min), max(_max), numBins(_numBins), bins(_numBins, 0) {\n\t\t\tscaling = numBins / (max - min);\n\t\t\tbin_range = (max - min) / numBins;\n\t\t}\n\n\t\tbool whatBin(T value, uint & bin) {\n\t\t\tint t = (int)(scaling * (value - min));\n\t\t\tif (t >= 0 && t <= ((int)numBins - 1)) {\n\t\t\t\tbin = t;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tvoid addValue(T value) {\n\t\t\tuint bin;\n\t\t\tif (whatBin(value, bin)) {\n\t\t\t\t++bins[bin];\n\t\t\t}\n\t\t}\n\n\t\tvoid addValues(const std::vector<T> & values) {\n\t\t\tfor (const T & v : values) {\n\t\t\t\taddValue(v);\n\t\t\t}\n\t\t}\n\n\t\tuint getModeIndice() const {\n\t\t\tuint mode, mode_size = 0;\n\t\t\tfor (const auto & [key, val] : bins) {\n\t\t\t\tif (val > mode_size) {\n\t\t\t\t\tmode_size = val;\n\t\t\t\t\tmode = key;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mode;\n\t\t}\n\n\t\tT getBinMiddle(uint bin) const {\n\t\t\treturn static_cast<T>(min + bin_range * (bin + 0.5));\n\t\t}\n\n\t\tstd::vector<float> normalized_values() const {\n\t\t\tfloat sum = 0;\n\t\t\tfor (uint b = 0; b < numBins; ++b) {\n\t\t\t\tsum += bins[b];\n\t\t\t}\n\t\t\tstd::vector<float> out(numBins);\n\t\t\tfor (uint b = 0; b < numBins; ++b) {\n\t\t\t\tout[b] = bins[b]/sum;\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\tprotected:\n\t\tstd::vector<uint> bins;\n\t\tdouble max, min, scaling, bin_range;\n\t\tuint numBins = 50;\n\t};\n\n\tusing Histo1f = Histogram<float, 1>;\n\tusing ColorHistogram = Histogram<uchar, 3>;\n\n\tstruct TimeHistogram {\n\n\t\tTimeHistogram(double _min, double _max, int _numBins) : numBins(_numBins), max(_max), min(_min) {\n\t\t\tassert(numBins > 0 && max > min);\n\t\t\tscaling = numBins / (max - min);\n\t\t\tbin_range = (max - min) / numBins;\n\t\t}\n\n\t\tvoid addValues(const std::vector<sibr::Vector3ub> & values) {\n\t\t\tfor (const sibr::Vector3ub & value : values) {\n\t\t\t\taddValue(value);\n\t\t\t}\n\t\t}\n\n\t\tsibr::Vector3ub getBinMiddle(const sibr::Vector3ub & bin) const {\n\t\t\tsibr::Vector3ub out;\n\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\tout[c] = sibr::clamp((int)(min + bin_range * (bin[c] + 0.5)), 0, 255);\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\n\t\tsibr::Vector3ub getHMode() const {\n\t\t\tsibr::Vector3ub mode;\n\t\t\tint mode_size = 0;\n\t\t\tfor (const auto & key_val : bins) {\n\t\t\t\tif (key_val.second > mode_size) {\n\t\t\t\t\tmode_size = key_val.second;\n\t\t\t\t\tmode = key_val.first;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mode;\n\t\t}\n\n\t\tvoid addValue(const sibr::Vector3ub & value) {\n\t\t\t++bins[whatBin(value)];\n\t\t}\n\n\t\tsibr::Vector3ub whatBin(const sibr::Vector3ub & value) {\n\t\t\tsibr::Vector3ub bin;\n\t\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\t\tbin[c] = sibr::clamp((int)(scaling*(value[c] - min)), 0, numBins - 1);\n\t\t\t}\n\t\t\treturn bin;\n\t\t}\n\n\t\tvoid computeSortedBins() {\n\t\t\tstd::vector<std::pair<int, sibr::Vector3ub>> all_bins;\n\t\t\tint num_elts = 0;\n\t\t\tfor (const auto & bin : bins) {\n\t\t\t\tnum_elts += bin.second;\n\t\t\t\tall_bins.push_back({ bin.second,bin.first });\n\t\t\t}\n\t\t\tstd::sort(all_bins.begin(), all_bins.end());\n\t\t\tfloat cdf = 0; \n\t\t\tfor (int i = (int)all_bins.size() - 1; i >= 0; --i) {\n\t\t\t\tfloat f = all_bins[i].first /(float)num_elts;\n\t\t\t\t//std::cout << \"(\" << all_bins[i].first << \", \" << (int)all_bins[i].second[0] << \"),\";\n\t\t\t\t\n\t\t\t\tsorted_bins[all_bins[i].second] = cdf;\n\t\t\t\tcdf = std::min(cdf + f, 1.0f);\n\t\t\t}\n\t\t\t//std::cout << std::endl;\n\t\t}\n\t\tstd::map<sibr::Vector3ub, int> bins;\n\t\tstd::map<sibr::Vector3ub, float> sorted_bins;\n\t\tdouble max, min, scaling, bin_range;\n\t\tint numBins = 50;\n\t};\n\n\tclass SIBR_VIDEO_EXPORT VideoUtils {\n\n\tpublic:\n\t\tstatic void simpleFlowViz(cv::VideoCapture & cap, float ratio);\n\t\tstatic void simpleFlowSave(cv::VideoCapture & cap, float ratio, std::function<std::string(int flow_id)> naming_f);\n\n\t\ttemplate<typename FunType, typename... OtherArgsTypes>\n\t\tstatic void loopAndDisplay(cv::VideoCapture & cap, float ratio, FunType f, const OtherArgsTypes &... args);\n\n\t\tstatic void deepFlowViz(cv::VideoCapture & cap, float ratio);\n\n\t\tstatic cv::Mat getGrey(const cv::Mat & mat);\n\n\t\tstatic cv::Mat getFlowViz(const cv::Mat & flow);\n\n\t\tstatic cv::Mat cropFromSize(const cv::Mat & mat, const sibr::Vector2i & size);\n\n\t\tstatic void getMeanVariance(cv::VideoCapture & cap, cv::Mat & outMean, cv::Mat & outVariance, const sibr::Vector2i & finalSize);\n\t\tstatic void getMeanVariance2(cv::VideoCapture & cap, cv::Mat & outMean, cv::Mat & outVariance, const sibr::Vector2i & finalSize, float starting_point_s = 0);\n\n\t\tstatic cv::Mat getMedian(sibr::Video & vid, float time_skiped_begin = 0, float time_skiped_end = 0);\n\t\tstatic cv::Mat3b getMedian(const std::string & path, float time_percentage_crop  = 0);\n\n\t\tstatic cv::Mat getBackgroundImage(sibr::Video & vid, int numBins = 50, float time_skip_begin = 0, float time_skip_end = 0);\n\t\tstatic cv::Mat getBackgroundImage(const cv::Mat volume, int w, int h, int numBins = 50);\n\t\tstatic void getBackGroundVideo(sibr::Video & vid, PyramidLayer & out_mask, PyramidLayer & out_video, cv::Mat & mask,\n\t\t\tconst sibr::ImageRGB & mean = {}, int threshold = 75, int numBins = 50, float time_skip_begin = 0, float time_skip_end = 0);\n\n\t\tstatic sibr::Volume1u getBackgroundVolume(const sibr::Volume3u & volume, int threshold = 75, int numBins = 150);\n\t\tstatic sibr::Volume1f getBackgroundVolumeF(const sibr::Volume3u & volume, int numBins = 150);\n\n\t\tstatic void computeSaveSimpleFlow(sibr::Video & vid, bool viz = false);\n\n\t\tstatic cv::Mat getTemporalSpatialRatio(sibr::Video & vid, PyramidLayer & out_ratio, const sibr::ImageRGB & spatial_ratio,\n\t\t\tint numBins = 50, float time_skip_begin = 0, float time_skip_end = 0);\n\n\t\tstatic cv::Mat getLaplacian(cv::Mat mat, int size = 3, bool smooth = false, bool absolute = false);\n\n\t\tstatic cv::Mat getCanny(cv::Mat mat);\n\n\n\t\tstatic void computeSaveVideoMaskF(Video & vid, int threshold, bool viz = false);\n\t\tstatic void computeSaveVideoMaskBlur(Video & vid, int time_window);\n\n\t\tstatic int rotationAngleFromMetadata(const std::string & videoPath);\n\n\t\tstatic void ECCtransform(cv::Mat matA, cv::Mat matB, cv::Mat & correctedB, cv::Mat & diff, int cvMotion);\n\n\t\tstatic void smallAlignmentVideo(sibr::Video & vid, const std::string & outputVidPath, bool viz = false);\n\t\tstatic void smallAlignmentVideo2(sibr::Video & vid, const std::string & outputVidPath, bool viz = false);\n\n\t\tstatic int codec_ffdshow;\n\t\tstatic int codec_OpenH264;\n\t\tstatic int codec_OpenH264_fallback;\n\n\tprotected:\n\t\tstatic cv::Mat applyFlow(const cv::Mat & prev, const cv::Mat & flow);\n\n\t\tstatic void simpleFlow(cv::VideoCapture & cap, float ratio,\n\t\t\tstd::function<bool(cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id)> f,\n\t\t\tstd::function<void(void)> end_function = []() {}\n\t\t);\n\n\t\tstatic void deepFlow(cv::VideoCapture & cap, float ratio,\n\t\t\tstd::function<bool(cv::Mat prev, cv::Mat next, cv::Mat flow, int flow_id)> f,\n\t\t\tstd::function<void(void)> end_function = []() {}\n\t\t);\n\t};\n\n\ttemplate<typename FunType, typename... OtherArgsTypes>\n\tvoid VideoUtils::loopAndDisplay(cv::VideoCapture & cap, float ratio, FunType f, const OtherArgsTypes &... args)\n\t{\n\t\tcv::Mat next;\n\t\tstatic_assert(std::is_same_v<decltype(f(next, args...)), cv::Mat>, \"FunType must return cv::Mat\");\n\n\t\tcap.set(cv::VideoCaptureProperties::CAP_PROP_POS_FRAMES, 0);\n\n\t\twhile (true) {\n\t\t\tcap >> next;\n\t\t\tif (next.empty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tauto size = cv::Size((int)(ratio*next.size().width), (int)(ratio*next.size().height));\n\t\t\tcv::resize(next, next, size);\n\t\t\tcv::imshow(\"imshow\", f(next, args...));\n\t\t\tif (cv::waitKey(10) == 27) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tcv::destroyAllWindows();\n\t}\n\n\t/** }@ */\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/video/sibr_video.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_video sibr_video\n\n\t\\brief Video loading, processing and display. \n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(sibr_view)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\" \"shaders/*.fp\" \"shaders/*.vp\" \"shaders/*.gp\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB INTERFACE_SOURCES \"interface/*.cpp\" \"interface/*.h\" )\nsource_group(\"Source Files\\\\interface\" FILES ${INTERFACE_SOURCES})\n\nfile(GLOB SOURCES\n\t\"*.cpp\" \"*.h\" \"*.hpp\"\n\t\"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\"  \"shaders/*.fp\" \"shaders/*.vp\" \"shaders/*.gp\"\n\t\"interface/*.cpp\" \"interface/*.h\"\n\t)\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(\n\t${Boost_INCLUDE_DIRS}\n\t${imgui_INCLUDE_DIRS}\n)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\timgui\n\tsibr_graphics\n\tsibr_assets\n\tsibr_raycaster\n\tsibr_scene\n\tsibr_video\n)\n\nadd_definitions( -DSIBR_VIEW_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${SIBR_FOLDER})\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS \"${SHADERS}\"\n\tRSC_FOLDER \"core\"\n    #COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/Config.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"core/graphics/Config.hpp\"\r\n\r\n\r\n//// Export Macro (used for creating DLLs) ////\r\n# ifdef SIBR_OS_WINDOWS\r\n#  ifdef SIBR_STATIC_VIEW_DEFINE\r\n#    define SIBR_VIEW_EXPORT\r\n#    define SIBR_NO_VIEW_EXPORT\r\n#  else\r\n#    ifndef SIBR_VIEW_EXPORT\r\n#      ifdef SIBR_VIEW_EXPORTS\r\n          /* We are building this library */\r\n#        define SIBR_VIEW_EXPORT __declspec(dllexport)\r\n#      else\r\n          /* We are using this library */\r\n#        define SIBR_VIEW_EXPORT __declspec(dllimport)\r\n#      endif\r\n#    endif\r\n#    ifndef SIBR_NO_EXPORT\r\n#      define SIBR_NO_EXPORT \r\n#    endif\r\n#  endif\r\n# else\r\n#  define SIBR_VIEW_EXPORT\r\n# endif\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/DatasetView.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"DatasetView.hpp\"\n\nnamespace sibr {\n\t\n\tDatasetView::DatasetView(const BasicIBRScene & scene, const Vector2u & defaultRenderingRes, const Vector2i & defaultViewRes)\n\t\t: MultiViewBase(defaultViewRes)\n\t{\n\t\tconst auto & input_cams = scene.cameras()->inputCameras();\n\t\tconst auto & input_images = scene.images()->inputImages();\n\n\t\tif (input_images.size() != input_cams.size()) {\n\t\t\tSIBR_ERR << \"cams not matching input images\";\n\n\t\t}\n\t\tconst std::string mmm_str = \"mesh\";\n\t\tMultiMeshManager::Ptr mmm(new MultiMeshManager(mmm_str));\n\t\tmmm->addMesh(\"proxy\", scene.proxies()->proxyPtr());\t\t\n\t\tmmm->getCameraHandler().fromCamera(*input_cams[0]);\n\t\tfor (int i = 0; i < (int)input_cams.size(); ++i) {\n\t\t\tcams.push_back(*input_cams[i]);\n\t\t}\n\n\t\tconst std::string grid_str = \"grid\";\n\t\tImagesGrid::Ptr grid(new ImagesGrid());\n\t\tgrid->addImageLayer(\"input images\", input_images);\n\n\t\taddSubView(meshSubViewStr, mmm, defaultRenderingRes);\n\t\taddSubView(gridSubViewStr, grid, defaultRenderingRes);\n\t}\n\n\tvoid DatasetView::onGui(Window & win)\n\t{\n\t}\n\n\tvoid DatasetView::onUpdate(Input & input)\n\t{\n\t\tMultiViewBase::onUpdate(input);\n\n\n\t\tInput meshInput = Input::subInput(input, getMeshView().viewport);\n\t\tif (meshInput.key().isActivated(Key::LeftControl) && meshInput.mouseButton().isActivated(Mouse::Right)) {\n\t\t\tRaycastingCamera cam = RaycastingCamera(getMMM()->getCameraHandler().getCamera());\n\t\t\tRay ray = cam.getRay(meshInput.mousePosition().cast<float>());\n\t\t\tauto hit = proxyData().raycaster->intersect(ray);\n\n\t\t\tif (hit.hitSomething()) {\n\t\t\t\tcurrentRepro.point3D = ray.at(hit.dist());\n\t\t\t\tcurrentRepro.active = true;\n\t\t\t\tcurrentRepro.repros.clear();\n\t\t\t\trepro(currentRepro);\n\t\t\t\tgetGrid()->addPixelsToHighlight(\"zinputRepro\", { }, { 1,0,0 }, 0.25f);\n\t\t\t}\n\t\t}\n\n\t\tInput gridInput = Input::subInput(input, getGridView().viewport);\n\t\tif (gridInput.key().isActivated(Key::LeftControl) && gridInput.mouseButton().isActivated(Mouse::Right)) {\n\t\t\tconst auto & pix = getGrid()->getCurrentPixel();\n\t\t\tif (pix) {\n\t\t\t\tRay ray = cams[pix.im].getRay(pix.pos.cast<float>());\n\t\t\t\tauto hit = proxyData().raycaster->intersect(ray);\n\t\t\t\tif (hit.hitSomething()) {\n\t\t\t\t\tcurrentRepro.point3D = ray.at(hit.dist());\n\t\t\t\t\tcurrentRepro.active = true;\n\t\t\t\t\tcurrentRepro.repros.clear();\n\t\t\t\t\trepro(currentRepro);\n\t\t\t\t\tgetGrid()->addPixelsToHighlight(\"zinputRepro\", { pix }, { 1,0,0 }, 0.25f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t}\n\n\tvoid DatasetView::onRender(Window & win)\n\t{\n\t\tif (currentRepro) {\n\t\t\tdisplayRepro(currentRepro);\n\t\t}\n\n\t\tMultiViewBase::onRender(win);\n\t}\n\n\tvoid DatasetView::repro(ReprojectionData & data)\n\t{\n\t\tconst Vector3f & pt = data.point3D;\n\t\tfor (int im = 0; im<(int)cams.size(); ++im) {\n\t\t\tconst auto & cam = cams[im];\n\t\t\tif (!cam.frustumTest(pt)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tVector3f pt2d = cam.projectImgSpaceInvertY(pt);\n\n\t\t\tif (data.occlusionTest) {\n\t\t\t\tfloat dist = (cam.position() - pt).norm();\n\t\t\t\tRay ray = Ray(pt, (cam.position() - pt).normalized());\n\t\t\t\tauto hit = proxyData().raycaster->intersect(ray, 0.01f);\n\t\t\t\tif (hit.hitSomething() && std::abs(hit.dist() - dist) / dist > 0.01f) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdata.repros.push_back(MVpixel(im, pt2d.xy().cast<int>()));\n\t\t}\n\t}\n\n\tvoid DatasetView::displayRepro(const ReprojectionData & data)\n\t{\n\t\tgetMMM()->addPoints(\"repro 3D point\", { data.point3D });\n\n\t\tMesh::Ptr reproLines(new Mesh());\n\t\tstd::vector<MVpixel> pixs;\n\t\tstd::vector<int> repro_imgs;\n\t\tfor (const auto & rep : data.repros) {\n\t\t\tconst auto & cam = cams[rep.im];\n\t\t\tMesh reproLine;\n\t\t\treproLine.vertices({ cam.position(), data.point3D });\n\t\t\treproLine.triangles({ 0,0,1 });\n\t\t\treproLines->merge(reproLine);\n\t\t\trepro_imgs.push_back(rep.im);\n\t\t}\n\n\t\tgetMMM()->addMeshAsLines(\"repro ines\", reproLines).setColor({ 1,0,1 });\n\t\tgetGrid()->addPixelsToHighlight(\"repros\", data.repros, { 0,0,1 }, 0.25f);\n\t\t//getGrid()->addImagesToHighlight(\"reproImgs\", repro_imgs, { 0,1,0 }, 0.1f);\n\t}\n\n\tMultiViewBase::BasicSubView & DatasetView::getMeshView()\n\t{\n\t\treturn _subViews[meshSubViewStr];\n\t}\n\n\tMultiViewBase::BasicSubView & DatasetView::getGridView()\n\t{\n\t\treturn _subViews[gridSubViewStr];\n\t}\n\n\tMultiMeshManager::Ptr DatasetView::getMMM()\n\t{\n\t\treturn std::static_pointer_cast<MultiMeshManager>(getMeshView().view);\n\t}\n\n\tImagesGrid::Ptr sibr::DatasetView::getGrid()\n\t{\n\t\treturn std::static_pointer_cast<ImagesGrid>(getGridView().view);\n\t}\n\n\tMeshData & DatasetView::proxyData()\n\t{\n\t\treturn getMMM()->getMeshData(\"proxy\");\n\t}\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/DatasetView.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"MultiViewManager.hpp\"\n#include \"SceneDebugView.hpp\"\n#include \"ImagesGrid.hpp\"\n#include \"core/scene/BasicIBRScene.hpp\"\n\nnamespace sibr {\n\n\t/** Visualize and explore a MVS dataset. \n\tAllow reprojections between one of the input images, scene geometry and other images.\n\t \\ingroup sibr_view\n\t */\n\tclass SIBR_VIEW_EXPORT DatasetView \n\t\t: public MultiViewBase\n\t{\n\t\tSIBR_CLASS_PTR(DatasetView);\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t * \\param scene the IBR scene\n\t\t * \\param defaultRenderingRes the mesh view rendering resolution\n\t\t * \\param defaultViewRes the window/view resolution\n\t\t */\n\t\tDatasetView(const BasicIBRScene & scene, const Vector2u & defaultRenderingRes = { 0,0 }, const Vector2i & defaultViewRes = { 800, 600 });\n\n\t\t/** Reprojection mode. */\n\t\tenum ReprojectionMode { NONE, IMAGE_TO_IMAGE, MESH_TO_IMAGE };\n\n\t\t/** Update the GUI. */\n\t\tvirtual void\tonGui(Window& win) override;\n\n\t\t/** Update state based on user input.\n\t\t *\\param input the view input\n\t\t */\n\t\tvirtual void\tonUpdate(Input& input) override;\n\n\t\t/** Perform rendering.\n\t\t *\\param win the destination window\n\t\t **/\n\t\tvirtual void\tonRender(Window& win) override;\n\n\tprotected:\n\n\t\t/** Contain data related to the reprojection of a point in input images. */\n\t\tstruct ReprojectionData {\n\n\t\t\t/** \\return true if point is active */\n\t\t\toperator bool() const { return active; }\n\n\t\t\tstd::vector<MVpixel> repros; ///< Store reprojected pixel positions.\n\t\t\tMVpixel image_input; ///< Initial selected position.\n\n\t\t\tVector3f point3D; ///< World space point.\n\t\t\tbool occlusionTest = true; ///< Should occlusion test be applied.\n\t\t\tbool active = false; ///< Is the point active.\n\t\t};\n\n\t\t/** populate reprojection information.\n\t\t\\param data the info to populate\n\t\t*/\n\t\tvoid repro(ReprojectionData & data);\n\t\t\n\t\t/** Visualize the reprojection information.\n\t\t\\param data the reprojection to display\n\t\t*/\n\t\tvoid displayRepro(const ReprojectionData & data);\n\n\t\t/** \\return the mesh subview. */\n\t\tBasicSubView & getMeshView();\n\n\t\t/** \\return the images subview. */\n\t\tBasicSubView & getGridView();\n\n\t\t/** \\return the mesh display manager. */\n\t\tMultiMeshManager::Ptr getMMM();\n\n\t\t/** \\return the image grid manager. */\n\t\tImagesGrid::Ptr getGrid();\n\n\t\t/** \\return the mesh display data. */\n\t\tMeshData & proxyData();\n\n\t\tstd::vector<RaycastingCamera> cams; ///< Input cameras.\n\t\tReprojectionData currentRepro; ///< Current selected reprojection.\n\t\tReprojectionMode reproMode = MESH_TO_IMAGE; ///< Current reprojection mode.\n\n\t\tconst std::string meshSubViewStr = \"dataset view - mesh\";\n\t\tconst std::string gridSubViewStr = \"grid\";\n\n\t\t\n\t};\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/FPSCamera.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include \"FPSCamera.hpp\"\r\n#include <boost/filesystem.hpp>\r\n#include \"core/graphics/Input.hpp\"\r\n#include \"core/graphics/Viewport.hpp\"\r\n#include \"core/graphics/Window.hpp\"\r\n#include \"core/view/UIShortcuts.hpp\"\r\n#include \"core/graphics/GUI.hpp\"\r\n\r\n\r\n# define IBRVIEW_CAMSPEED 1.f\r\n\r\nnamespace sibr {\r\n\r\n\tFPSCamera::FPSCamera(void) : _hasBeenInitialized(false) \r\n\t{ \r\n\t\tUIShortcuts::global().add(\"[FPS camera] j\", \"rotate camera -Y (look left)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] l\", \"rotate camera +Y (look right)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] i\", \"rotate camera +X (look up)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] k\", \"rotate camera -X (look down)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] u\", \"rotate camera +Z \");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] o\", \"rotate camera -Z \");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] w\", \"move camera -Z (move forward)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] s\", \"move camera +Z (move backward)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] a\", \"move camera -X (strafe left)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] d\", \"move camera +X (strafe right)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] q\", \"move camera -Y (move down)\");\r\n\t\tUIShortcuts::global().add(\"[FPS camera] e\", \"move camera +Y (move up)\");\r\n\t/*\r\n\t\t_speedFpsCam = 1.0f;\r\n\t\t_speedRotFpsCam = 1.0f;\r\n\t\t_useAcceleration = true; */\r\n\t\t_speedFpsCam = 0.5f;\r\n\t\t_speedRotFpsCam = 1.0f;\r\n\t\t_useAcceleration = false; \r\n\t}\r\n\r\n\tvoid FPSCamera::fromCamera( const sibr::InputCamera & cam)\r\n\t{\r\n\t\t_currentCamera = cam;\r\n\t\t_hasBeenInitialized = true;\r\n\t}\r\n\r\n\tvoid FPSCamera::update(const sibr::Input & input, float deltaTime) {\r\n\t\r\n\t\tif (!_hasBeenInitialized) { return; }\r\n\t\t// Read input and update camera.\r\n\t\tmoveUsingWASD(input, deltaTime);\r\n\t\tmoveUsingMousePan(input, deltaTime);\r\n\t}\r\n\r\n\tvoid FPSCamera::snap(const std::vector<InputCamera::Ptr> & cams){\r\n\t\tsibr::Vector3f sumDir(0.f, 0.f, 0.f);\r\n\t\tsibr::Vector3f sumUp(0.f, 0.f, 0.f);\r\n\t\tfor (const auto& cam: cams)\r\n\t\t{\r\n\t\t\tfloat dist = 1.0f/std::max(1e-6f,distance(_currentCamera.position(), cam->position()));\r\n\t\t\tsumDir += dist * cam->dir();\r\n\t\t\tsumUp  += dist * cam->up();\r\n\t\t}\r\n\t\tMatrix4f m = lookAt(Vector3f(0, 0, 0), sumDir, sumUp);\r\n\t\t_currentCamera.rotation(quatFromMatrix(m));\r\n\t}\r\n\r\n\tvoid FPSCamera::update(const sibr::Input & input, const float deltaTime, const Viewport & viewport)\r\n\t{\r\n\t\tupdate(input, deltaTime);\r\n\t}\r\n\r\n\tconst sibr::InputCamera & FPSCamera::getCamera( void ) const\r\n\t{\r\n\t\tif( !_hasBeenInitialized ){\r\n\t\t\tSIBR_ERR << \" FPS Camera : camera not initialized before use\" << std::endl\r\n\t\t\t\t<< \"\\t you should use either fromMesh(), fromCamera() or load() \" << std::endl;\r\n\t\t}\r\n\t\treturn _currentCamera;\r\n\t}\r\n\r\n\tvoid FPSCamera::setSpeed(const float speed, const float angular) {\r\n\t\t_speedFpsCam = speed;\r\n\t\tif(angular != 0.0f) {\r\n\t\t\t_speedRotFpsCam = angular;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid FPSCamera::setGoalAltitude(const float& goalAltitude) {\r\n\t\t_goalAltitude = goalAltitude;\r\n\t}\r\n\r\n\tvoid FPSCamera::onGUI(const std::string& suffix) {\r\n\t\tif(ImGui::Begin(suffix.c_str())) {\r\n\t\t\tImGui::PushScaledItemWidth(130);\r\n\t\t\tImGui::Checkbox(\"Acceleration\", &_useAcceleration);\r\n\t\t\tImGui::SameLine();\r\n\t\t\tif(!_useAcceleration) {\r\n\t\t\t\tImGui::InputFloat(\"Speed\", &_speedFpsCam, 0.1f, 0.5f);\r\n\t\t\t\tImGui::SameLine();\r\n\t\t\t}\r\n\t\t\tImGui::InputFloat(\"Rot. speed\", &_speedRotFpsCam, 0.1f, 0.5f);\r\n\t\t\tImGui::PopItemWidth();\r\n\t\t}\r\n\t\tImGui::End();\r\n\t}\r\n\r\n\r\n\tvoid FPSCamera::moveUsingWASD(const sibr::Input& input, float deltaTime)\r\n\t{\r\n\r\n\r\n\t\tif (input.key().isActivated(sibr::Key::LeftControl)) { return; }\r\n\r\n\t\tfloat camSpeed = 2.f * deltaTime\t\t* IBRVIEW_CAMSPEED;\r\n\t\tif (_currentCamera.ortho()) {\r\n\t\t\tcamSpeed *= 5.0f;\r\n\t\t}\r\n\t\tfloat camRotSpeed = 30.f * deltaTime\t* IBRVIEW_CAMSPEED;\r\n\t\t//float camSpeed = 0.1f;\r\n\t\t//float camRotSpeed = 1.f;\r\n\r\n\t\tsibr::Vector3f move(0, 0, 0);\r\n\r\n\t\tmove.x() -= input.key().isActivated(sibr::Key::A) ? camSpeed : 0.f;\r\n\t\tmove.x() += input.key().isActivated(sibr::Key::D) ? camSpeed : 0.f;\r\n\t\tmove.z() -= input.key().isActivated(sibr::Key::W) ? camSpeed : 0.f;\r\n\t\tmove.z() += input.key().isActivated(sibr::Key::S) ? camSpeed : 0.f;\r\n\t\tmove.y() -= input.key().isActivated(sibr::Key::Q) ? camSpeed : 0.f;\r\n\t\tmove.y() += input.key().isActivated(sibr::Key::E) ? camSpeed : 0.f;\r\n\r\n\t\t// If the acceleration effect is enabled, we alter the speed along a move.\r\n\t\tif(_useAcceleration) {\r\n\t\t\tif (move.isNull() == true) {\r\n\t\t\t\t_speedFpsCam = 1.f;\r\n\t\t\t} else {\r\n\t\t\t\t_speedFpsCam *= 1.02f;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tsibr::Vector3f pivot(0, 0, 0);\r\n\r\n\t\tcamRotSpeed *= _speedRotFpsCam;\r\n\t\tpivot[1] += input.key().isActivated(sibr::Key::J) ? camRotSpeed : 0.f;\r\n\t\tpivot[1] -= input.key().isActivated(sibr::Key::L) ? camRotSpeed : 0.f;\r\n\t\tpivot[0] -= input.key().isActivated(sibr::Key::K) ? camRotSpeed : 0.f;\r\n\t\tpivot[0] += input.key().isActivated(sibr::Key::I) ? camRotSpeed : 0.f;\r\n\t\tpivot[2] -= input.key().isActivated(sibr::Key::O) ? camRotSpeed : 0.f;\r\n\t\tpivot[2] += input.key().isActivated(sibr::Key::U) ? camRotSpeed : 0.f;\r\n\r\n\t\tif (_currentCamera.ortho()) {\r\n\t\t\tif (input.key().isActivated(sibr::Key::Z)) {\r\n\t\t\t\t_currentCamera.orthoRight(_currentCamera.orthoRight()/1.1f);\r\n\t\t\t\t_currentCamera.orthoTop(_currentCamera.orthoTop()/1.1f);\r\n\t\t\t\t_speedRotFpsCam /= 1.1f;\r\n\t\t\t}\r\n\t\t\telse if (input.key().isActivated(sibr::Key::X)) {\r\n\t\t\t\t_currentCamera.orthoRight(_currentCamera.orthoRight()*1.1f);\r\n\t\t\t\t_currentCamera.orthoTop(_currentCamera.orthoTop()*1.1f);\r\n\t\t\t\t_speedRotFpsCam *= 1.1f;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Try to keep the same altitude as cameras around.\r\n\t\tif (_goalAltitude != -1) {\r\n\t\t\tsibr::Vector3f worldUp(0., 0., 1.);\r\n\t\t\tconst sibr::Vector3f custom_forward = _currentCamera.right().cross(worldUp);\r\n\t\t\tconst sibr::Vector3f translation_right = (_speedFpsCam * move.x()) * _currentCamera.right();\r\n\r\n\t\t\tsibr::Vector3f translation = _speedFpsCam * (move.z() * custom_forward) + translation_right;\r\n\t\t\t//const float altitudeDiff = _goalAltitude - _currentCamera.position().z();\r\n\t\t\ttranslation[2] = _goalAltitude - _currentCamera.position().z();\r\n\r\n\t\t\t_currentCamera.translate(translation);\r\n\t\t}\r\n\t\telse {\r\n\t\t\t_currentCamera.translate(move * _speedFpsCam, _currentCamera.transform());\r\n\t\t}\r\n\r\n\t\t_currentCamera.rotate(pivot, _currentCamera.transform());\r\n\t}\r\n\r\n\tvoid FPSCamera::moveUsingMousePan( const sibr::Input& input, float deltaTime )\r\n\t{\r\n\t\t\r\n\t\tfloat speed = 0.05f*deltaTime;\r\n\t\tsibr::Vector3f move(\r\n\t\t\tinput.mouseButton().isActivated(sibr::Mouse::Left)? input.mouseDeltaPosition().x()*speed : 0.f,\r\n\t\t\tinput.mouseButton().isActivated(sibr::Mouse::Right)? input.mouseDeltaPosition().y()*speed : 0.f,\r\n\t\t\tinput.mouseButton().isActivated(sibr::Mouse::Middle)? input.mouseDeltaPosition().y()*speed : 0.f\r\n\t\t\t);\r\n\t\t_currentCamera.translate(move, _currentCamera.transform());\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/FPSCamera.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <memory>\n#include <fstream>\n\n#include \"Config.hpp\"\n#include \"core/graphics/Shader.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include \"ICameraHandler.hpp\"\n\n\nnamespace sibr {\n\n\tclass Viewport;\n\tclass Mesh;\n\tclass Input;\n\n\t/** Interactive camera that can be moved using WASD keys.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT FPSCamera : public ICameraHandler\n\t{\n\t\n\tpublic:\n\n\t\t/**\n\t\t Default constructor.\n\t\t*/\n\t\tFPSCamera( void );\n\n\t\t/**\n\t\t\tSetup the FPS camera so that it has the same pose as the argument camera. \n\t\t\\param cam the reference camera\n\t\t*/\n\t\tvoid fromCamera(const sibr::InputCamera & cam);\n\n\t\t/**\n\t\t\tUpdate the FPS camera based on the user input (keyboard). \n\t\t\\param input the user input\n\t\t\\param deltaTime time elapsed since last update\n\t\t*/\n\t\tvoid update( const sibr::Input & input, float deltaTime);\n\n\t\t/** Move to a camera position/orientation that is a distance-wieghted combination of the given cameras.\n\t\t\\param cams the cameras list.\n\t\t*/\n\t\tvoid snap(const std::vector<InputCamera::Ptr> & cams);\n\n\t\t// ICameraHandler interface\n\n\t\t/** Update the FPS camera based on the user input.\n\t\t\\param input the user input\n\t\t\\param deltaTime time elapsed since last update\n\t\t\\param viewport the view viewport\n\t\t*/\n\t\tvirtual void update(const sibr::Input & input, const float deltaTime, const Viewport & viewport) override;\n\n\t\t/** \\return the current camera */\n\t\tvirtual const sibr::InputCamera & getCamera( void ) const override;\n\n\t\t/** Set the camera speed.\n\t\t\\param speed translation speed\n\t\t\\param angular rotation speed\n\t\t*/\n\t\tvoid setSpeed(const float speed, const float angular = 0.0);\n\n\t\t/** Dispaly GUI.\n\t\t\\param suffix Panel title suffix\n\t\t*/\n\t\tvirtual void onGUI(const std::string& suffix) override;\n\n\t\tvoid setGoalAltitude(const float& goalAltitude);\n\n\tprivate:\n\n\t\tfloat _speedFpsCam, _speedRotFpsCam; ///< Camera speeds.\n\t\tbool _hasBeenInitialized; ///< Has the camera been initialized.\n\t\tsibr::InputCamera _currentCamera; ///< Current camera.\n\t\tbool _useAcceleration; ///< Should the camera accelerate the longer keys are pressed.\n\t\tfloat _goalAltitude;\n\n\t\t/** Update camera pose based on keys. \n\t\t\\param input user input\n\t\t\\param deltaTime elapsed time\n\t\t*/\n\t\tvoid moveUsingWASD( const sibr::Input& input, float deltaTime);\n\n\t\t/** Update camera pose based on mouse.\n\t\t\\param input user input\n\t\t\\param deltaTime elapsed time\n\t\t*/\n\t\tvoid moveUsingMousePan( const sibr::Input& input, float deltaTime);\n\t\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/FPSCounter.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include <fstream>\r\n#include <memory>\r\n\r\n#include \"core/view/FPSCounter.hpp\"\r\n#include \"core/assets/Resources.hpp\"\r\n\r\n#include <imgui/imgui.h>\r\n#include \"core/graphics/GUI.hpp\"\r\n#include \"imgui/imgui_internal.h\"\r\n\r\n#define SIBR_FPS_SMOOTHING 60\r\n\r\n\r\nnamespace sibr\r\n{\r\n\r\n\tint FPSCounter::_count = 0;\r\n\r\n\tFPSCounter::FPSCounter(const bool overlayed){\r\n\t\t_frameTimes = std::vector<float>(SIBR_FPS_SMOOTHING, 0.0f);\r\n\t\t_frameIndex = 0;\r\n\t\t_frameTimeSum = 0.0f;\r\n\t\t_lastFrameTime = std::chrono::high_resolution_clock::now();\r\n\t\t_position = sibr::Vector2f(-1, -1);\r\n\t\tif (overlayed) {\r\n\t\t\t_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;\r\n\t\t} else {\r\n\t\t\t_flags = 0;\r\n\t\t}\r\n\t\t_hidden = false;\r\n\t\t_name = \"Metrics##\" + std::to_string(_count);\r\n\t\t++_count;\r\n\t}\r\n\r\n\tvoid FPSCounter::init(const sibr::Vector2f & position){\r\n\t\t_position = position;\r\n\t}\r\n\t\r\n\tvoid FPSCounter::render(){\r\n\r\n\t\tif (_hidden) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (_position.x() != -1) {\r\n\t\t\tImGui::SetNextWindowPos(ImVec2(_position.x(), _position.y()));\r\n\t\t\tImGui::SetNextWindowSize(ImVec2(0, ImGui::GetTitleBarHeight()), ImGuiCond_FirstUseEver);\r\n\t\t}\r\n\t\t\r\n\t\tImGui::SetNextWindowBgAlpha(0.5f);\r\n\t\tif (ImGui::Begin(_name.c_str(), nullptr, _flags))\r\n\t\t{\r\n\t\t\tImGui::SetWindowFontScale(1.8);\r\n\t\t\tconst float frameTime = _frameTimeSum / float(SIBR_FPS_SMOOTHING);\r\n\t\t\tImGui::Text(\"%.2f (%.2f ms)\", 1.0f/ frameTime, frameTime*1000.0f);\r\n\t\t\tImGui::SetWindowFontScale(1);\r\n\t\t}\r\n\r\n\t\tImGui::End();\r\n\t}\r\n\t\r\n\tvoid FPSCounter::update(float deltaTime){\r\n\t\t_frameTimeSum -= _frameTimes[_frameIndex];\r\n\t\t_frameTimeSum += deltaTime;\r\n\t\t_frameTimes[_frameIndex] = deltaTime;\r\n\t\t_frameIndex = (_frameIndex + 1) % SIBR_FPS_SMOOTHING;\r\n\t}\r\n\t\r\n\tvoid FPSCounter::update(bool doRender) {\r\n\t\tauto now = std::chrono::high_resolution_clock::now();\r\n\t\tfloat deltaTime = std::chrono::duration<float>(now - _lastFrameTime).count();\r\n\t\tupdate(deltaTime);\r\n\t\tif (doRender) {\r\n\t\t\trender();\r\n\t\t}\r\n\t\t_lastFrameTime = now;\r\n\t\t\r\n\t}\r\n\r\n} // namespace sibr \r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/FPSCounter.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <vector>\n# include \"core/view/Config.hpp\"\n# include <core/system/Vector.hpp>\n\n# include <chrono>\n\nnamespace sibr\n{\n\n\t/** Provde a small GUI panel to display the current framerate, smoothed over multiple frames.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT FPSCounter\n\t{\n\tpublic:\n\t\ttypedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point; ///< Time type.\n\n\t\t/** Constructor.\n\t\t\\param overlayed if true, the GUI panel is always displayed on top of all others. \n\t\t*/\n\t\tFPSCounter(const bool overlayed = true);\n\n\t\t/** Setup at a given screen location.\n\t\t\\param position the position on screen (in pixels).\n\t\t*/\n\t\tvoid init(const sibr::Vector2f & position);\n\n\t\t/** generate the ImGui panel. */\n\t\tvoid render();\n\n\t\t/** Update state using external timing.\n\t\t\\param deltaTime time elapsed since last udpate. \n\t\t*/\n\t\tvoid update(float deltaTime);\n\n\t\t/** Update state using internal timer.\n\t\t\\param doRender should the ImGui panel be genrated immediatly\n\t\t*/\n\t\tvoid update(bool doRender = true);\n\n\t\t/** Toggle the panel visibility. */\n\t\tvoid toggleVisibility() {\n\t\t\t_hidden = !_hidden;\n\t\t}\n\n\t\t/** \\return true if the panel visible. */\n\t\tbool active() const {\n\t\t\treturn !_hidden;\n\t\t}\n\n\tprivate:\n\t\ttime_point\t\t\t\t\t\t\t_lastFrameTime; ///< Last frame duration.\n\t\tsibr::Vector2f\t\t\t\t\t\t_position; ///< on screen position.\n\t\tstd::vector<float>\t\t\t\t\t_frameTimes; ///< Last N frame times.\n\t\tsize_t\t\t\t\t\t\t\t\t_frameIndex; ///< Current position in the time list.\n\t\tfloat\t\t\t\t\t\t\t\t_frameTimeSum; ///< Current running sum.\n\t\tint\t\t\t\t\t\t\t\t\t_flags; ///< Imgui display flags.\n\t\tbool\t\t\t\t\t\t\t\t_hidden; ///< Visibility status.\n\t\tstd::string\t\t\t\t\t\t\t_name; ///< Panel name.\n\t\tstatic int\t\t\t\t\t\t\t_count; ///< Internal counter to avoid collision when multiple framerate panels are displayed.\n\t};\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/IBRBasicUtils.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"IBRBasicUtils.hpp\"\n\nnamespace sibr {\n\tstd::vector<uint> IBRBasicUtils::selectCameras(const std::vector<InputCamera::Ptr>& cams, const Camera & eye, uint count)\n\t{\n\t\t// Select one method\n\t\treturn selectCamerasAngleWeight(cams, eye, count);\n\t\t//return selectCamerasSimpleDist(cams, eye, count);\n\t}\n\n\tstd::vector<uint> IBRBasicUtils::selectCamerasSimpleDist(const std::vector<InputCamera::Ptr>& cams, const sibr::Camera & eye, uint count, const bool& distOnly)\n\t{\n\t\tstd::vector<uint> warped_img_id;\n\t\tstd::multimap<float, uint> dist;                 // distance wise closest input cameras\n\n\t\tfor (uint i = 0; i < cams.size(); ++i)\n\t\t{\n\t\t\tif (cams.at(i)->isActive())\n\t\t\t{\n\t\t\t\tfloat d = sibr::distance(cams[i]->position(), eye.position());\n\t\t\t\tfloat a = sibr::dot(cams[i]->dir(), eye.dir());\n\t\t\t\tif (distOnly) {\n\t\t\t\t\tdist.insert(std::make_pair(d, i));\n\t\t\t\t}\n\t\t\t\telse if (a > 0.707) {\t\t\t\t\t// cameras with 45 degrees\n\t\t\t\t\tdist.insert(std::make_pair(d, i));\t// sort distances in increasing order\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstd::multimap<float, uint>::const_iterator\td_it(dist.begin());\n\t\tfor (uint i = 0; d_it != dist.end() && i < count; ++d_it, ++i)\n\t\t\twarped_img_id.push_back(d_it->second);\n\n\t\tSIBR_ASSERT(warped_img_id.size() <= count);\n\n\t\treturn warped_img_id;\n\t}\n\n\tstd::vector<uint> IBRBasicUtils::selectCamerasAngleWeight(const std::vector<InputCamera::Ptr>& cams, const sibr::Camera & eye, uint count)\n\t{\n\t\tconst Vector3f& position = eye.position();\n\t\tconst Quaternionf& rotation = eye.rotation();\n\t\tfloat angleWeight = 0.3f;\n\n\t\tfloat maxdist = 0.f;\n\t\tstd::vector<float>\tsqrDists(cams.size(), 0.f);\n\n\t\tfor (uint i = 0; i < cams.size(); ++i)\n\t\t{\n\t\t\tif (cams.at(i)->isActive())\n\t\t\t{\n\t\t\t\tfloat sqrDist = (cams[i]->position() - position).squaredNorm();\n\t\t\t\tsqrDists[i] = sqrDist;\n\t\t\t\tmaxdist = std::max(sqrDist, maxdist);\n\t\t\t}\n\t\t}\n\n\t\tstd::multimap<float, uint>\tfactors;\n\t\tfor (uint i = 0; i < cams.size(); ++i)\n\t\t{\n\t\t\tif (cams.at(i)->isActive())\n\t\t\t{\n\t\t\t\tfloat a = sibr::dot(cams[i]->dir(), eye.dir());\n\t\t\t\tif (a > 0.707)\t\t\t\t\t\t\t// cameras with 45 degrees\n\t\t\t\t{\n\t\t\t\t\tconst float midAngle = 4.71239f; // = 270 degree\n\t\t\t\t\tfloat sqrDist = sqrDists[i];\n\t\t\t\t\tfloat currNormalDist = inverseLerp(0.f, maxdist, sqrDist);\n\t\t\t\t\tfloat currNormalAngle = inverseLerp(0.f, midAngle, angleRadian(rotation, cams[i]->rotation()));\n\t\t\t\t\tfloat factor = currNormalDist*(1.f - angleWeight) + currNormalAngle*angleWeight;\n\n\t\t\t\t\tfactors.insert(std::make_pair(factor, i));\t// sort distances in increasing order\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstd::vector<uint> warped_img_id;\n\t\tstd::multimap<float, uint>::const_iterator\td_it(factors.begin());\n\t\tfor (uint i = 0; d_it != factors.end() && i < count; ++d_it, ++i)\n\t\t\twarped_img_id.push_back(d_it->second);\n\n\t\tSIBR_ASSERT(warped_img_id.size() <= count);\n\n\t\treturn warped_img_id;\n\t}\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/IBRBasicUtils.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"core/view/Config.hpp\"\n#include \"core/graphics/Utils.hpp\"\n#include \"core/graphics/Camera.hpp\"\n#include \"core/graphics/Image.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include <map>\n\nnamespace sibr{\n\n\t/** Provide basic IBR utilities. \n\t \\ingroup sibr_view\n\t */\n\tclass SIBR_VIEW_EXPORT IBRBasicUtils\n\t{\n\n\tpublic:\n\n\t\t/** Select cameras for a given viewpoint.\n\t\t\\param cams cameras to select from\n\t\t\\param eye novel viewpoint\n\t\t\\param count number of cameras to select\n\t\t\\return a list of selected camera indices.\n\t\t\\warning The number of cameras selected might be lower than count\n\t\t\\sa selectCamerasAngleWeight\n\t\t*/\n\t\tstatic std::vector<uint> selectCameras(const std::vector<InputCamera::Ptr>& cams, const sibr::Camera& eye, uint count);\n\n\t\t/** Select cameras based on distance to a given viewpoint. Cameras with an orientation that is more than 45� off compared to the reference are ignored.\n\t\t\\param cams cameras to select from\n\t\t\\param eye novel viewpoint\n\t\t\\param count number of cameras to select\n\t\t\\return a list of selected camera indices.\n\t\t\\warning The number of cameras selected might be lower than count\n\t\t*/\n\t\tstatic std::vector<uint> selectCamerasSimpleDist(const std::vector<InputCamera::Ptr>& cams, const sibr::Camera& eye, uint count, const bool& distOnly = false);\n\n\t\n\t\t/** Select cameras based on distance and orientation to a given viewpoint. Cameras with an orientation that is more than 45� off compared to the reference are ignored.\n\t\t\\param cams cameras to select from\n\t\t\\param eye novel viewpoint\n\t\t\\param count number of cameras to select\n\t\t\\return a list of selected camera indices.\n\t\t\\warning The number of cameras selected might be lower than count\n\t\t*/\n\t\tstatic std::vector<uint> selectCamerasAngleWeight(const std::vector<InputCamera::Ptr>& cams, const sibr::Camera& eye, uint count);\n\n\t};\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ICameraHandler.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"ICameraHandler.hpp\""
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ICameraHandler.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <memory>\n#include <fstream>\n\n#include \"Config.hpp\"\n\n#include \"core/assets/InputCamera.hpp\"\n#include \"core/graphics/Viewport.hpp\"\n\nnamespace sibr {\n\tclass Input;\n\n\t/**\n\t * Represent an interaction mode (FPS, trackball,...) for a camera controlled by the user, or a combination of multiple modes.\n\t  \\ingroup sibr_view\n\t */\n\tclass SIBR_VIEW_EXPORT ICameraHandler\n\t{\n\tpublic:\n\t\tSIBR_CLASS_PTR(ICameraHandler)\n\n\tpublic:\n\n\t\t/** Update the camera handler state.\n\t\t\\param input user input\n\t\t\\param deltaTime time elapsed since last udpate\n\t\t\\param viewport view viewport\n\t\t*/\n\t\tvirtual void update(const sibr::Input & input, const float deltaTime, const Viewport & viewport) = 0;\n\n\t\t/** \\return the current camera. */\n\t\tvirtual const InputCamera & getCamera(void) const = 0;\n\n\t\t// We allow for default empty implementations of render and onGUI.\n\n\t\t/** Render on top of the associated view(s). \n\t\t\\param viewport the rendering region\n\t\t*/\n\t\tvirtual void onRender(const sibr::Viewport & viewport){};\n\t\t\n\t\t/** Display GUI options and infos\n\t\t\\param windowName extra name to avoid collsiion between the windows of different handlers. \n\t\t*/\n\t\tvirtual void onGUI(const std::string & windowName) {};\n\n\t};\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ImageView.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"ImageView.hpp\"\n\n#include <imgui/imgui.h>\n\nnamespace sibr {\n\n\tImageView::ImageView(bool interactiveMode)\n\t{\n\t\t_display.init(\"Display\", sibr::loadFile(\n\t\t\tsibr::getShadersDirectory(\"core\") + \"/image_viewer.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"core\") + \"/image_viewer.frag\"));\n\t\t\n\t\t_minVal.init(_display, \"minVal\");\n\t\t_maxVal.init(_display, \"maxVal\");\n\t\t_channels.init(_display, \"channels\");\n\t\t_size.init(_display, \"size\");\n\t\t_pos.init(_display, \"pos\");\n\t\t_scale.init(_display, \"scale\");\n\t\t_correctRatio.init(_display, \"correctRatio\");\n\t\t\n\t\t_minVal = { 0.0f, 0.0f, 0.0f, 0.0f };\n\t\t_maxVal = {1.0f, 1.0f, 1.0f, 1.0f};\n\t\t_showChannels[0] = _showChannels[1] = _showChannels[2] = _showChannels[3] = true;\n\t\t_bgColor = {0.25f, 0.25f, 0.25f};\n\t\t_pos = {0.0f, 0.0f};\n\t\t_scale = 1.0f;\n\t\t// When in \"fixed\" mode, don't respect the aspect ratio, to make sure that the full image is visible to the viewer.\n\t\t_correctRatio = interactiveMode;\n\t\t_showGUI = interactiveMode;\n\t\t_allowInteraction = interactiveMode;\n\t}\n\n\tvoid ImageView::onUpdate(Input& input, const Viewport & vp) {\n\t\tif(!_allowInteraction) {\n\t\t\treturn;\n\t\t}\n\t\t_scale = std::max(_scale - float(input.mouseScroll()) * 0.05f, 0.001f);\n\t\tif(input.mouseButton().isActivated(Mouse::Left)) {\n\t\t\tsibr::Vector2f delta = input.mouseDeltaPosition().cast<float>().cwiseQuotient(vp.finalSize());\n\t\t\tdelta[1] *= -1.0f;\n\t\t\t_pos = _pos.get() + delta;\n\t\t}\n\t}\n\n\tvoid ImageView::onGUI() {\n\n\t\tif(!_showGUI) {\n\t\t\treturn;\n\t\t}\n\t\tconst std::string guiName = name() + \" options\";\n\t\tif(ImGui::Begin(guiName.c_str())) {\n\n\t\t\tImGui::Text(\"Size: %dx%d. Scale: %.2f%%\", int(_size.get()[0]), int(_size.get()[1]), 100.0f * _scale);\n\t\t\t\n\t\t\tif (ImGui::Button(\"Reset view\")) {\n\t\t\t\t_pos = sibr::Vector2f(0.0f, 0.0f);\n\t\t\t\t_scale = 1.0f;\n\t\t\t}\n\t\t\tImGui::SameLine();\n\t\t\tImGui::Checkbox(\"Correct aspect ratio\", &_correctRatio.get());\n\t\t\t\n\t\t\tImGui::Separator();\n\n\t\t\tImGui::Text(\"Channels\"); ImGui::SameLine();\n\t\t\tImGui::Checkbox(\"R\", &_showChannels[0]); ImGui::SameLine();\n\t\t\tImGui::Checkbox(\"G\", &_showChannels[1]); ImGui::SameLine();\n\t\t\tImGui::Checkbox(\"B\", &_showChannels[2]); ImGui::SameLine();\n\t\t\tImGui::Checkbox(\"A\", &_showChannels[3]);\n\n\t\t\tImGui::ColorEdit3(\"Background\", &_bgColor[0]);\n\t\t\t\n\t\t\tImGui::Separator();\n\t\t\t\n\t\t\tconst float dragSpeed = 0.05f;\n\t\t\tbool editBounds = false;\n\t\t\t\n\t\t\tif(_lockChannels) {\n\t\t\t\t// Only display one value and ensure synchronisation between the RGB components.\n\t\t\t\teditBounds = ImGui::DragFloat(\"Min.\", &_minVal.get()[0], dragSpeed) || editBounds;\n\t\t\t\teditBounds = ImGui::DragFloat(\"Max.\", &_maxVal.get()[0], dragSpeed) || editBounds;\n\t\t\t} else {\n\t\t\t\teditBounds = ImGui::DragFloat4(\"Min.\", &_minVal.get()[0], dragSpeed) || editBounds;\n\t\t\t\teditBounds = ImGui::DragFloat4(\"Max.\", &_maxVal.get()[0], dragSpeed) || editBounds;\n\t\t\t}\n\t\t\t// Ensure internal state consistency.\n\t\t\tif(editBounds && _lockChannels) {\n\t\t\t\t_minVal.get()[3] = _minVal.get()[2] = _minVal.get()[1] = _minVal.get()[0];\n\t\t\t\t_maxVal.get()[3] = _maxVal.get()[2] = _maxVal.get()[1] = _maxVal.get()[0];\n\t\t\t}\n\t\t\t// Ensure ordering.\n\t\t\tif(editBounds) {\n\t\t\t\tconst sibr::Vector4f temp = _minVal;\n\t\t\t\t_minVal = temp.cwiseMin(_maxVal.get());\n\t\t\t\t_maxVal = temp.cwiseMax(_maxVal.get());\n\t\t\t}\n\t\t\t\n\t\t\tImGui::Checkbox(\"Lock values\", &_lockChannels);\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::Button(\"Reset values\")) {\n\t\t\t\t_minVal = sibr::Vector4f(0.0f, 0.0f, 0.0f, 0.0f);\n\t\t\t\t_maxVal = sibr::Vector4f(1.0f, 1.0f, 1.0f, 1.0f);\n\t\t\t}\n\t\t}\n\t\tImGui::End();\n\t}\n\n\tvoid ImageView::setRenderTarget(const IRenderTarget& rt, uint handle) {\n\t\t_tex = nullptr;\n\t\t_texHandle = rt.handle(handle);\n\t\t_size.get()[0] = float(rt.w()); _size.get()[1] = float(rt.h());\n\t}\n\n\tvoid ImageView::setTexture(const ITexture2D& tex)\n\t{\n\t\t_tex = nullptr;\n\t\t_texHandle = tex.handle();\n\t\t_size.get()[0] = float(tex.w()); _size.get()[1] = float(tex.h());\n\t}\n\n\t\n\tvoid ImageView::onRender(const Viewport & vpRender){\n\n\t\tvpRender.bind();\n\t\tvpRender.clear(_bgColor);\n\t\tif (_texHandle == 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Update channels flags.\n\t\t_channels.get()[0] = float(_showChannels[0]);\n\t\t_channels.get()[1] = float(_showChannels[1]);\n\t\t_channels.get()[2] = float(_showChannels[2]);\n\t\t_channels.get()[3] = float(_showChannels[3]);\n\n\t\t_display.begin();\n\n\t\tif(_showChannels[3]) {\n\t\t\tglEnable(GL_BLEND);\n\t\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\t\tglBlendEquation(GL_FUNC_ADD);\n\t\t}\n\t\t\n\t\t_maxVal.send();\n\t\t_minVal.send();\n\t\t_channels.send();\n\t\t_scale.send();\n\t\t_pos.send();\n\t\t_size.send();\n\t\t_correctRatio.send();\n\t\t\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D, _texHandle);\n\t\tRenderUtility::renderScreenQuad();\n\n\t\tglDisable(GL_BLEND);\n\t\t_display.end();\n\n\t\tCHECK_GL_ERROR;\n\t}\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ImageView.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Image.hpp>\n# include <core/graphics/Shader.hpp>\n# include <core/view/ViewBase.hpp>\n\nnamespace sibr {\n\n\t/** Basic view to display an image and inspect it.\n\t *Two modes are supported:\n\t * * interactive, where the user can pan/zoom, rescale the values, display some channels, via the mouse and GUI.\n\t * * fixed, where the image is displayed as is, without any modification possible.\n\t */\n\tclass SIBR_VIEW_EXPORT ImageView : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(ImageView);\n\tpublic:\n\n\t\t/** Constructor.\n\t\t * \\param interactiveMode should the GUI panel be displayed and the user be able to pan/zoom into the image\n\t\t */\n\t\tImageView(bool interactiveMode = true);\n\n\t\t/** Render the image in the currently bound rendertarget.\n\t\t *\\param vpRender the region to render into\n\t\t */\n\t\tvoid onRender(const Viewport & vpRender) override;\n\n\t\t/** Update user interactions.\n\t\t *\\param input the user input for the view\n\t\t *\\param vp the view viewport\n\t\t */\n\t\tvoid onUpdate(Input& input, const Viewport & vp) override;\n\n\t\t/*** Render GUI panels. */\n\t\tvoid onGUI() override;\n\n\t\t/** Set an attachment of a rendertarget as the texture to display.\n\t\t *\\param rt the rendertarget to display\n\t\t *\\param handle the index of the attachment to display\n\t\t *\\warning Will only be valid until the RT is deleted.\n\t\t */\n\t\tvoid setRenderTarget(const IRenderTarget & rt, uint handle = 0);\n\t\t\n\t\t/** Set the texture to display.\n\t\t *\\param tex the texture to display\n\t\t *\\warning Will only be valid until the texture is deleted.\n\t\t */\n\t\tvoid setTexture(const ITexture2D& tex);\n\n\t\t/** Set an image as the texture to display. An internal copy of the image will be sent to the GPU.\n\t\t *\\param img the image\n\t\t */\n\t\ttemplate<typename T_Type, unsigned T_NumComp>\n\t\tvoid setImage(const Image<T_Type, T_NumComp> & img) {\n\t\t\t// Create texture on the fly.\n\t\t\tstd::shared_ptr<Texture2D<T_Type, T_NumComp>> tex(new Texture2D<T_Type, T_NumComp>(img));\n\t\t\t_tex = tex;\n\t\t\t_texHandle = _tex->handle();\n\t\t\t_size.get()[0] = float(_tex->w());\n\t\t\t_size.get()[1] = float(_tex->h());\n\t\t}\n\n\t\t/** Set if the GUI panel should be displayed or not.\n\t\t *\\param opt display option\n\t\t **/\n\t\tvoid showGUI(bool opt) {\n\t\t\t_showGUI = opt;\n\t\t}\n\n\t\t/** Set if the user should be able to pan/zoom the image\n\t\t *\\param opt interaction option\n\t\t **/\n\t\tvoid allowInteraction(bool opt) {\n\t\t\t_allowInteraction = opt;\n\t\t}\n\t\t\n\tprotected:\n\n\t\tITexture2D::Ptr _tex; ///< Internal texture for the image input case.\n\t\tGLuint _texHandle = 0; ///< Texture to display.\n\t\t\n\t\tGLShader _display; ///< Shader.\n\t\t\n\t\tGLuniform<sibr::Vector4f> _minVal; ///< Normalization minimum.\n\t\tGLuniform<sibr::Vector4f> _maxVal; ///< Normalization maximum.\n\t\tbool _lockChannels = true; ///< Use the same normalization values for all channels.\n\t\t\n\t\tstd::array<bool, 4> _showChannels; ///< Display which channels.\n\t\tGLuniform<sibr::Vector4f> _channels; ///< Display which channels (shader).\n\n\t\tGLuniform<sibr::Vector2f> _pos; ///< Center position.\n\t\tGLuniform<sibr::Vector2f> _size; ///< Image size.\n\t\tGLuniform<float> _scale; ///< Image scale.\n\t\tGLuniform<bool> _correctRatio; ///< Use proper aspect ratio to display.\n\t\t\n\t\tsibr::Vector3f _bgColor; ///< Background color.\n\t\tbool _showGUI = true; ///< Show the GUI be displayed or not.\n\t\tbool _allowInteraction = true; ///< Should the user be able to pan/zoom into the image.\n\t};\n\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ImagesGrid.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"ImagesGrid.hpp\"\n\n#include <imgui/imgui.h>\n\n#define GUI_TEXT(txt) { std::stringstream sss; sss << txt << std::endl;  ImGui::Text(sss.str().c_str()); }\n\nnamespace sibr\n{\n\tvoid ImagesGrid::onUpdate(Input & input, const Viewport & vp)\n\t{\n\t\tconst Vector2f size = vp.finalSize();\n\n\t\tif (current_level_tex) {\n\t\t\timSizePixels = { current_level_tex->w(), current_level_tex->h() };\n\t\t\timSizePixels = imSizePixels.cwiseQuotient(pow(2.0, current_lod)*Vector2f(1, 1)).unaryExpr([](float f) { return std::floor(f); });\n\n\t\t\tnum_imgs = (int)current_layer->imgs_texture_array->depth();\n\t\t}\n\n\t\tcurrentActivePix = pixFromScreenPos(input.mousePosition(), size);\n\t\t_vp = vp;\n\n\t\tsetupGrid(vp);\n\n\t\tupdateZoomBox(input, vp);\n\t\tupdateZoomScroll(input);\n\t\tupdateDrag(input, size);\n\n\t\tif (currentActivePix && input.key().isActivated(Key::LeftControl) && input.mouseButton().isReleased(Mouse::Code::Left) ) {\n\t\t\tif (selectionMode == IMAGE_SELECTION) {\n\t\t\t\tcurrent_layer->image_selection.switchSelection(currentActivePix.im);\n\t\t\t}\n\t\t\tif (selectionMode == PIXEL_SELECTION && !current_layer->flip_texture) {\n\t\t\t\tcurrent_layer->pixel_selection.switchSelection(currentActivePix);\n\t\t\t}\n\t\t}\n\t\n\t\tstd::vector<int> all_ims;\n\t\tstd::iota(all_ims.begin(), all_ims.end(), 0);\n\t\taddImagesToHighlight(\"imBorders\", all_ims, { 0,0,0 });\n\n\t\tif (currentActivePix) {\n\t\t\taddPixelsToHighlight(\"activePix\", { currentActivePix }, { 0, 1, 0 }, 0.25f);\n\t\t}\n\t\t\n\t\tconst auto & imgs_list = current_layer->image_selection.get();\n\t\tif (!imgs_list.empty()) {\n\t\t\tstd::vector<int> selected_ims(std::begin(imgs_list), std::end(imgs_list));\n\t\t\taddImagesToHighlight(\"imSelection\", selected_ims, { 0,1,0 }, 0.1f);\n\t\t}\n\t\t\n\n\t}\n\n\tvoid ImagesGrid::onRender(const Viewport & viewport)\n\t{\n\t\tviewport.bind();\n\n\t\tviewport.clear(Vector3f(0.7f, 0.7f, 0.7f));\n\n\t\tif (!current_level_tex) {\n\t\t\treturn;\n\t\t}\n\n\t\tdraw_utils.image_grid(num_imgs, current_level_tex->handle(), grid_adjusted, viewRectangle.tl(), viewRectangle.br(), current_lod, current_layer->flip_texture);\n\n\t\tfor (const auto & ims_highlight : images_to_highlight) {\n\t\t\tconst auto & imgs = ims_highlight.second;\n\t\t\tfor (int im : imgs.data) {\n\t\t\t\thighlightImage(im, viewport, imgs.color, imgs.alpha);\n\t\t\t}\t\t\n\t\t}\n\n\t\tfor (const auto & pixels_highlight : pixels_to_highlight) {\n\t\t\tconst auto & pix_data = pixels_highlight.second;\n\t\t\tfor (const auto  pix : pix_data.data) {\n\t\t\t\thighlightPixel(pix, viewport, pix_data.color);\n\t\t\t}\n\t\t}\n\n\t\tdisplayZoom(viewport, draw_utils);\n\t}\n\n\tvoid ImagesGrid::onRender(IRenderTarget & dst)\n\t{\n\t\tdst.bind();\n\n\t\tViewport vp(0.0f, 0.0f, (float)dst.w(), (float)dst.h());\n\t\tonRender(vp);\n\n\t\tdst.unbind();\n\t}\n\n\tvoid ImagesGrid::onGUI()\n\t{\n\t\tif (ImGui::Begin(\"grid_gui\")) {\n\n\t\t\t\n\t\t\toptionsGUI();\n\n\t\t\tlistImagesLayerGUI();\n\n\t\t\tif (currentActivePix) {\n\t\t\t\tGUI_TEXT(\"current pix : \" << currentActivePix.im << \", \" << currentActivePix.pos.transpose());\n\n\t\t\t\tVector4f value = current_layer->imgs_texture_array->readBackPixel(currentActivePix.im, currentActivePix.pos[0], currentActivePix.pos[1], current_lod);\n\t\t\t\tif (integer_pixel_values) {\n\t\t\t\t\tVector4i value_i = (255 * value).cast<int>();\n\t\t\t\t\tGUI_TEXT(\" \\t value : \" << value_i.transpose());\n\t\t\t\t} else {\n\t\t\t\t\tGUI_TEXT(\" \\t value : \" << value.transpose());\n\t\t\t\t}\n\n\n\t\t\t}\n\n\t\t\tstd::stringstream s;\n\t\t\ts << \"active images : \";\n\t\t\tfor (int im : current_layer->image_selection.get()) {\n\t\t\t\ts << im << \", \";\n\t\t\t}\n\t\t\tImGui::Text(s.str().c_str());\n\n\t\t}\n\t\tImGui::End();\n\t}\n\n\tvoid ImagesGrid::addImagesToHighlight(const std::string & name, const std::vector<int>& imgs, const Vector3f & col, float alpha_fill)\n\t{\n\t\timages_to_highlight[name] = { imgs, col, alpha_fill };\n\t}\n\n\tvoid ImagesGrid::addPixelsToHighlight(const std::string & name, const std::vector<MVpixel>& pixs, const Vector3f & col, float alpha_fill)\n\t{\n\t\tpixels_to_highlight[name] = { pixs, col, alpha_fill };\n\t}\n\n\tconst MVpixel & ImagesGrid::getCurrentPixel()\n\t{\n\t\treturn currentActivePix;\n\t}\n\n\tvoid ImagesGrid::listImagesLayerGUI()\n\t{\n\t\t\n\t\tif (ImGui::CollapsingHeader(\"images_layers\")) {\n\t\t\t\n\t\t\t// 0 name | 1 infos | 2 options\n\t\t\tImGui::Columns(3, \"images_layers_list\");\n\n\t\t\tImGui::Separator();\n\n\t\t\tImGui::Text(\"layer\");\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"num x w x h\");\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"options\");\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Separator();\n\n\t\t\tfor (auto imgs_it = images_layers.begin(); imgs_it != images_layers.end(); ++imgs_it) {\n\t\t\t\tif (ImGui::Selectable(imgs_it->name.c_str(), current_layer == imgs_it)) {\n\t\t\t\t\tcurrent_layer = imgs_it;\n\t\t\t\t\tcurrent_level_tex = current_layer->imgs_texture_array;\n\t\t\t\t}\n\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tauto & tex_arr = imgs_it->imgs_texture_array;\n\t\t\t\tGUI_TEXT(tex_arr->depth() << \" x \" << tex_arr->w() << \" x \" << tex_arr->h());\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tImGui::Checkbox((\"flip##\" + imgs_it->name).c_str(), &imgs_it->flip_texture);\n\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tImGui::Separator();\n\t\t\t}\n\n\t\t\tImGui::Columns(1);\n\t\t\t\n\t\t}\n\t}\n\n\tvoid ImagesGrid::optionsGUI()\n\t{\n\t\tif (ImGui::CollapsingHeader(\"grid_options\")) {\n\n\n\t\t\tif (ImGui::SliderInt(\"num per row\", &num_per_row, 1, num_imgs)) {\n\t\t\t\tviewRectangle.center = { 0.5f, 0.5f };\n\t\t\t\tviewRectangle.diagonal = { 0.5f, 0.5f };\n\t\t\t}\n\t\t\tif (ImGui::SliderInt(\"pyramid level\", &current_lod, 0, 10)) {\n\t\t\t\tcurrentActivePix.isDefined = false;\n\t\t\t}\n\n\t\t\tstatic const std::vector<const char*> selection_mode_str = { \"no selection\", \"image\" ,\"pixel\" };\n\t\t\tfor (int i = 0; i < (int)selection_mode_str.size(); ++i) {\n\t\t\t\tif (i != 0) {\n\t\t\t\t\tImGui::SameLine();\n\t\t\t\t}\n\t\t\t\tif (ImGui::RadioButton(selection_mode_str[i], selectionMode == (SelectionMode)i)) {\n\t\t\t\t\tselectionMode = (SelectionMode)i;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tImGui::Checkbox(\"integer pixel values\", &integer_pixel_values);\n\t\t}\n\t}\n\n\tbool ImagesGrid::name_collision(const std::string & name) const\n\t{\n\t\tfor (const auto & layer : images_layers) {\n\t\t\tif (layer.name == name) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tvoid ImagesGrid::setupFirstLayer()\n\t{\n\t\tif (images_layers.size() == 1) {\n\t\t\tcurrent_layer = images_layers.begin();\n\t\t\tcurrent_level_tex = current_layer->imgs_texture_array;\n\t\t}\n\t}\n\n\tDrawUtilities::DrawUtilities()\n\t{\n\t\tinitBaseShader();\n\t\tinitGridShader();\n\t}\n\n\tvoid DrawUtilities::baseRendering(const Mesh & mesh, Mesh::RenderMode mode, const Vector3f & color, \n\t\tconst Vector2f & translation, const Vector2f & scaling, float alpha, const Viewport & vp)\n\t{\n\t\tglEnable(GL_BLEND);\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\tglBlendEquation(GL_FUNC_ADD);\n\n\t\tvp.bind();\n\t\tbaseShader.begin();\n\n\t\tscalingGL.set(scaling);\n\t\ttranslationGL.set(translation);\n\t\tcolorGL.set(color);\n\t\talphaGL.set(alpha);\n\n\t\tmesh.render(false, false, mode);\n\n\t\tbaseShader.end();\n\n\t\tglDisable(GL_BLEND);\n\t}\n\n\tvoid DrawUtilities::rectangle(const Vector3f & color, const Vector2f & tl, const Vector2f & br, bool fill, float alpha, const Viewport & vp)\n\t{\n\t\tauto rectangleMesh = std::make_shared<Mesh>();\n\n\t\trectangleMesh->vertices({\n\t\t\t{ tl.x(), tl.y() , 0 },\n\t\t\t{ tl.x(), br.y() , 0 },\n\t\t\t{ br.x(), br.y() , 0 },\n\t\t\t{ br.x(), tl.y() , 0 }\n\t\t\t});\n\n\t\tif (fill) {\n\t\t\trectangleMesh->triangles({\n\t\t\t\t{ 0,1,2 },\n\t\t\t\t{ 0,2,3 }\n\t\t\t\t});\n\n\t\t\tbaseRendering(*rectangleMesh, Mesh::FillRenderMode, color, { 0,0 }, { 1,1 }, alpha, vp);\n\t\t}\n\n\t\trectangleMesh->triangles({\n\t\t\t{ 0,0,1 },{ 1,1,2 },{ 2,2,3 },{ 3,3,0 }\n\t\t\t});\n\n\t\tbaseRendering(*rectangleMesh, Mesh::LineRenderMode, color, { 0,0 }, { 1,1 }, 1.0f, vp);\n\t}\n\n\tvoid DrawUtilities::rectanglePixels(const Vector3f & color, const Vector2f & center, const Vector2f & diagonalPixs, bool fill, float alpha, const Viewport & vp)\n\t{\n\t\tVector2f diagUV = diagonalPixs.cwiseQuotient(vp.finalSize());\n\t\tVector2f tl = center - diagUV;\n\t\tVector2f br = center + diagUV;\n\t\trectangle(color, tl, br, fill, alpha, vp);\n\t}\n\n\tvoid DrawUtilities::circle(const Vector3f & color, const Vector2f & center, float radius, bool fill, float alpha, const Vector2f & scaling, int precision)\n\t{\n\t\t\n\t\tstatic Mesh::Vertices vertices;\n\t\tstatic Mesh::Triangles circleTriangles, circleFillTriangles;\n\n\t\tint n = precision;\n\t\tif (circleFillTriangles.size() != n) {\n\t\t\tn = precision;\n\t\t\tcircleTriangles.resize(n);\n\t\t\tcircleFillTriangles.resize(n);\n\t\t\tfor (int i = 0; i < n; ++i) {\n\t\t\t\tint next = (i + 1) % n;\n\t\t\t\tcircleTriangles[i] = Vector3u(i, i, next);\n\t\t\t\tcircleFillTriangles[i] = Vector3u(i, next, n);\n\t\t\t}\n\n\t\t\tvertices.resize(n + 1);\n\t\t}\n\n\t\tdouble base_angle = 2.0*M_PI / (double)n;\n\t\tfloat rho = 0.5f*radius*(float)(1.0 + cos(0.5*base_angle));\n\n\t\tfor (int i = 0; i < n; ++i) {\n\t\t\tdouble angle = i * base_angle;\n\t\t\tvertices[i] = Vector3f((float)cos(angle), (float)sin(angle), (float)0.0);\n\t\t}\n\t\tvertices[n] = Vector3f(0, 0, 0);\n\n\t\tauto circleMesh = std::make_shared<Mesh>();\n\t\tauto circleFilledMesh = std::make_shared<Mesh>();\n\t\tcircleMesh->vertices(vertices);\n\t\tcircleFilledMesh->vertices(vertices);\n\t\tcircleMesh->triangles(circleTriangles);\n\t\tcircleFilledMesh->triangles(circleFillTriangles);\n\n\t\tif (fill) {\n\t\t\tbaseRendering(*circleFilledMesh, Mesh::FillRenderMode, color, { 0,0 }, { radius, radius }, alpha, {});\n\t\t}\n\t\tbaseRendering(*circleMesh, Mesh::LineRenderMode, color, { 0,0 }, { radius, radius }, 1.0f, {});\n\t}\n\n\tvoid DrawUtilities::circlePixels(const Vector3f & color, const Vector2f & center, float radius, bool fill, float alpha, const Vector2f & winSize, int precision)\n\t{\n\t\tVector2f centerUV = center.cwiseQuotient(winSize);\n\t\tVector2f scaling = radius * Vector2f(1, 1).cwiseQuotient(winSize);\n\n\t\tcircle(color, centerUV, 1.0f, fill, alpha, scaling, precision);\n\t}\n\n\tvoid DrawUtilities::linePixels(const Vector3f & color, const Vector2f & ptA, const Vector2f & ptB, const Vector2f & winSize)\n\t{\n\t\tVector2f uvA = ptA.cwiseQuotient(winSize);\n\t\tVector2f uvB = ptB.cwiseQuotient(winSize);\n\n\t\tMesh line;\n\t\tline.vertices({\n\t\t\t{ uvA.x(), uvA.y(), 0.0f },\n\t\t\t{ uvB.x(), uvB.y(), 0.0f }\n\t\t\t});\n\t\tline.triangles({\n\t\t\tVector3u(0,0,1)\n\t\t\t});\n\n\t\tbaseRendering(line, Mesh::LineRenderMode, color, { 0,0 }, { 1.0, 1.0 }, 1.0f, {});\n\n\t}\n\n\tvoid DrawUtilities::image_grid(int num_imgs, uint texture, const Vector2f & grid, const Vector2f & tl, const Vector2f & br, int lod, bool flip_texture)\n\t{\n\t\tgridShader.begin();\n\n\t\tnumImgsGL.set(num_imgs);\n\t\tgridGL.set(grid);\n\t\tlodGL.set((float)lod);\n\n\t\tgridTopLeftGL.set(tl);\n\t\tgridBottomRightGL.set(br);\n\n\t\tflip_textureGL.set(flip_texture);\n\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, texture);\n\t\tRenderUtility::renderScreenQuad();\n\n\t\tgridShader.end();\n\t}\n\n\tvoid DrawUtilities::initBaseShader()\n\t{\n\t\tconst std::string translationScalingVertexShader =\n\t\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform vec2 translation;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform vec2 scaling;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"\tgl_Position = vec4(scaling*in_vertex.xy+translation,0.0, 1.0);\t\t\\n\"\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\t\tconst std::string colorAlphaFragmentShader =\n\t\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform vec3 color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform float alpha;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"\t\tout_color = vec4(color,alpha);\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\t\tbaseShader.init(\"InterfaceUtilitiesBaseShader\", translationScalingVertexShader, colorAlphaFragmentShader);\n\t\tcolorGL.init(baseShader, \"color\");\n\t\talphaGL.init(baseShader, \"alpha\");\n\t\tscalingGL.init(baseShader, \"scaling\");\n\t\ttranslationGL.init(baseShader, \"translation\");\n\t}\n\n\tvoid DrawUtilities::initGridShader()\n\t{\n\t\tconst std::string gridVertexShader =\n\t\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\n\t\t\t\"out vec2 uv_coord;\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform vec2 zoomTL;\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform vec2 zoomBR;\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"\tuv_coord = 0.5*in_vertex.xy + vec2(0.5);\t\t\\n\"\n\t\t\t\"\tuv_coord.y = 1.0 - uv_coord.y;\t\t\t\t\t\\n\"\n\t\t\t\"\tuv_coord = zoomTL + (zoomBR-zoomTL)*uv_coord;\t\\n\"\t\n\t\t\t\"\tgl_Position = vec4(in_vertex.xy,0.0, 1.0);\t\t\\n\"\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\n\t\t\n\n\t\tconst std::string gridFragmentShader =\n\t\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"layout(binding = 0) uniform sampler2DArray texArray;\t\t\t\t\\n\"\n\t\t\t\"uniform int numImgs;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform vec2 grid;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform float lod;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"uniform bool flip_texture;\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"in vec2 uv_coord;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"\tvec2 uvs = uv_coord;\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"\tuvs =  grid*uvs;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"  if( uvs.x < 0 || uvs.y < 0 ) { discard; } \t\t\t\t\t\t\\n\"\n\t\t\t\"   vec2 fracs = fract(uvs); \t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"   vec2 mods = uvs - fracs; \t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\t\"   int n = int(mods.x + grid.x*mods.y); \t\t\t\t\t\t\t\\n\"\n\t\t\t\" if ( n< 0 || n > numImgs || mods.x >= grid.x || mods.y >= (float(numImgs)/grid.x) ) { discard; } else { \\n\"\n\t\t\t\"\tout_color = textureLod(texArray,vec3(fracs.x, flip_texture ? 1.0 -fracs.y : fracs.y,n), lod);\t}\t\t\\n\"\n\t\t\t\"\t//out_color = vec4(n/64.0,0.0,0.0,1.0); }\t\t\t\t\t\t\\n\"\n\t\t\t\"\t//out_color = vec4(uv_coord.x,uv_coord.y,0.0,1.0);\t}\t\t\t\\n\"\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\n\t\tgridShader.init(\"InterfaceUtilitiesMultiViewShader\", gridVertexShader, gridFragmentShader);\n\t\tgridTopLeftGL.init(gridShader, \"zoomTL\");\n\t\tgridBottomRightGL.init(gridShader, \"zoomBR\");\n\t\tnumImgsGL.init(gridShader, \"numImgs\");\n\t\tgridGL.init(gridShader, \"grid\");\n\t\tlodGL.init(gridShader, \"lod\");\n\t\tflip_textureGL.init(gridShader, \"flip_texture\");\n\t}\n\n\tMVpixel GridMapping::pixFromScreenPos(const Vector2i & pos, const Vector2f & size)\n\t{\n\t\tVector2f uvScreen = (pos.cast<float>() + 0.5*Vector2f(1, 1)).cwiseQuotient(size);\n\n\t\tVector2f posF = viewRectangle.tl() + 2.0*viewRectangle.diagonal.cwiseProduct(uvScreen);\n\t\tposF = posF.cwiseProduct(grid_adjusted);\n\n\t\t//std::cout << posF.transpose() << \" \" << numImgs << std::endl;\n\n\t\tif (posF.x() < 0 || posF.y() < 0 || posF.x() >= grid_adjusted.x() /* || posF.y() >= grid.y()  */) {\n\t\t\treturn MVpixel();\n\t\t}\n\n\t\tint x = (int)std::floor(posF.x());\n\t\tint y = (int)std::floor(posF.y());\n\n\t\tint n = x + num_per_row * y;\n\t\tif (n >= num_imgs) {\n\t\t\treturn MVpixel();\n\t\t}\n\n\t\tVector2f frac = posF - Vector2f(x, y);\n\t\tint j = (int)std::floor(frac.x()*imSizePixels.x());\n\t\tint i = (int)std::floor(frac.y()*imSizePixels.y());\n\t\treturn MVpixel(n, Vector2i(j, i));\n\t}\n\n\tVector2f GridMapping::uvFromMVpixel(const MVpixel & pix, bool use_center)\n\t{\n\t\tVector2f pos = ((pix.pos.cast<float>() + (use_center ? 0.5 : 0)*Vector2f(1, 1)).cwiseQuotient(imSizePixels) +\n\t\t\tVector2f(pix.im % num_per_row, pix.im / num_per_row)).cwiseQuotient(grid_adjusted);\n\t\tpos = (pos - viewRectangle.tl()).cwiseQuotient(viewRectangle.diagonal) - Vector2f(1, 1);\n\t\tpos.y() = -pos.y();\n\t\treturn pos;\n\t}\n\n\tvoid GridMapping::updateZoomBox(const Input & input, const sibr::Viewport & vp)\n\t{\n\t\tVector2f size = vp.finalSize();\n\n\t\tif (input.key().isPressed(Key::Q)) {\n\t\t\tviewRectangle.center = Vector2f(0.5, 0.5);\n\t\t\tviewRectangle.diagonal = Vector2f(0.5, 0.5);\n\t\t}\n\n\t\tif (input.mouseButton().isPressed(Mouse::Code::Right) && !input.key().isActivated(Key::LeftControl) && !zoomSelection) {\n\t\t\tzoomSelection.isActive = true;\n\t\t\tzoomSelection.first = input.mousePosition();\n\t\t}\n\n\t\tif (zoomSelection) {\n\t\t\tzoomSelection.second = input.mousePosition();\n\n\t\t\tViewport aligned_vp = Viewport(0, 0, vp.finalWidth(), vp.finalHeight());\n\t\t\t\n\t\t\tVector2f currentTL = (zoomSelection.first.cwiseMin(zoomSelection.second)).cast<float>();\n\t\t\tVector2f currentBR = (zoomSelection.first.cwiseMax(zoomSelection.second)).cast<float>();\n\t\t\t\n\t\t\tconst auto clamp = [](Vector2f & v, float w, float h) { v = v.cwiseMax(Vector2f(1, 1)).cwiseMin(Vector2f(w - 2, h - 2)); };\n\t\t\tclamp(currentTL, vp.finalRight(), vp.finalBottom());\n\t\t\tclamp(currentBR, vp.finalRight(), vp.finalBottom());\n\n\t\t\tif (input.mouseButton().isReleased(Mouse::Code::Right)) {\n\t\t\t\tzoomSelection.isActive = false;\n\t\t\t\tif (((currentBR - currentTL).array() > Vector2f(5, 5).array()).all()) {\n\t\t\t\t\tVector2f tlPix = viewRectangle.tl().cwiseProduct(size) + (viewRectangle.br() - viewRectangle.tl()).cwiseProduct(currentTL);\n\t\t\t\t\tVector2f brPix = viewRectangle.tl().cwiseProduct(size) + (viewRectangle.br() - viewRectangle.tl()).cwiseProduct(currentBR);\n\n\t\t\t\t\tVector2f center = 0.5f*(brPix + tlPix);\n\t\t\t\t\tVector2f diag = 0.5f*(brPix - tlPix);\n\n\t\t\t\t\tfloat new_ratio = diag.x() / diag.y();\n\t\t\t\t\tfloat target_ratio = size.x() / size.y();\n\t\t\t\t\tif (new_ratio > target_ratio) {\n\t\t\t\t\t\tdiag.y() = diag.x() / target_ratio;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdiag.x() = diag.y() * target_ratio;\n\t\t\t\t\t}\n\n\t\t\t\t\tviewRectangle.center = center.cwiseQuotient(size);\n\t\t\t\t\tviewRectangle.diagonal = diag.cwiseQuotient(size);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else if (!input.mouseButton().isActivated(Mouse::Code::Right) && input.isInsideViewport(aligned_vp)) {\n\t\t\t\tzoomSelection.isActive = false;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tvoid GridMapping::updateZoomScroll(const Input & input)\n\t{\n\t\tdouble scroll = input.mouseScroll();\n\t\tif (scroll) {\n\t\t\tfloat ratio = (scroll > 0 ? 0.75f : 1.33f);\n\t\t\tif (input.key().isActivated(Key::LeftControl)) {\n\t\t\t\tratio *= ratio;\n\t\t\t}\n\t\t\tviewRectangle.diagonal *= ratio;\n\t\t}\n\t}\n\n\tvoid GridMapping::updateCenter(const Input & input, const Vector2f & size)\n\t{\n\t}\n\n\tvoid GridMapping::updateDrag(const Input & input, const Vector2f & size)\n\t{\n\t\tif (input.mouseButton().isPressed(Mouse::Left)) {\n\t\t\tdrag.isActive = true;\n\t\t\tdrag.position = input.mousePosition();\n\t\t\tdrag.center = viewRectangle.center.cast<float>();\n\t\t} else if (drag.isActive && input.mouseButton().isReleased(Mouse::Left)) {\n\t\t\tdrag.isActive = false;\n\t\t}\n\t\tif (drag.isActive && input.mouseButton().isActivated(Mouse::Left)) {\n\t\t\tVector2f translation = 2.0*(input.mousePosition() - drag.position).cast<float>().cwiseQuotient(size).cwiseProduct(viewRectangle.diagonal);\n\t\t\tviewRectangle.center = drag.center - translation;\n\t\t}\n\t}\n\n\tvoid GridMapping::displayZoom(const Viewport & viewport, DrawUtilities & utils)\n\t{\n\t\tif (zoomSelection) {\n\t\t\tVector2f tl = 2.0*zoomSelection.first.cast<float>().cwiseQuotient(_vp.finalSize()) - Vector2f(1, 1);\n\t\t\tVector2f br = 2.0*zoomSelection.second.cast<float>().cwiseQuotient(_vp.finalSize()) - Vector2f(1, 1);\n\t\t\ttl.y() = -tl.y();\n\t\t\tbr.y() = -br.y();\n\t\t\tutils.rectangle(Vector3f(1, 0, 0), tl, br, false, 0.15f, viewport);\n\t\t}\n\t}\n\n\tvoid GridMapping::highlightPixel(const MVpixel & pix, const Viewport & viewport, const Vector3f & color, const Vector2f & pixScreenSize)\n\t{\n\t\tVector2f pixTl = uvFromMVpixel(pix);\n\t\tVector2f pixBR = uvFromMVpixel(MVpixel(pix.im, pix.pos + Vector2i(1, 1)));\n\n\t\tviewport.bind();\n\n\t\tif ((pixBR - pixTl).cwiseProduct(viewport.finalSize()).norm() < pixScreenSize.diagonal().norm()) {\n\t\t\t//if pixel size in screen space is too tiny\n\t\t\tdraw_utils.rectanglePixels(color, 0.5*(pixTl + pixBR), pixScreenSize, true, 0.15f, viewport);\n\t\t} else {\n\t\t\t//otherwise highlight pixel intirely\n\t\t\tdraw_utils.rectangle(color, pixTl, pixBR, true, 0.15f, viewport);\n\t\t}\n\t}\n\n\tvoid GridMapping::highlightImage(int im, const sibr::Viewport & viewport, const sibr::Vector3f & color, float alpha)\n\t{\n\t\tVector2f imTl = uvFromMVpixel(MVpixel(im, { 0, 0 }));\n\t\tVector2f imBR = uvFromMVpixel(MVpixel(im, imSizePixels.cast<int>()));\n\n\t\tdraw_utils.rectangle(color, imTl, imBR, alpha != 0 , alpha, viewport);\n\t}\n\n\tvoid GridMapping::setupGrid(const Viewport & vp)\n\t{\n\t\tfloat ratio_img = imSizePixels.x() / imSizePixels.y();\n\t\tfloat ratio_vp = vp.finalWidth() / vp.finalHeight();\n\t\tgrid_adjusted = num_per_row * Vector2f(1, ratio_img / ratio_vp);\n\t}\n\n}\n\t\n#undef GUI_TEXT"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ImagesGrid.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n# include \"Config.hpp\"\n#include <core/graphics/Shader.hpp>\n# include <core/graphics/Texture.hpp>\n#include <core/view/ViewBase.hpp>\n#include <list>\n#include <map>\n\nnamespace sibr\n{\n\n\tclass SIBR_VIEW_EXPORT DrawUtilities\n\t{\n\tpublic:\n\n\t\tDrawUtilities();\n\n\t\tGLShader baseShader;\n\n\t\tGLuniform<Vector3f> colorGL;\n\t\tGLuniform <float> alphaGL;\n\t\tGLuniform <Vector2f> scalingGL;\n\t\tGLuniform <Vector2f> translationGL;\n\n\t\tGLShader gridShader;\n\n\t\t\n\t\tGLuniform <Vector2f> gridGL;\n\t\tGLuniform <Vector2f> gridTopLeftGL;\n\t\tGLuniform <Vector2f> gridBottomRightGL;\n\t\tGLuniform <float> lodGL;\n\t\tGLuniform <int> numImgsGL; \n\t\tGLuniform<bool> flip_textureGL;\n\n\t\tvoid baseRendering(const Mesh & mesh, Mesh::RenderMode mode, const Vector3f & color, const Vector2f & translation, const Vector2f & scaling, float alpha, const Viewport & vp);\n\n\t\tvoid rectangle(const Vector3f & color, const Vector2f & tl, const Vector2f & br, bool fill, float alpha, const Viewport & vp );\n\t\tvoid rectanglePixels(const Vector3f & color, const Vector2f & center, const Vector2f & diagonalPixs, bool fill, float alpha, const Viewport & vp);\n\t\tvoid circle(const Vector3f & color, const Vector2f & center, float radius, bool fill, float alpha, const Vector2f & scaling = Vector3f(1, 1), int precision = 50);\n\t\tvoid circlePixels(const Vector3f & color, const Vector2f & center, float radius, bool fill, float alpha, const Vector2f & winSize, int precision = 50);\n\t\tvoid linePixels(const Vector3f & color, const Vector2f & ptA, const Vector2f & ptB, const Vector2f & winSize);\n\t\t\n\t\tvoid image_grid(int num_imgs, uint texture, const Vector2f & grid, const Vector2f & tl, const Vector2f & br, int lod, bool flip_texture);\n\n\tprivate:\n\n\t\tvoid initBaseShader();\n\t\tvoid initGridShader();\n\n\t};\n\n\tstruct QuadData\n\t{\n\t\tVector2f center = { 0.5, 0.5 };\n\t\tVector2f diagonal = { 0.5, 0.5 };\n\n\t\tVector2f br() const { return center + diagonal; }\n\t\tVector2f tl() const { return center - diagonal; }\n\t};\n\n\tstruct QuadSelectionData\n\t{\n\t\toperator bool() const { return isActive; }\n\t\tVector2i first;\n\t\tVector2i second;\n\t\tbool isActive = false;\n\t};\n\n\tstruct DragClickData\n\t{\n\t\tVector2f center;\n\t\tVector2i position;\n\t\tbool isActive = false;\n\t};\n\n\tstruct MVpixel {\n\t\tMVpixel() : isDefined(false) {}\n\t\tMVpixel(int i, const Vector2i & px) : im(i), pos(px), isDefined(true) {}\n\t\t\n\t\toperator bool() const { return isDefined; }\n\t\tbool operator ==(const MVpixel & other) const { return im == other.im && pos == other.pos; }\n\n\t\tVector2i pos;\n\t\tint im;\n\t\tbool isDefined = false;\n\t};\n\n\n\tclass SIBR_VIEW_EXPORT GridMapping {\n\n\tprotected:\n\t\tMVpixel pixFromScreenPos(const Vector2i & pos, const Vector2f & size);\n\n\t\t//uvs in opengl [1,-1]\n\t\tVector2f uvFromMVpixel(const MVpixel & pix, bool use_center = false);\n\n\t\tvoid updateZoomBox(const Input & input, const sibr::Viewport & vp);\n\t\tvoid updateZoomScroll(const Input & input);\n\t\tvoid updateCenter(const Input & input, const Vector2f & size);\n\t\tvoid updateDrag(const Input & input, const Vector2f & size);\n\n\t\tvoid displayZoom(const sibr::Viewport & viewport, DrawUtilities & utils);\n\n\t\tvoid highlightPixel(const MVpixel & pix, const sibr::Viewport & viewport, const sibr::Vector3f & color = { 0, 1, 0 }, const sibr::Vector2f & minPixSize = { 10.0f, 10.0f });\n\t\tvoid highlightImage(int im, const sibr::Viewport & viewport, const sibr::Vector3f & color = { 0, 1, 0 }, float alpha = 0);\n\t\tvoid setupGrid(const Viewport & vp);\n\n\t\tDrawUtilities draw_utils;\n\t\tViewport _vp;\n\t\tQuadData viewRectangle;\n\t\tQuadSelectionData zoomSelection;\n\t\tDragClickData drag;\n\n\t\tint num_per_row = 4;\n\t\tVector2f grid_adjusted;\n\n\t\tVector2f imSizePixels;\n\t\tint num_imgs;\n\t};\n\n\n\n\ttemplate<typename T>\n\tclass ObjectSelection {\n\tpublic:\n\t\tvoid switchSelection(const T & t) {\n\t\t\tfor (auto it = _selected.begin(); it != _selected.end(); ++it) {\n\t\t\t\tif (*it == t) {\n\t\t\t\t\t_selected.erase(it);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_selected.push_back(t);\n\t\t}\n\n\t\tconst std::list<T> get() const {\n\t\t\treturn _selected;\n\t\t}\n\n\tprotected:\n\t\tstd::list<T> _selected;\n\t};\n\n\tstruct ImageGridLayer {\t\n\t\tITexture2DArray::Ptr imgs_texture_array;\n\n\t\tObjectSelection<MVpixel> pixel_selection;\n\t\tObjectSelection<int> image_selection;\n\n\t\tstd::string name;\n\t\tbool flip_texture = false;\n\t};\n\n\ttemplate<typename T> \n\tstruct HighlightData {\n\t\tstd::vector<T> data;\n\t\tVector3f color;\n\t\tfloat alpha = 0;\n\t};\n\t\n\n\tclass SIBR_VIEW_EXPORT ImagesGrid : public ViewBase, GridMapping \n\t{\n\t\tSIBR_CLASS_PTR(ImagesGrid);\n\n\tpublic:\n\n\t\tenum SelectionMode { NO_SELECTION, IMAGE_SELECTION, PIXEL_SELECTION };\n\n\t\t//ViewBase interface\n\t\tvirtual void\tonUpdate(Input& input, const Viewport & vp) override;\n\t\tvirtual void\tonRender(const Viewport & viewport) override;\n\t\tvirtual void\tonRender(IRenderTarget & dst);\n\t\tvirtual void\tonGUI() override;\n\n\t\tvoid addImagesToHighlight(const std::string & name, const std::vector<int> & imgs, const Vector3f & col, float alpha_fill = 0);\n\t\tvoid addPixelsToHighlight(const std::string & name, const std::vector<MVpixel> & pixs, const Vector3f & col, float alpha_fill = 0);\n\n\n\t\tconst MVpixel & getCurrentPixel();\n\n\tprotected:\n\n\t\tvoid listImagesLayerGUI();\n\t\tvoid optionsGUI();\n\n\t\tbool name_collision(const std::string & name) const;\n\t\tvoid setupFirstLayer();\n\n\t\tstd::list<ImageGridLayer> images_layers;\n\t\tstd::list<ImageGridLayer>::iterator current_layer;\n\t\tITexture2DArray::Ptr current_level_tex;\n\t\tint current_lod = 0;\n\t\tbool integer_pixel_values = true;\n\n\t\tstd::map< std::string, HighlightData<MVpixel> > pixels_to_highlight;\n\t\tstd::map<std::string, HighlightData<int> > images_to_highlight;\n\n\t\tMVpixel currentActivePix;\n\t\tSelectionMode selectionMode = IMAGE_SELECTION;\n\n\tpublic:\n\t\ttemplate<typename T, uint N>\n\t\tvoid addImageLayer(\n\t\t\tconst std::string & layer_name,\n\t\t\tconst std::vector<Image<T, N> > & images,\n\t\t\tuint flags = 0\n\t\t) {\n\t\t\tstd::vector<cv::Mat> images_cv(images.size());\n\t\t\tfor (size_t im = 0; im < images.size(); ++im) {\n\t\t\t\timages_cv[im] = images[im].toOpenCVBGR();\n\t\t\t}\n\t\t\taddImageLayer<T, N>(layer_name, images_cv, flags);\n\t\t}\n\n\t\ttemplate<typename T, uint N>\n\t\tvoid addImageLayer(\n\t\t\tconst std::string & layer_name,\n\t\t\tconst std::vector<ImagePtr<T,N>> & images,\n\t\t\tuint flags = 0\n\t\t) {\n\t\t\tstd::vector<cv::Mat> images_cv(images.size());\n\t\t\tfor (size_t im = 0; im < images.size(); ++im) {\n\t\t\t\timages_cv[im] = images[im]->toOpenCVBGR();\n\t\t\t}\n\t\t\taddImageLayer<T, N>(layer_name, images_cv, flags);\n\t\t}\n\n\t\ttemplate<typename T, uint N>\n\t\tvoid addImageLayer(\n\t\t\tconst std::string & layer_name,\n\t\t\tconst std::shared_ptr<Texture2DArray<T,N>> & images\n\t\t) {\n\t\t\tif (name_collision(layer_name)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tImageGridLayer layer;\n\t\t\tlayer.name = layer_name;\n\t\t\tlayer.imgs_texture_array = { std::static_pointer_cast<ITexture2DArray>(images) };\n\n\t\t\timages_layers.push_back(layer);\n\n\t\t\tsetupFirstLayer();\n\t\t}\n\n\t\ttemplate<typename T, uint N>\n\t\tvoid addImageLayer(\n\t\t\tconst std::string & layer_name,\n\t\t\tconst std::vector<cv::Mat> & images,\n\t\t\tuint flags = 0\n\t\t) {\n\n\t\t\tif (!images.size()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (name_collision(layer_name)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tImageGridLayer layer;\n\t\t\tlayer.name = layer_name;\n\t\t\tlayer.imgs_texture_array = std::make_shared<Texture2DArray<T, N>>(images, flags | SIBR_GPU_AUTOGEN_MIPMAP);\n\t\t\timages_layers.push_back(layer);\n\n\t\t\tsetupFirstLayer();\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/InteractiveCameraHandler.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"InteractiveCameraHandler.hpp\"\n#include \"core/graphics/Input.hpp\"\n#include \"core/graphics/Viewport.hpp\"\n#include \"core/graphics/Window.hpp\"\n#include \"core/raycaster/Raycaster.hpp\"\n#include \"core/view/UIShortcuts.hpp\"\n#include \"core/graphics/GUI.hpp\"\n\n# define IBRVIEW_SMOOTHCAM_POWER\t0.1f\n# define IBRVIEW_USESMOOTHCAM\t\ttrue\n# define SIBR_INTERPOLATE_FRAMES    30\n\n\nnamespace sibr {\n\n\tInteractiveCameraHandler::InteractiveCameraHandler(const bool supportRecording) : _trackball(true) {\n\t\t_currentMode = NONE;\n\t\t_shouldSmooth = IBRVIEW_USESMOOTHCAM;\n\t\t_startCam = 0;\n\t\t_interpFactor = 0;\n\t\t_shouldSnap = false;\n\t\t_supportRecording = supportRecording;\n\t\t_radius = 100.0f;\n\t\t_currentCamId = 0;\n\t\t_saveFrame = false;\n\t\t_saveFrameVideo = false;\n\t\t_viewport = Viewport(0, 0, 0, 0);\n\t\t_triggerCameraUpdate = false;\n\t\t_isSetup = false;\n\n\t\tsibr::UIShortcuts::global().add(\"[Camera] b\", \"orbit mode\");\n\t\tsibr::UIShortcuts::global().add(\"[Camera] y\", \"trackball mode\");\n\t\tsibr::UIShortcuts::global().add(\"[Camera] v\", \"interpolation mode\");\n\t\tsibr::UIShortcuts::global().add(\"[Camera] maj+y\", \"show/hide trackball\");\n\t\tif (_supportRecording) {\n\t\t\tsibr::UIShortcuts::global().add(\"c\", \"playback camera path\");\n\t\t\tsibr::UIShortcuts::global().add(\"ctrl+c\", \"save camera path (enter filename in the prompt)\");\n\t\t\tsibr::UIShortcuts::global().add(\"shift+c\", \"load camera path (enter filename in the prompt)\");\n\t\t\tsibr::UIShortcuts::global().add(\"alt+c\", \"start recording camera path\");\n\t\t}\n\n\n\t}\n\n\t// save default camera for a scene\n\tvoid InteractiveCameraHandler::saveDefaultCamera(const std::string& datasetPath)\n\t{\n\t\tstd::string selectedFile = datasetPath;\n\n\t\tselectedFile.append(\"/default_camera.bin\");\n\t\t_currentCamera.saveToBinary(selectedFile);\n\t\tSIBR_LOG << \"Saved camera (\" << selectedFile << \").\" << std::endl;\n\t}\n\n\tvoid InteractiveCameraHandler::loadDefaultCamera(const sibr::InputCamera& cam, const std::string& datasetPath)\n\t{\n\t\tsibr::InputCamera savedCam;\n\t\tstd::ifstream camFile(datasetPath + \"/default_camera.bin\");\n\t\tfromCamera(cam, false);\n\t\tif (camFile.good()) {\n\t\t\tsavedCam.loadFromBinary(datasetPath + \"/default_camera.bin\");\n\t\t\tSIBR_LOG << \"Loaded  \" << datasetPath << \"/default_camera.bin\" << std::endl;\n\t\t\tfromCamera(savedCam, false);\n\t\t}\n\t}\n\n\tvoid InteractiveCameraHandler::setup(const std::vector<InputCamera::Ptr>& cams, const sibr::Vector2u & resolution, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster)\n\t{\n\t\tsetup(cams, viewport, raycaster);\n\n\t}\n\n\tvoid InteractiveCameraHandler::setup(const sibr::InputCamera & cam, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster) {\n\t\t_raycaster = raycaster;\n\t\t_viewport = viewport;\n\t\tfromCamera(cam, false);\n\t}\n\n\tvoid InteractiveCameraHandler::setup(const Eigen::AlignedBox<float, 3> & areaOfInterest, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster)\n\t{\n\t\t_raycaster = raycaster;\n\t\t_viewport = viewport;\n\t\t_radius = areaOfInterest.diagonal().norm();\n\t\t// Use the trackball to compute an initial camera.\n\t\t_trackball.fromBoundingBox(areaOfInterest, viewport);\n\t\tfromCamera(_trackball.getCamera(), false);\n\t}\n\n\tvoid InteractiveCameraHandler::setup(const std::vector<InputCamera::Ptr>& cams, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster, const sibr::Vector2f & clippingPlanes) {\n\n\t\t// setup interpolation path if not set\n\t\tif (_interpPath.empty()) {\n\t\t\tsetupInterpolationPath(cams);\n\t\t}\n\t\t// Update the near and far planes.\n\n\t\tsibr::Vector3f center(0, 0, 0);\n\t\tfor (const auto& cam : cams) {\n\t\t\tcenter += cam->transform().position();\n\t\t}\n\t\tcenter /= cams.size();\n\n\t\tfloat avgDist = 0;\n\t\tfor (const auto& cam : cams) {\n\t\t\tavgDist += (cam->transform().position() - center).norm();\n\t\t}\n\t\tavgDist /= cams.size();\n\t\t_radius = avgDist;\n\n\t\tsibr::InputCamera idealCam = *cams[0];\n\t\tif(clippingPlanes[0] < 0.0f || clippingPlanes[1] < 0.0f) {\n\t\t\tfloat zFar = -1.0f, zNear = -1.0f;\n\t\t\tfor (const auto & cam : cams) {\n\t\t\t\tzFar = (zFar<0 || cam->zfar() > zFar ? cam->zfar() : zFar);\n\t\t\t\tzNear = (zNear < 0 || cam->znear() < zNear ? cam->znear() : zNear);\n\t\t\t}\n\t\t\tidealCam.zfar(zFar*1.1f);\n\t\t\tidealCam.znear(zNear*0.9f);\n\t\t} else {\n\t\t\tidealCam.znear(clippingPlanes[0]);\n\t\t\tidealCam.zfar(clippingPlanes[1]);\n\t\t}\n\t\t\n\t\tSIBR_LOG << \"Interactive camera using (\" << idealCam.znear() << \",\" << idealCam.zfar() << \") near/far planes.\" << std::endl;\n\n\t\tsetup(idealCam, viewport, raycaster);\n\t}\n\n\tvoid InteractiveCameraHandler::setup(const std::shared_ptr<sibr::Mesh> mesh, const sibr::Viewport & viewport) {\n\t\t_raycaster = std::make_shared<sibr::Raycaster>();\n\t\t_raycaster->addMesh(*mesh);\n\t\t_viewport = viewport;\n\t\t_trackball.fromBoundingBox(mesh->getBoundingBox(), viewport);\n\t\t_radius = mesh->getBoundingBox().diagonal().norm();\n\t\tfromCamera(_trackball.getCamera(), false);\n\t}\n\n\tvoid InteractiveCameraHandler::fromCamera(const sibr::InputCamera & cam, bool interpolate, bool updateResolution) {\n\t\t_isSetup = true;\n\n\t\tsibr::InputCamera idealCam(cam);\n\t\tif (updateResolution) {\n\t\t\t// Viewport might have not been set, in this case defer the full camera update \n\t\t\t// until after the viewport has been updated, ie in onUpdate().\n\t\t\tif (_viewport.isEmpty()) {\n\t\t\t\t_triggerCameraUpdate = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst float w = _viewport.finalWidth();\n\t\t\t\tconst float h = _viewport.finalHeight();\n\t\t\t\tidealCam.size(uint(w), uint(h));\n\t\t\t\tidealCam.aspect(w / h);\n\t\t\t}\n\t\t}\n\n\t\t_orbit.fromCamera(idealCam, _raycaster);\n\t\t_fpsCamera.fromCamera(idealCam);\n\n\n\t\tif (_raycaster != nullptr) {\n\t\t\tsibr::RayHit hit = _raycaster->intersect(sibr::Ray(idealCam.position(), idealCam.dir()));\n\t\t\t// If hit at the proxy surface, save the distance between the camera and the mesh, to use as a trackball radius.\n\t\t\tif (hit.hitSomething()) {\n\t\t\t\t_radius = hit.dist();\n\t\t\t}\n\t\t}\n\t\t_trackball.fromCamera(idealCam, _viewport, _radius);\n\n\t\t_currentCamera = idealCam;\n\t\t_cameraFovDeg = _currentCamera.fovy() * 180.0f / float(M_PI);\n\n\t\tif (!interpolate) {\n\t\t\t_previousCamera = _currentCamera;\n\t\t}\n\n\t\t_clippingPlanes[0] = _currentCamera.znear();\n\t\t_clippingPlanes[1] = _currentCamera.zfar();\n\t}\n\n\tvoid InteractiveCameraHandler::fromTransform(const Transform3f & transform, bool interpolate, bool updateResolution)\n\t{\n\t\tInputCamera camCopy = getCamera();\n\t\tcamCopy.transform(transform);\n\t\tfromCamera(camCopy, interpolate, updateResolution);\n\t}\n\n\tvoid InteractiveCameraHandler::setClippingPlanes(float znear, float zfar) {\n\t\tif (znear > 0.0f) {\n\t\t\t_clippingPlanes[0] = znear;\n\t\t}\n\t\tif (zfar > 0.0f) {\n\t\t\t_clippingPlanes[1] = zfar;\n\t\t}\n\t\t_currentCamera.znear(_clippingPlanes[0]);\n\t\t_currentCamera.zfar(_clippingPlanes[1]);\n\t\tfromCamera(_currentCamera);\n\t}\n\n\tvoid InteractiveCameraHandler::switchMode(const InteractionMode mode) {\n\t\tif (_currentMode == mode) {\n\t\t\treturn;\n\t\t}\n\t\t_currentMode = mode;\n\n\t\t// Synchronize internal cameras.\n\t\tfromCamera(_currentCamera, _shouldSmooth);\n\n\t\t_interpFactor = 0;\n\n\t\tstd::cout << \"Switched to \";\n\t\tswitch (_currentMode) {\n\t\tcase ORBIT:\n\t\t\tstd::cout << \"orbit\";\n\t\t\tbreak;\n\t\tcase INTERPOLATION:\n\t\t\tstd::cout << \"interpolation\";\n\t\t\tbreak;\n\t\tcase TRACKBALL:\n\t\t\tstd::cout << \"trackball\";\n\t\t\tbreak;\n\t\tcase NONE:\n\t\t\tstd::cout << \"none\";\n\t\t\tbreak;\n\t\tcase FPS:\n\t\tdefault:\n\t\t\tstd::cout << \"fps&pan\";\n\t\t\tbreak;\n\t\t}\n\t\tstd::cout << \" mode.\" << std::endl;\n\n\t}\n\n\tint\tInteractiveCameraHandler::findNearestCamera(const std::vector<InputCamera::Ptr>& inputCameras, const bool& useRotation) const\n\t{\n\t\tif (inputCameras.size() == 0)\n\t\t\treturn -1;\n\n\t\tint selectedCam = 0;\n\t\tint numCams = inputCameras.size();\n\n\t\tstd::vector<uint> sortByDistance = sibr::IBRBasicUtils::selectCamerasSimpleDist(inputCameras, _currentCamera, numCams);\n\t\tstd::vector<uint> sortByAngle = sibr::IBRBasicUtils::selectCamerasAngleWeight(inputCameras, _currentCamera, numCams);\n\n\t\tstd::map<uint, int> weights;\n\t\tfor (uint cam_id = 0; cam_id < sortByDistance.size(); cam_id++) {\n\t\t\tweights[sortByDistance[cam_id]] = cam_id;\n\t\t}\n\n\t\tif (useRotation) {\n\t\t\tstd::vector<uint> sortByAngle = sibr::IBRBasicUtils::selectCamerasAngleWeight(inputCameras, _currentCamera, numCams);\n\t\t\tfor (uint cam_id = 0; cam_id < sortByAngle.size(); cam_id++) {\n\t\t\t\tweights[sortByAngle[cam_id]] += cam_id;\n\t\t\t}\n\t\t}\n\n\t\tstd::multimap<int, uint> combinedWeight;\n\n\t\tfor (auto const& weight : weights) {\n\t\t\tcombinedWeight.insert(std::make_pair(weight.second, weight.first));\n\t\t}\n\n\t\tselectedCam = combinedWeight.begin()->second;\n\t\t\n\t\treturn selectedCam;\n\t}\n\n\tvoid InteractiveCameraHandler::setupInterpolationPath(const std::vector<InputCamera::Ptr> & cameras) {\n\t\t_interpPath.resize(cameras.size());\n\n\t\tbool defaultPath = false;\n\t\tfor (int i = 0; i < cameras.size(); i++) {\n\t\t\tif (cameras[i]->isActive()) {\n\t\t\t\tif (cameras[i]->id() < cameras.size()) {\n\t\t\t\t\t_interpPath[cameras[i]->id()] = cameras[i];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tstd::cout << \"Cameras ID inconsistent. Setting default interpolation path.\" << std::endl;\n\t\t\t\t\tdefaultPath = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (defaultPath) {\n\t\t\t_interpPath.clear();\n\t\t\tfor (int i = 0; i < cameras.size(); i++) {\n\t\t\t\tif (cameras[i]->isActive()) {\n\t\t\t\t\t_interpPath.push_back(cameras[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstd::sort(_interpPath.begin(), _interpPath.end(), [](const InputCamera::Ptr & a, const InputCamera::Ptr & b) {\n\t\t\t\treturn a->id() < b->id();\n\t\t\t});\n\t\t}\n\t}\n\n\tvoid InteractiveCameraHandler::interpolate() {\n\t\tif (_interpPath.empty()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If we reach the last frame of the interpolation b/w two cameras, skip to next camera.\n\t\tif (_interpFactor == SIBR_INTERPOLATE_FRAMES - 1)\n\t\t{\n\t\t\t_interpFactor = 0;\n\t\t\t_startCam++;\n\t\t}\n\n\t\t// If we reach the last camera, restart the interpolation.\n\t\tif (_startCam >= _interpPath.size() - 1) {\n\t\t\t_interpFactor = 0;\n\t\t\t_startCam = 0;\n\t\t}\n\n\n\t\tfloat k = std::min(std::max(((_interpFactor) / (float)SIBR_INTERPOLATE_FRAMES), 1e-6f), 1.0f - 1e-6f);\n\n\t\tsibr::InputCamera & camStart = *_interpPath[_startCam];\n\t\tsibr::InputCamera & camNext = *_interpPath[_startCam + 1];\n\t\tconst sibr::Camera cam = sibr::Camera::interpolate(camStart, camNext, k);\n\t\t_currentCamera = sibr::InputCamera(cam, camStart.w(), camStart.h());\n\t\t_currentCamera.aspect(_viewport.finalWidth() / _viewport.finalHeight());\n\n\n\t\t_interpFactor = _interpFactor + 1;\n\t}\n\n\tvoid InteractiveCameraHandler::snapToCamera(const int i) {\n\t\tif (!_interpPath.empty()) {\n\t\t\tunsigned int nearestCam = (i == -1 ? findNearestCamera(_interpPath) : i);\n\t\t\tnearestCam = sibr::clamp(nearestCam, (unsigned int)(0), (unsigned int)(_interpPath.size() - 1));\n\t\t\tfromCamera(*_interpPath[nearestCam], true, false);\n\t\t}\n\t}\n\n\tfloat InteractiveCameraHandler::getInterpolatedHeight(const std::vector<InputCamera::Ptr>& inputCameras)\n\t{\n\t\tconst uint numCams = inputCameras.size();\n\t\tstd::vector<uint> sortByDistance = sibr::IBRBasicUtils::selectCamerasSimpleDist(inputCameras, _currentCamera, numCams, true);\n\t\tVector3f pos0 = 0.5f * (_interpPath[sortByDistance[0]]->position() + _interpPath[sortByDistance[1]]->position());\n\t\tVector3f pos1 = 0.5f * (_interpPath[sortByDistance[2]]->position() + _interpPath[sortByDistance[3]]->position());\n\n\t\tconst float dist = (pos1 - pos0).norm();\n\t\tconst float currentDist = (_currentCamera.position() - pos0).norm();\n\t\tconst float dist0 = (_currentCamera.position() - pos0).norm();\n\t\tconst float dist1 = (_currentCamera.position() - pos1).norm();\n\t\tconst float t = dist1 / (dist0 + dist1);\n\n\t\tconst float height = t * pos0.z() + (1 - t) * pos1.z(); // (cam1->position().z() - cam0->position().z());\n\t\treturn height;\n\t}\n\n\tvoid InteractiveCameraHandler::setFPSCameraSpeed(const float speed) {\n\t\t_fpsCamera.setSpeed(speed);\n\t}\n\n\tvoid InteractiveCameraHandler::update(const sibr::Input & input, float deltaTime, const sibr::Viewport & viewport) {\n\t\tif (!viewport.isEmpty()) {\n\t\t\t_viewport = viewport;\n\t\t}\n\t\tif (_triggerCameraUpdate && !_viewport.isEmpty()) {\n\t\t\tfromCamera(_currentCamera, false, true);\n\t\t\t_triggerCameraUpdate = false;\n\t\t}\n\t\tif (input.key().isReleased(Key::N)) {\n\t\t\t_keyCameras.emplace_back(new InputCamera(getCamera()));\n\t\t}\n\n\t\tif (input.key().isReleased(sibr::Key::B)) {\n\t\t\tswitchMode(_currentMode == ORBIT ? FPS : ORBIT);\n\t\t}\n\t\telse if (input.key().isReleased(sibr::Key::V)) {\n\t\t\tswitchMode(_currentMode == INTERPOLATION ? FPS : INTERPOLATION);\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::LeftShift) && input.key().isReleased(sibr::Key::Y)) {\n\t\t\tif (_currentMode == TRACKBALL) {\n\t\t\t\t_trackball.drawThis = !_trackball.drawThis;\n\t\t\t\tSIBR_LOG << \"[Trackball] Display visual guides: \" << (_trackball.drawThis ? \"on\" : \"off\") << \".\" << std::endl;\n\t\t\t}\n\t\t}\n\t\t// only free key\n\t\telse if (input.key().isReleased(sibr::Key::M)) {\n\t\t\t_cameraRecorder.saveImage(\"\", _currentCamera, _currentCamera.w(), _currentCamera.h());\n\t\t}\n\t\telse if (input.key().isReleased(sibr::Key::Y)) {\n\t\t\tswitchMode(_currentMode == TRACKBALL ? FPS : TRACKBALL);\n\t\t}\n\t\telse if (input.key().isReleased(sibr::Key::Space)) {\n\t\t\tswitchSnapping();\n\t\t}\n\t\telse if (input.key().isReleased(sibr::Key::P)) {\n\t\t\tsnapToCamera(-1);\n\n\t\t}\n\t\telse if (_supportRecording) {\n\t\t\tif (input.key().isActivated(Key::LeftShift) && (input.key().isActivated(Key::LeftAlt) || input.key().isActivated(Key::LeftControl)) && input.key().isReleased(Key::C))\n\t\t\t{\n\n\t\t\t\t_saveFrame = !_saveFrame;\n\t\t\t\tif (_saveFrame) {\n\t\t\t\t\tstd::string pathOutView;\n\t\t\t\t\tfor (uint i = 0; i < 10; ++i) std::cout << std::endl;\n\t\t\t\t\tstd::cout << \"Enter path to output the frames:\" << std::endl;\n\t\t\t\t\tsafeGetline(std::cin, pathOutView);\n\n\t\t\t\t\tif (!pathOutView.empty()) {\n\t\t\t\t\t\t_cameraRecorder.saving(pathOutView + \"/\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t_cameraRecorder.stopSaving();\n\t\t\t\t\t\t_saveFrame = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_cameraRecorder.stopSaving();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (input.key().isActivated(Key::LeftShift) && input.key().isReleased(Key::C))\n\t\t\t{\n\t\t\t\tstd::string filename;\n\n\t\t\t\tint w, h;\n\t\t\t\tfor (uint i = 0; i < 10; ++i) std::cout << std::endl;\n\t\t\t\tstd::cout << \"Enter a filename for loading a camera path:\" << std::endl;\n\t\t\t\tsafeGetline(std::cin, filename);\n\t\t\t\tstd::cout << \"Enter width for camera\" << std::endl;\n\t\t\t\tstd::cin >> w;\n\t\t\t\tstd::cout << \"Enter height for camera\" << std::endl;\n\t\t\t\tstd::cin >> h;\n\t\t\t\tstd::cin.get();\n\n\t\t\t\t_cameraRecorder.reset();\n\t\t\t\tif (boost::filesystem::extension(filename) == \".out\")\n\t\t\t\t\t_cameraRecorder.loadBundle(filename, w, h);\n\t\t\t\telse\n\t\t\t\t\t_cameraRecorder.load(filename);\n\t\t\t\t_cameraRecorder.playback();\n\t\t\t}\n\t\t\telse if (input.key().isActivated(Key::LeftControl) && input.key().isReleased(Key::C))\n\t\t\t{\n\t\t\t\tstd::string filename;\n\t\t\t\tfor (uint i = 0; i < 10; ++i) std::cout << std::endl;\n\t\t\t\tstd::cout << \"Enter a filename for saving a camera path:\" << std::endl;\n\t\t\t\tsafeGetline(std::cin, filename);\n\t\t\t\t_cameraRecorder.save(filename);\n\t\t\t\t_cameraRecorder.saveAsBundle(filename + \".out\", _currentCamera.h());\n\t\t\t\t_cameraRecorder.saveAsLookAt(filename + \".lookat\");\n\t\t\t\tif (_fribrExport) {\n\t\t\t\t\tconst int height = int(std::floor(1920.0f / _currentCamera.aspect()));\n\t\t\t\t\t_cameraRecorder.saveAsFRIBRBundle(filename + \"_fribr/\", 1920, height);\n\t\t\t\t}\n\t\t\t\t_cameraRecorder.stop();\n\t\t\t}\n\t\t\telse if (input.key().isActivated(Key::LeftAlt) && input.key().isReleased(Key::C))\n\t\t\t{\n\t\t\t\t_cameraRecorder.reset();\n\t\t\t\t_cameraRecorder.record();\n\t\t\t}\n\t\t\telse if (input.key().isActivated(Key::RightAlt) && input.key().isReleased(Key::C)) {\n\t\t\t\tstd::string filename;\n\t\t\t\tfor (uint i = 0; i < 10; ++i) std::cout << std::endl;\n\t\t\t\tstd::cout << \"Enter a filename for saving a camera path:\" << std::endl;\n\t\t\t\tsafeGetline(std::cin, filename);\n\t\t\t\t_cameraRecorder.playback();\n\t\t\t\t_cameraRecorder.saveAsBundle(filename + \".out\", _currentCamera.h());\n\t\t\t\t_cameraRecorder.saveAsLookAt(filename + \".lookat\");\n\t\t\t\tif (_fribrExport) {\n\t\t\t\t\tconst int height = int(std::floor(1920.0f / _currentCamera.aspect()));\n\t\t\t\t\t_cameraRecorder.saveAsFRIBRBundle(filename + \"_fribr/\", 1920, height);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (input.key().isReleased(Key::C)) {\n\t\t\t\t_cameraRecorder.playback();\n\t\t\t}\n\t\t}\n\n\t\t// If the camera recorder is currently playing, don't update the various camera modes.\n\t\tif (!_cameraRecorder.isPlaying()) {\n\n\t\t\tswitch (_currentMode) {\n\t\t\tcase ORBIT:\n\t\t\t\t_orbit.update(input, _raycaster);\n\t\t\t\t_currentCamera = _orbit.getCamera();\n\t\t\t\tbreak;\n\t\t\tcase INTERPOLATION:\n\t\t\t\tinterpolate();\n\t\t\t\tbreak;\n\t\t\tcase TRACKBALL:\n\t\t\t\t_trackball.update(input, _viewport, _raycaster);\n\t\t\t\t_currentCamera = _trackball.getCamera();\n\t\t\t\tbreak;\n\t\t\tcase NONE:\n\t\t\t\t//do nothing\n\t\t\t\tbreak;\n\t\t\tcase FPS:\n\t\t\tdefault:\n\n\t\t\t\tif (_altitudeInterp) {\n\n\t\t\t\t\t_fpsCamera.setGoalAltitude(getInterpolatedHeight(_interpPath));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_fpsCamera.setGoalAltitude(-1.f);\n\t\t\t\t}\n\n\t\t\t\t_fpsCamera.update(input, deltaTime);\n\t\t\t\tif (_shouldSnap) {\n\t\t\t\t\t_fpsCamera.snap(_interpPath);\n\t\t\t\t}\n\t\t\t\t_currentCamera = _fpsCamera.getCamera();\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (_shouldSmooth && _currentMode != INTERPOLATION) {\n\t\t\t\tconst sibr::Camera newcam = sibr::Camera::interpolate(_previousCamera, _currentCamera, IBRVIEW_SMOOTHCAM_POWER);\n\t\t\t\t_currentCamera = sibr::InputCamera(newcam, _currentCamera.w(), _currentCamera.h());\n\t\t\t}\n\n\t\t}\n\n\t\t// Note this call has three modes: record (only read the arg camera) | playback (overwrite the arg camera) | do nothing (do nothing)\n\t\t_cameraRecorder.use(_currentCamera);\n\n\t\t_previousCamera = _currentCamera;\n\t\t_clippingPlanes[0] = _currentCamera.znear();\n\t\t_clippingPlanes[1] = _currentCamera.zfar();\n\t}\n\n\tconst sibr::InputCamera& InteractiveCameraHandler::getCamera(void) const {\n\t\treturn _currentCamera;\n\t}\n\n\tvoid InteractiveCameraHandler::onRender(const sibr::Viewport& viewport) {\n\t\tif (_currentMode == TRACKBALL) {\n\t\t\t_trackball.onRender(viewport);\n\t\t}\n\t}\n\n\tvoid InteractiveCameraHandler::onGUI(const std::string& suffix) {\n\n\t\tconst std::string fullName = (suffix);\n\n\n\t\t// Saving camera.\n\t\tif (ImGui::Begin(fullName.c_str())) {\n\t\t\t// _currentMode = 4;\n\t\t\tImGui::PushScaledItemWidth(130);\n\t\t\tImGui::Combo(\"Mode\", (int*)&_currentMode, \"FPS\\0Orbit\\0Interp.\\0Trackball\\0None\\0\\0\");\n\t\t\tswitchMode(_currentMode);\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::Button(\"Load camera\")) {\n\t\t\t\tstd::string selectedFile;\n\t\t\t\tif (sibr::showFilePicker(selectedFile, Default)) {\n\t\t\t\t\tif (!selectedFile.empty()) {\n\t\t\t\t\t\tsibr::InputCamera savedCam;\n\t\t\t\t\t\tsavedCam.loadFromBinary(selectedFile);\n\t\t\t\t\t\tSIBR_LOG << \"Loaded saved camera (\" << selectedFile << \").\" << std::endl;\n\t\t\t\t\t\tfromCamera(savedCam, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::Button(\"Save camera (bin)\")) {\n\t\t\t\tstd::string selectedFile;\n\t\t\t\tif (sibr::showFilePicker(selectedFile, Save)) {\n\t\t\t\t\tif (!selectedFile.empty()) {\n\t\t\t\t\t\tif (selectedFile[selectedFile.size() - 1] == '/' || selectedFile[selectedFile.size() - 1] == '\\\\') {\n\t\t\t\t\t\t\tselectedFile.append(\"default_camera.bin\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_currentCamera.saveToBinary(selectedFile);\n\t\t\t\t\t\tSIBR_LOG << \"Saved camera (\" << selectedFile << \").\" << std::endl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tImGui::Separator();\n\t\t\tif (ImGui::Button(\"Snap to closest\")) {\n\t\t\t\t_currentCamId = findNearestCamera(_interpPath);\n\t\t\t\tsnapToCamera(_currentCamId);\n\t\t\t}\n\n\t\t\tImGui::SameLine();\n\t\t\tImGui::Checkbox(\"Altitude interp\", &_altitudeInterp);\n\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::InputInt(\"Snap to\", &_currentCamId, 1, 10)) {\n\t\t\t\t_currentCamId = sibr::clamp(_currentCamId, 0, int(_interpPath.size()) - 1);\n\t\t\t\tsnapToCamera(_currentCamId);\n\t\t\t}\n\n\t\t\tif (_currentMode == TRACKBALL) {\n\t\t\t\tImGui::SameLine();\n\t\t\t\tImGui::Checkbox(\"Show trackball\", &_trackball.drawThis);\n\t\t\t}\n\n\t\t\tif (ImGui::InputFloat(\"Fov Y\", &_cameraFovDeg, 1.0f, 5.0f)) {\n\t\t\t\t_cameraFovDeg = sibr::clamp(_cameraFovDeg, 1.0f, 180.0f);\n\t\t\t\t_currentCamera.fovy(_cameraFovDeg * float(M_PI) / 180.0f);\n\t\t\t\t// Synchronize internal cameras.\n\t\t\t\tfromCamera(_currentCamera, _shouldSmooth);\n\t\t\t}\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::InputFloat(\"Near\", &_clippingPlanes[0], 1.0f, 10.0f)) {\n\t\t\t\t_currentCamera.znear(_clippingPlanes[0]);\n\t\t\t\tfromCamera(_currentCamera);\n\t\t\t}\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::InputFloat(\"Far\", &_clippingPlanes[1], 1.0f, 10.0f)) {\n\t\t\t\t_currentCamera.zfar(_clippingPlanes[1]);\n\t\t\t\tfromCamera(_currentCamera);\n\t\t\t}\n\n\t\t\tImGui::Separator();\n\t\t\tImGui::PopItemWidth();\n\n\t\t\t// Record camera keypoints.\n\t\t\tImGui::Text(\"Key cameras: %d\", _keyCameras.size());\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::Button(\"Add key\")) {\n\t\t\t\t_keyCameras.emplace_back(new InputCamera(getCamera()));\n\t\t\t}\n\t\t\tImGui::SameLine();\n\n\t\t\tif (!_keyCameras.empty()) {\n\t\t\t\tif (ImGui::Button(\"Remove key\")) {\n\t\t\t\t\t_keyCameras.pop_back();\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t}\n\n\t\t\tif (ImGui::Button(\"Save key cameras...\")) {\n\t\t\t\tstd::string outpath;\n\t\t\t\tif (sibr::showFilePicker(outpath, Save, \"\", \"lookat\") && !outpath.empty()) {\n\t\t\t\t\tInputCamera::saveAsLookat(_keyCameras, outpath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tImGui::Separator();\n\t\t}\n\t\tImGui::End();\n\n\t\t// Recording handling.\n\t\tif (_supportRecording) {\n\t\t\tstd::string selectedFile;\n\n\t\t\tif (ImGui::Begin(fullName.c_str())) {\n\t\t\t\tImGui::PushScaledItemWidth(130);\n\n\t\t\t\tif (ImGui::Button(\"Play\")) {\n\t\t\t\t\t_cameraRecorder.playback();\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif (ImGui::Button(\"Play (No Interp)\")) {\n\t\t\t\t\t_cameraRecorder.playback();\n\t\t\t\t\t_cameraRecorder.playNoInterpolation(true);\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif (ImGui::Button(\"Record\")) {\n\t\t\t\t\t_cameraRecorder.reset();\n\t\t\t\t\t_cameraRecorder.record();\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif (ImGui::Button(\"Stop\")) {\n\t\t\t\t\t_cameraRecorder.stop();\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif(ImGui::InputFloat(\"Speed##CamRecorder\", &_cameraRecorder.speed(), 0.1f)) {\n\t\t\t\t\t_cameraRecorder.speed() = sibr::clamp(_cameraRecorder.speed(), 0.0f, 1.0f);\n\t\t\t\t}\n\n\t\t\t\tif (ImGui::Button(\"Load path\")) {\n\t\t\t\t\tif (sibr::showFilePicker(selectedFile, Default)) {\n\t\t\t\t\t\tif (!selectedFile.empty()) {\n\t\t\t\t\t\t\tSIBR_LOG << \"Loading\" << std::endl;\n\t\t\t\t\t\t\t_cameraRecorder.reset();\n\t\t\t\t\t\t\tif (boost::filesystem::extension(selectedFile) == \".out\")\n\t\t\t\t\t\t\t\t_cameraRecorder.loadBundle(selectedFile, _currentCamera.w(), _currentCamera.h());\n\t\t\t\t\t\t\telse if (boost::filesystem::extension(selectedFile) == \".lookat\")\n\t\t\t\t\t\t\t\t_cameraRecorder.loadLookat(selectedFile, _currentCamera.w(), _currentCamera.h());\n\t\t\t\t\t\t\telse if (boost::filesystem::extension(selectedFile) == \".txt\")\n\t\t\t\t\t\t\t\t_cameraRecorder.loadColmap(selectedFile, _currentCamera.w(), _currentCamera.h());\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t_cameraRecorder.load(selectedFile);\n// dont play back until explicitly requested \n//\t\t\t\t\t\t\t_cameraRecorder.playback();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif (ImGui::Button(\"Save path\")) {\n\t\t\t\t\t_cameraRecorder.stop();\n\t\t\t\t\tif (sibr::showFilePicker(selectedFile, Save)) {\n\t\t\t\t\t\tif (!selectedFile.empty()) {\n\t\t\t\t\t\t\tSIBR_LOG << \"Saving\" << std::endl;\n\t\t\t\t\t\t\t// std::cout << \" save !!!\" << std::endl;\n\t\t\t\t\t\t\t_cameraRecorder.save(selectedFile + \".path\");\n\t\t\t\t\t\t\t_cameraRecorder.saveAsBundle(selectedFile + \".out\", _currentCamera.h());\n\t\t\t\t\t\t\t_cameraRecorder.saveAsColmap(selectedFile, _currentCamera.h(), _currentCamera.w());\n\t\t\t\t\t\t\t_cameraRecorder.saveAsLookAt(selectedFile + \".lookat\");\n\t\t\t\t\t\t\tif (_fribrExport) {\n\t\t\t\t\t\t\t\tconst int height = int(std::floor(1920.0f / _currentCamera.aspect()));\n\t\t\t\t\t\t\t\t_cameraRecorder.saveAsFRIBRBundle(selectedFile + \"_fribr/\", 1920, height);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//ImGui::SameLine();\n\t\t\t\tImGui::Checkbox(\"Save video (from playing)\", (&_saveFrame));\n\t\t\t\tif (_saveFrame) {\n\t\t\t\t\t_cameraRecorder.savingVideo(_saveFrame);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tImGui::SameLine();\n\t\t\t\tconst bool saveFrameOld = _saveFrameVideo;\n\t\t\t\tImGui::Checkbox(\"Save frames (from playing)\", (&_saveFrameVideo));\n\t\t\t\tif (_saveFrameVideo && !saveFrameOld) {\n\t\t\t\t\tif (sibr::showFilePicker(selectedFile, Directory)) {\n\t\t\t\t\t\tif (!selectedFile.empty()) {\n\t\t\t\t\t\t\t_cameraRecorder.saving(selectedFile + \"/\");\n\t\t\t\t\t\t\t_cameraRecorder.savingVideo(_saveFrameVideo);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t_cameraRecorder.stopSaving();\n\t\t\t\t\t\t\t_saveFrameVideo = false;\n\t\t\t\t\t\t\t_cameraRecorder.savingVideo(_saveFrameVideo);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!_saveFrameVideo && saveFrameOld) {\n\t\t\t\t\t_cameraRecorder.stopSaving();\n\t\t\t\t\t_cameraRecorder.savingVideo(_saveFrameVideo);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//ImGui::SameLine();\n\t\t\t\t//ImGui::Checkbox(\"Fribr export\", &_fribrExport);\n\t\t\t\tImGui::Separator();\n\t\t\t\tImGui::PopItemWidth();\n\t\t\t}\n\t\t\tImGui::End();\n\t\t}\n\t\t// add the FPS camera controls in the same ImGui window.\n\t\t_fpsCamera.onGUI(suffix);\n\t\t\n\n\t}\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/InteractiveCameraHandler.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include <memory>\r\n#include <fstream>\r\n\r\n#include \"Config.hpp\"\r\n#include \"core/graphics/Shader.hpp\"\r\n#include \"core/assets/InputCamera.hpp\"\r\n\r\n#include \"core/view/IBRBasicUtils.hpp\"\r\n#include \"core/view/FPSCamera.hpp\"\r\n#include \"core/view/Orbit.hpp\"\r\n#include \"core/view/TrackBall.h\"\r\n#include \"core/assets/CameraRecorder.hpp\"\r\n#include \"core/graphics/Viewport.hpp\"\r\n#include \"core/graphics/Mesh.hpp\"\r\n#include \"ICameraHandler.hpp\"\r\n\r\nnamespace sibr {\r\n\tclass Mesh;\r\n\tclass Input;\r\n\tclass Raycaster;\r\n\r\n\t/**\r\n\t\tThe InteractiveCameraHandler gathers various types of camera interactions and\r\n\t\tallows the user to switch between them, keeping them in sync.\r\n\t\tIt can also perform camera interpolation along a path.\r\n\t\t\\ingroup sibr_view\r\n\t*/\r\n\tclass SIBR_VIEW_EXPORT InteractiveCameraHandler : public ICameraHandler\r\n\t{\r\n\r\n\tpublic:\r\n\r\n\t\tSIBR_CLASS_PTR(InteractiveCameraHandler);\r\n\r\n\t\t/** Current handler interaction mode. */\r\n\t\tenum InteractionMode {\r\n\t\t\tFPS = 0, ORBIT = 1, INTERPOLATION = 2, TRACKBALL = 3, NONE=4\r\n\t\t};\r\n\r\n\t\t/** Constructor.\r\n\t\t *\\param supportRecording can this handler record camera paths.\r\n\t\t *\\todo Do we really need this option?\r\n\t\t */\r\n\t\tInteractiveCameraHandler(const bool supportRecording = true);\r\n\r\n\t\t/** \\deprecated Resolution is deprecated and will be removed in the near future.\r\n\t\t *\tSee setup(const std::vector<InputCamera::Ptr>&, const sibr::Viewport&, std::shared_ptr<sibr::Raycaster>,...) instead. */\r\n\t\tvoid setup(const std::vector<InputCamera::Ptr> & cams, const sibr::Vector2u & resolution, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster);\r\n\r\n\t\t/** Setup an interactive camera handler from an existing camera.\r\n\t\tThe interactive camera will be initialized at the position of the argument camera.\r\n\t\t\\param cam initialization camera\r\n\t\t\\param viewport the window viewport\r\n\t\t\\param raycaster raycaster containing the mesh displayed (used for the trackball centering), can be nullptr\r\n\t\t*/\r\n\t\tvoid setup(const sibr::InputCamera & cam, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster);\r\n\r\n\t\t/** Setup an interactive camera handler from an area of interest.\r\n\t\tThe interactive camera will be initialized so that the area is completely visible.\r\n\t\t\\param areaOfInterest the region of space to show\r\n\t\t\\param viewport the window viewport\r\n\t\t\\param raycaster raycaster containing the mesh displayed (used for the trackball centering), can be nullptr\r\n\t\t*/\r\n\t\tvoid setup(const Eigen::AlignedBox<float, 3>& areaOfInterest, const sibr::Viewport & viewport, const std::shared_ptr<sibr::Raycaster> raycaster);\r\n\r\n\t\t/** Setup an interactive camera handler from a series of existing cameras and mesh. \r\n\t\tThe interactive camera will be initialized at the position of the first camera from the list.\r\n\t\t\\param cams a list of cameras (used for interpolation path)\r\n\t\t\\param viewport the window viewport\r\n\t\t\\param raycaster raycaster containing the mesh displayed (used for the trackball centering), can be nullptr\r\n\t\t\\param clippingPlanes optional clipping planes to enforce\r\n\t\t*/\r\n\t\tvoid setup(const std::vector<InputCamera::Ptr>& cams, const sibr::Viewport& viewport, std::shared_ptr<sibr::Raycaster> raycaster, const sibr::Vector2f & clippingPlanes = {-1.0f,-1.0f});\r\n\r\n\t\t/** Setup an interactive camera handler from a mesh.\r\n\t\tThe interactive camera will be initialized so that the mesh is completely visible.\r\n\t\t\\param mesh the mesh to display\r\n\t\t\\param viewport the window viewport\r\n\t\t\\note a raycaster will be set up internally\r\n\t\t*/\r\n\t\tvoid setup(std::shared_ptr<sibr::Mesh> mesh, const sibr::Viewport& viewport);\r\n\r\n\t\t/** Setup a camera path for the interpolation mode. \r\n\t\t * \\param cameras to interpolate along\r\n\t\t */\r\n\t\tvoid setupInterpolationPath(const std::vector<InputCamera::Ptr> & cameras);\r\n\r\n\t\t/** Move the interactive camera to a new position and change its internal parameters.\r\n\t\t\\param cam the cameras the parameters and pose should be copied from\r\n\t\t\\param interpolate smooth interpolation between the current pose and the new one\r\n\t\t\\param updateResolution should the resolution of the camera be updated or not. Can be disabled if the new cam has a size incompatible with the current viewport.\r\n\t\t*/\r\n\t\tvoid fromCamera(const sibr::InputCamera & cam, bool interpolate = true, bool updateResolution = true);\r\n\t\t\r\n\t\t/** Move the interactive camera to a new position.\r\n\t\t\\param transform the transform the orientation and pose should be copied from\r\n\t\t\\param interpolate smooth interpolation between the current pose and the new one\r\n\t\t\\param updateResolution should the resolution of the camera be updated or not. Can be disabled if the new cam has a size incompatible with the current viewport.\r\n\t\t*/\r\n\t\tvoid fromTransform(const Transform3f & transform, bool interpolate = true, bool updateResolution = true);\r\n\r\n\t\t/** Set the clipping planes.\r\n\t\t *\\param znear near plane\r\n\t\t *\\param zfar far plane\r\n\t\t */\r\n\t\tvoid setClippingPlanes(float znear, float zfar);\r\n\r\n\t\t/** Find the camera in a list closest to the current interactive camera position\r\n\t\t\\param inputCameras the list to search in\r\n\t\t\\return the index of the closest camera in the list, or -1\r\n\t\t\\note This function ignores cameras that are not 'active' in the list.\r\n\t\t*/\r\n\t\tint\tfindNearestCamera(const std::vector<InputCamera::Ptr>& inputCameras, const bool& useRotation = true) const;\r\n\r\n\t\t/** Toggle camera motion smoothing. */\r\n\t\tvoid switchSmoothing() { _shouldSmooth = !_shouldSmooth; SIBR_LOG << \"Smoothing \" << (_shouldSmooth ? \"enabled\" : \"disabled\") << std::endl; }\r\n\r\n\t\t/** Toggle automatic snapping when getting close to a camera from the interpolation path. */\r\n\t\tvoid switchSnapping() { _shouldSnap = !_shouldSnap; SIBR_LOG << \"Snapping \" << (_shouldSnap ? \"enabled\" : \"disabled\") << std::endl; }\r\n\r\n\t\t/** Switch the interaction mode (trackball, fps,...). \r\n\t\t\t\\param mode the new mode\r\n\t\t*/\r\n\t\tvoid switchMode(const InteractionMode mode);\r\n\r\n\t\t/** Save the current camera as a binary file to a standard location.\r\n\t\t * \\param datasetPath destination directory\r\n\t\t * \\note \"default_camera.bin\" will be appended to the path.\r\n\t\t */\r\n\t\tvoid saveDefaultCamera(const std::string& datasetPath);\r\n\r\n\t\t/** Load a camera parameters from a binary file at a standard location.\r\n\t\t *\\param cam the camera to use if loading fails\r\n\t\t * \\param datasetPath source directory\r\n\t\t * \\note \"default_camera.bin\" will be appended to the path.\r\n\t\t */\r\n\t\tvoid loadDefaultCamera(const sibr::InputCamera& cam, const std::string& datasetPath);\r\n\r\n\t\t/** \\return the current interaction mode. */\r\n\t\tInteractionMode getMode() const { return _currentMode; }\r\n\r\n\t\t/** Set the speed of the FPS camera.\r\n\t\t\\param speed the new speed\r\n\t\t*/\r\n\t\tvoid setFPSCameraSpeed(const float speed);\r\n\r\n\t\t/// ICameraHandler interface.\r\n\t\t/** Update function, call at every tick.\r\n\t\t\\param input the input object for the current view.\r\n\t\t\\param deltaTime time elapsed since last frame\r\n\t\t\\param viewport optional window viewport (can be used by the trackball for instance)\r\n\t\t*/\r\n\t\tvirtual void update(const sibr::Input & input, float deltaTime, const sibr::Viewport & viewport = Viewport(0.0f, 0.0f, 0.0f, 0.0f)) override;\r\n\r\n\t\t/** \\return the current camera. */\r\n\t\tvirtual const sibr::InputCamera & getCamera(void) const override;\r\n\r\n\t\t/** Render additional information on screen (trackball gizmo).\r\n\t\t\\param viewport the window viewport\r\n\t\t*/\r\n\t\tvirtual void onRender(const sibr::Viewport & viewport) override;\r\n\r\n\t\t/** Show the GUI. \r\n\t\t\\param suffix additional GUI name suffix to avoid collisions when having multiple handlers. \r\n\t\t*/\r\n\t\tvirtual void onGUI(const std::string & suffix) override;\r\n\r\n\t\t/** \\return the camera recorder */\r\n\t\tsibr::CameraRecorder & getCameraRecorder() { return _cameraRecorder; };\r\n\t\t\r\n\t\t/** \\return the camera trackball */\r\n\t\tsibr::TrackBall & getTrackball() { return _trackball; }\r\n\r\n\t\t/** Snap the interactive camera to one of the interpolation path cameras.\r\n\t\t\\param id the index of the camera to snap to. if -1, the closest camera. \r\n\t\t*/\r\n\t\tvoid snapToCamera(int id = -1);\r\n\r\n\t\t/** \\return the handler raycaster.\r\n\t\t\t\\warning Can be nullptr \r\n\t\t*/\r\n\t\tstd::shared_ptr<Raycaster> & getRaycaster() { return _raycaster; }\r\n\r\n\t\t/** \\return true if the handler has been entirely setup */\r\n\t\tbool isSetup() const { return _isSetup; }\r\n\r\n\t\t/** \\return the handler viewport */\r\n\t\tconst sibr::Viewport & getViewport() const { return _viewport; }\r\n\r\n\t\t/** \\return radius used for trackball*/\r\n\t\tfloat & getRadius() { return _radius; }\r\n\r\n\t\tfloat getInterpolatedHeight(const std::vector<InputCamera::Ptr>& inputCameras);\r\n\r\n\tprivate:\r\n\r\n\t\tint _currentCamId; ///< Current snapped camera ID.\r\n\r\n\t\tbool _shouldSmooth; ///< Motion smoothing.\r\n\t\tbool _shouldSnap; ///< Currently snapping.\r\n\t\tbool _altitudeInterp = false; ///< Interpolate altitude on the cameras height or not.\r\n\r\n\t\tfloat _bottom_vp = 1.f;\r\n\r\n\t\tsibr::FPSCamera _fpsCamera; ///< FPS handler.\r\n\t\tsibr::Orbit _orbit; ///< Orbit handler.\r\n\t\tsibr::TrackBall _trackball; ///< Trackball handler.\r\n\r\n\t\tInteractionMode _currentMode; ///< Current handler mode.\r\n\r\n\t\tfloat _radius; ///< Trackball radius property (for GUI).\r\n\r\n\t\tstd::shared_ptr<sibr::Raycaster> _raycaster; ///< Raycaster (for trackball).\r\n\t\tsibr::Viewport _viewport;  ///< Current viewport.\r\n\r\n\t\tsibr::InputCamera _previousCamera; ///< Previous camera (for interpolation).\r\n\t\tsibr::InputCamera _currentCamera; ///< Current camera.\r\n\r\n\t\t/// Parameters for path interpolation.\r\n\t\tuint _startCam; ///< Start camera index in the list.\r\n\t\tuint _interpFactor; ///< Current interpolation factor between cam _startCam and _startCam+1.\r\n\t\tstd::vector<InputCamera::Ptr> _interpPath; ///< Cameras along the path.\r\n\r\n\t\tsibr::CameraRecorder _cameraRecorder; ///< Camera recorder.\r\n\t\tbool _supportRecording; ///< Does the camera support recording (uneeded).\r\n\t\tstd::vector<InputCamera::Ptr> _keyCameras; ///< Key cameras saved punctually. \\note This could be merged with the camera recorder.\r\n\r\n\t\tsibr::Vector2f _clippingPlanes; ///< Clipping planes parameter (for GUI).\r\n\t\tbool _saveFrame; ///< Should the frame be saved as an image.\r\n\t\tbool _saveFrameVideo; ///< Should the frame be saved as part of a video.\r\n\t\tbool _triggerCameraUpdate; ///< Should the camera be updated (delayed if info is missing).\r\n\t\tbool _isSetup; ///< Is the handler setup.\r\n\t\tfloat _cameraFovDeg = 0.0f; ///< Camera field of view in degrees (for GUI).\r\n\t\tbool _fribrExport = false; ///< Switch to FRIBR compatible export mode for paths.\r\n\r\n\t\t/** Interpolate along the path. */\r\n\t\tvoid interpolate();\r\n\r\n\t};\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/MultiMeshManager.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"MultiMeshManager.hpp\"\n\n#include <imgui/imgui.h>\n\nnamespace sibr {\n\n\tMeshData MeshData::dummy = MeshData(\"dummy\", Mesh::Ptr(), DUMMY, Mesh::FillRenderMode);\n\n\tMeshData::MeshData(const std::string & _name, Mesh::Ptr mesh_ptr, MeshType mType, Mesh::RenderMode render_mode) :\n\t\tmeshPtr(mesh_ptr), renderMode(render_mode), name(_name), meshType(mType)\n\t{\n\t\tif (mType == POINTS) {\n\t\t\trenderMode = Mesh::PointRenderMode;\n\t\t}\n\t\tif (mType == LINES && renderMode == Mesh::FillRenderMode) {\n\t\t\trenderMode = Mesh::LineRenderMode;\n\t\t}\n\t\tif (renderMode != Mesh::FillRenderMode) {\n\t\t\tbackFaceCulling = false;\n\t\t}\n\t}\n\n\tMeshData MeshData::getNormalsMeshData() const\n\t{\n\t\tMeshData data(name + \"_normals\", meshPtr, normalMode == PER_TRIANGLE ? TRIANGLES : POINTS);\n\t\tdata.setColor(normalsColor).setDepthTest(depthTest);\n\t\tdata.normalsLength = (normalsInverted ? -normalsLength : normalsLength);\n\t\treturn data;\n\t}\n\n\tMeshData::operator bool() const\n\t{\n\t\treturn meshType != DUMMY;\n\t}\n\n\tvoid MeshData::renderGeometry() const\n\t{\n\t\tCHECK_GL_ERROR;\n\t\tif (!meshPtr) {\n\t\t\treturn;\n\t\t}\n\t\tif (renderMode == Mesh::PointRenderMode) {\n\t\t\tmeshPtr->render_points(depthTest);\n\t\t} else {\n\t\t\tmeshPtr->render(depthTest, backFaceCulling, renderMode, frontFaceCulling, invertDepthTest);\n\t\t}\n\t\tCHECK_GL_ERROR;\n\t}\n\n\tvoid MeshData::onGUI(const std::string & name)\n\t{\n\t\t// rendering mode\n\t\tstatic const std::string renderModeStrs[3] = { \"Points\",  \"Lines\",  \"Fill\" };\n\n\t\tif (ImGui::BeginCombo((\"##render_mode_\" + name).c_str(), renderModeStrs[(int)renderMode].data())) {\n\t\t\tfor (int t = (int)meshType; t >= 0; --t) {\n\t\t\t\tif (ImGui::Selectable(renderModeStrs[t].data(), t == (int)renderMode)) {\n\t\t\t\t\trenderMode = (Mesh::RenderMode)t;\n\t\t\t\t}\t\t\t\n\t\t\t}\t\n\t\t\tImGui::EndCombo();\n\t\t}\n\t\tImGui::NextColumn();\n\n\t\t//alpha\n\t\tImGui::SliderFloat((\"##alpha_\" + name).c_str(), &alpha, 0, 1);\n\t\tImGui::NextColumn();\n\n\t\t//user color\n\t\tstatic const std::string colorModeStrs[2] = { \"User-defined\",  \"Vertex\" };\n\t\tif (ImGui::BeginCombo((\"##color_mode_\" + name).c_str(), colorModeStrs[(int)colorMode].data())) {\t\n\t\t\tif (meshPtr && meshPtr->hasColors()) {\n\t\t\t\tif (ImGui::Selectable(colorModeStrs[VERTEX].data(), colorMode == VERTEX)) {\n\t\t\t\t\tcolorMode = VERTEX;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ImGui::Selectable(colorModeStrs[USER_DEFINED].data(), colorMode == USER_DEFINED)) {\n\t\t\t\tcolorMode = USER_DEFINED;\n\t\t\t}\n\t\t\tImGui::EndCombo();\n\t\t}\n\t\tif (colorMode == USER_DEFINED) {\n\t\t\tImGui::SameLine();\n\t\t\tImGui::ColorEdit3((\"##color_picker_\" + name).c_str(), &userColor[0], ImGuiColorEditFlags_NoInputs);\n\t\t} \n\t\tImGui::NextColumn();\n\n\t\t//rendering options\n\t\tif (ImGui::ArrowButton((\"##OptionsArrow\" + name).c_str(), ImGuiDir_Down)) {\n\t\t\tImGui::OpenPopup((\"##Options_popup_\" + name).c_str());\n\t\t}\t\n\t\tif (ImGui::BeginPopup((\"##Options_popup_\" + name).c_str())) {\n\t\t\tImGui::Checkbox((\"Depth Test##\" + name).c_str(), &depthTest);\n\t\t\tif (meshType == TRIANGLES) {\n\t\t\t\tImGui::Checkbox((\"Cull faces##\" + name).c_str(), &backFaceCulling);\n\t\t\t\tImGui::Checkbox((\"Swap back/front##\" + name).c_str(), &frontFaceCulling);\n\t\t\t}\n\t\t\tif (renderMode == Mesh::PointRenderMode) {\n\t\t\t\tImGui::PushItemWidth(75);\n\t\t\t\tImGui::SliderInt((\"PointSize##\" + name).c_str(), &radius, 1, 50);\n\t\t\t\tImGui::PopItemWidth();\n\t\t\t}\n\t\t\tif (meshType == TRIANGLES) {\n\t\t\t\tImGui::Separator();\n\t\t\t\tImGui::Checkbox((\"ShowNormals##\" + name).c_str(), &showNormals);\n\t\t\t\tif (showNormals) {\t\t\n\t\t\t\t\tstatic const std::string normalModeStrs[2] = { \"Per-triangle\", \"Per-vertex\"};\n\t\t\t\t\tif (ImGui::BeginCombo((\"##normal_mode_\" + name).c_str(), normalModeStrs[(int)normalMode].data())) {\n\t\t\t\t\t\tif (ImGui::Selectable(normalModeStrs[PER_TRIANGLE].data(), normalMode == PER_TRIANGLE)) {\n\t\t\t\t\t\t\tnormalMode = PER_TRIANGLE;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (meshPtr && meshPtr->hasNormals()) {\n\t\t\t\t\t\t\tif (ImGui::Selectable(normalModeStrs[PER_VERTEX].data(), normalMode == PER_VERTEX)) {\n\t\t\t\t\t\t\t\tnormalMode = PER_VERTEX;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tImGui::EndCombo();\n\t\t\t\t\t}\n\t\t\t\t\tImGui::Checkbox((\"NormalInverted##\" + name).c_str(), &normalsInverted);\n\t\t\t\t\tImGui::PushItemWidth(90);\n\t\t\t\t\tImGui::SliderFloat((\"NormalSize##\" + name).c_str(), &normalsLength, 0.001f, 10.0f, \"%.3f\", 3.0f);\n\t\t\t\t\tImGui::PopItemWidth();\n\t\t\t\t\tImGui::ColorEdit3((\"NormalsColor##color_picker_\" + name).c_str(), &normalsColor[0], ImGuiColorEditFlags_NoInputs);\n\t\t\t\t}\n\t\t\t\tif (!meshPtr->hasNormals()) {\n\t\t\t\t\tif (ImGui::Button((\"Compute Normals##\" + name).c_str()) ){\n\t\t\t\t\t\tmeshPtr->generateNormals();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tImGui::Checkbox((\"Phong shading##\" + name).c_str(), &phongShading);\n\t\t\t\t}\n\t\t\t\tImGui::Separator();\n\t\t\t}\n\t\t\tImGui::EndPopup();\n\t\t}\n\t\tImGui::NextColumn();\n\t}\n\n\tstd::string MeshData::getInfos() const\n\t{\n\t\tif (!meshPtr) {\n\t\t\treturn \"no mesh\";\n\t\t}\n\n\t\tstd::stringstream s;\n\t\ts << meshPtr->vertices().size() << \" vertices \\n\" <<\n\t\t\tmeshPtr->triangles().size() << \" triangles \\n\" <<\n\t\t\t\"hasNormals() : \" << meshPtr->hasNormals() << \"\\n\" <<\n\t\t\t\"hasColors() : \" << meshPtr->hasColors() << \"\\n\" <<\n\t\t\t\"hasTexCoords() : \" << meshPtr->hasTexCoords() << \"\\n\"\n\t\t\t;\n\n\t\treturn s.str();\n\t}\n\n\tMeshData & MeshData::setColor(const Vector3f & col)\n\t{\n\t\tuserColor = col;\n\t\treturn *this;\n\t}\n\n\tMeshData & MeshData::setBackFace(bool backface)\n\t{\n\t\tbackFaceCulling = backface;\n\t\treturn *this;\n\t}\n\n\tMeshData & MeshData::setDepthTest(bool depth_test)\n\t{\n\t\tdepthTest = depth_test;\n\t\treturn *this;\n\t}\n\n\tMeshData & MeshData::setColorRandom()\n\t{\n\t\tstatic const auto baseHash = [](uint p) {\n\t\t\tp = 1103515245U * ((p >> 1U) ^ (p));\n\t\t\tuint h32 = 1103515245U * ((p) ^ (p >> 3U));\n\t\t\treturn h32 ^ (h32 >> 16);\n\t\t};\n\n\t\tstatic const uint mask = 0x7fffffffU;\n\n\t\tstatic int seed_x = 0;\n\t\t\n\t\t++seed_x;\n\t\t\n\t\tuint n = baseHash(uint(seed_x));\n\t\tVector3u tmp = Vector3u(n, n * 16807U, n * 48271U);\n\t\tfor (int c = 0; c < 3; ++c) {\n\t\t\tuserColor[c] = (tmp[c] & mask) / float(0x7fffffff);\n \t\t}\n\t\t \n\t\t return *this;\n\t}\n\n\tMeshData & MeshData::setRadiusPoint(int rad)\n\t{\n\t\tradius = rad;\n\t\treturn *this;\n\t}\n\n\tMeshData& MeshData::setScale(float s)\n\t{\n\t\t// TODO: insérer une instruction return ici\n\t\tscale = s;\n\t\treturn *this;\n\t}\n\n\tMeshData& MeshData::setTransformation(const sibr::Matrix4f& tr)\n\t{\n\t\t// TODO: insérer une instruction return ici\n\t\ttransformation = tr;\n\t\treturn *this;\n\t}\n\n\tMeshData & MeshData::setAlpha(float _alpha) {\n\t\talpha = _alpha;\n\t\treturn *this;\n\t}\n\n\tMeshData & MeshData::setColorMode(ColorMode mode)\n\t{\n\t\tcolorMode = mode;\n\t\treturn *this;\n\t}\n\n\tvoid ShaderAlphaMVP::initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom)\n\t{\n\t\tshader.init(name, vert, frag, geom);\n\t\tmvp.init(shader, \"mvp\");\t\n\t\talpha.init(shader, \"alpha\");\n\t}\n\n\tvoid ShaderAlphaMVP::setUniforms(const Camera & eye, const MeshData & data)\n\t{\n\t\tmvp.set(eye.viewproj()*data.transformation);\n\t\talpha.set(data.alpha);\n\t}\n\n\tvoid ShaderAlphaMVP::render(const Camera & eye, const MeshData & data)\n\t{\n\t\tshader.begin();\n\n\t\tsetUniforms(eye, data);\n\n\t\tdata.renderGeometry();\n\n\t\tshader.end();\n\t}\n\n\tvoid ColorMeshShader::initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom)\n\t{\n\t\tShaderAlphaMVP::initShader(name, vert, frag, geom);\n\t\tuser_color.init(shader, \"user_color\");\n\t}\n\n\tvoid ColorMeshShader::setUniforms(const Camera & eye, const MeshData & data)\n\t{\n\t\tShaderAlphaMVP::setUniforms(eye, data);\n\t\tuser_color.set(data.userColor);\n\t}\n\n\tvoid PointShader::initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom)\n\t{\n\t\tColorMeshShader::initShader(name, vert, frag, geom);\n\t\tradius.init(shader, \"radius\");\n\t}\n\n\tvoid PointShader::setUniforms(const Camera & eye, const MeshData & data)\n\t{\n\t\tColorMeshShader::setUniforms(eye, data);\n\t\tradius.set(data.radius);\n\t}\n\n\tvoid PointShader::render(const Camera & eye, const MeshData & data)\n\t{\n\t\tglEnable(GL_PROGRAM_POINT_SIZE);\n\t\tColorMeshShader::render(eye, data);\n\t\tglDisable(GL_PROGRAM_POINT_SIZE);\n\t}\n\n\tvoid NormalRenderingShader::initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom)\n\t{\n\t\tColorMeshShader::initShader(name, vert, frag, geom);\n\t\tnormals_size.init(shader, \"normals_size\");\n\t}\n\n\tvoid NormalRenderingShader::setUniforms(const Camera & eye, const MeshData & data)\n\t{\n\t\tColorMeshShader::setUniforms(eye, data);\n\t\tnormals_size.set(data.normalsLength);\n\t}\n\n\tvoid MeshShadingShader::initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom)\n\t{\n\t\tColorMeshShader::initShader(name, vert, frag, geom);\n\t\tlight_position.init(shader, \"light_position\");\n\t\tphong_shading.init(shader, \"phong_shading\");\n\t\tuse_mesh_color.init(shader, \"use_mesh_color\");\n\t}\n\n\tvoid MeshShadingShader::setUniforms(const Camera & eye, const MeshData & data)\n\t{\n\t\tColorMeshShader::setUniforms(eye, data);\n\t\tlight_position.set(eye.position());\n\t\tphong_shading.set(data.phongShading);\n\t\tuse_mesh_color.set(data.colorMode == MeshData::ColorMode::VERTEX);\n\t}\n\n\tMultiMeshManager::MultiMeshManager(const std::string & _name) : name(_name)\n\t{\n\t\tinitShaders();\n\n\t\tauto cube = Mesh::getTestCube();\n\t\tTrackBall tb;\n\t\ttb.fromMesh(*cube, Viewport(0,0,1600,1200));\n\t\tcamera_handler.fromCamera(tb.getCamera());\n\n\t\tcamera_handler.switchMode(InteractiveCameraHandler::InteractionMode::TRACKBALL);\n\t}\n\n\tvoid MultiMeshManager::onUpdate(Input & input, const Viewport & vp)\n\t{\n\t\tif (!camera_handler.isSetup() && list_meshes.size() > 0) {\n\t\t\tfor (const auto & mesh_data : list_meshes) {\n\t\t\t\tif (mesh_data.raycaster && mesh_data.meshPtr) {\n\t\t\t\t\tauto bbox = mesh_data.meshPtr->getBoundingBox();\n\t\t\t\t\tif (bbox.volume() > 0) {\n\t\t\t\t\t\tcamera_handler.getRaycaster() = mesh_data.raycaster;\n\t\t\t\t\t\tTrackBall tb;\n\t\t\t\t\t\ttb.fromBoundingBox(bbox, vp);\n\t\t\t\t\t\tcamera_handler.fromCamera(tb.getCamera());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (camera_handler.isSetup() && !camera_handler.getRaycaster()) {\n\t\t\tfor (const auto & mesh : list_meshes) {\n\t\t\t\tif (mesh.raycaster) {\n\t\t\t\t\tcamera_handler.getRaycaster() = mesh.raycaster;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (selected_mesh_it_is_valid) {\n\t\t\tconst auto & mesh = *selected_mesh_it;\t\t\n\t\t\tif( mesh.raycaster) {\n\t\t\t\tcamera_handler.getRaycaster() = mesh.raycaster;\n\t\t\t}\n\t\t}\n\n\t\tcamera_handler.update(input, 1 / 60.0f, vp);\n\t}\n\n\tvoid MultiMeshManager::onRender(const Viewport & viewport)\n\t{\n\t\tglPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, \"Multimesh manager\");\n\n\t\tviewport.clear(backgroundColor);\n\t\tviewport.bind();\n\t\trenderMeshes();\n\t\tcamera_handler.onRender(viewport);\n\n\t\tglPopDebugGroup();\n\t}\n\n\tvoid MultiMeshManager::onRender(IRenderTarget & dst)\n\t{\n\t\tdst.bind();\n\n\t\tconst Viewport vp(0.0f, 0.0f, (float)dst.w(), (float)dst.h());\n\t\tonRender(vp);\n\n\t\tdst.unbind();\n\t}\n\n\tvoid MultiMeshManager::onGUI()\n\t{\n\t\tif (ImGui::Begin(name.c_str())) {\n\t\t\tImGui::Separator();\n\n\t\t\tlist_mesh_onGUI();\n\t\n\t\t}\n\t\tImGui::End();\n\t}\n\n\tvoid MultiMeshManager::removeMesh(const std::string & name)\n\t{\n\t\tfor (auto it = list_meshes.begin(); it != list_meshes.end(); ++it) {\n\t\t\tif (it->name == name) {\n\t\t\t\tlist_meshes.erase(it);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid MultiMeshManager::setIntialView(const std::string& dataset_path)\n\t{\n\t\tconst std::string topViewPath = dataset_path + \"/cameras/topview.txt\";\n\t\tstd::ifstream topViewFile(topViewPath);\n\t\tif (topViewFile.good())\n\t\t{\n\t\t\tSIBR_LOG << \"Loaded saved topview (\" << topViewPath << \").\" << std::endl;\n\t\t\t// Intialize a temp camera (used to load the saved top view pose) with\n\t\t\t// the current top view camera to get the resolution/fov right.\n\t\t\tInputCamera cam(camera_handler.getCamera());\n\t\t\tcam.readFromFile(topViewFile);\n\t\t\t// Apply it to the top view FPS camera.\n\t\t\t//camera_handler.fromCamera(cam, false);\n\t\t\tcamera_handler.fromTransform(cam.transform(), false, true);\n\t\t}\n\t}\n\n\tvoid MultiMeshManager::initShaders()\n\t{\n\t\tconst std::string folder = sibr::getShadersDirectory(\"core\") + \"/\";\n\n\t\tcolored_mesh_shader.initShader(\"colored_mesh_shader\",\n\t\t\tloadFile(folder + \"alpha_colored_mesh.vert\"), \n\t\t\tloadFile(folder + \"alpha_colored_mesh.frag\")\n\t\t);\n\t\tpoints_shader.initShader(\"points_shader\",\n\t\t\tloadFile(folder + \"alpha_points.vert\"),\n\t\t\tloadFile(folder + \"alpha_points.frag\")\n\t\t);\n\t\tper_vertex_normals_shader.initShader(\"per_vertex_normal_shader\",\n\t\t\tloadFile(folder + \"alpha_colored_per_vertex_normals.vert\"),\n\t\t\tloadFile(folder + \"alpha_colored_mesh.frag\"),\n\t\t\tloadFile(folder + \"alpha_colored_per_vertex_normals.geom\")\n\t\t);\n\t\tper_triangle_normals_shader.initShader(\"per_triangle_normal_shader\",\n\t\t\tloadFile(folder + \"alpha_colored_per_triangle_normals.vert\"),\n\t\t\tloadFile(folder + \"alpha_colored_mesh.frag\"),\n\t\t\tloadFile(folder + \"alpha_colored_per_triangle_normals.geom\")\n\t\t);\n\t}\n\n\tvoid MultiMeshManager::renderMeshes()\n\t{\n\t\tglEnable(GL_BLEND);\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\tglBlendEquation(GL_FUNC_ADD);\n\n\t\tfor (const auto & mesh_data : list_meshes) {\n\t\t\tif (!mesh_data.active) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (mesh_data.renderMode == Mesh::PointRenderMode) {\n\t\t\t\tpoints_shader.render(camera_handler.getCamera(), mesh_data);\n\t\t\t} else {\n\t\t\t\tcolored_mesh_shader.render(camera_handler.getCamera(), mesh_data);\n\t\t\t}\n\n\t\t\tif (mesh_data.showNormals) {\n\t\t\t\tif (mesh_data.normalMode == MeshData::PER_VERTEX ) {\n\t\t\t\t\tper_vertex_normals_shader.render(camera_handler.getCamera(), mesh_data.getNormalsMeshData());\n\t\t\t\t} else {\n\t\t\t\t\tper_triangle_normals_shader.render(camera_handler.getCamera(), mesh_data.getNormalsMeshData());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\tglDisable(GL_BLEND);\n\t}\n\n\tvoid MultiMeshManager::list_mesh_onGUI()\n\t{\n\t\tIterator swap_it_src, swap_it_dst;\n\t\tbool do_swap = false;\n\t\tstatic int num_swap = 1;\n\t\t\n\t\tif (ImGui::CollapsingHeader((\"Meshes list##\" + name).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {\n\n\t\t\tstatic char loaded_mesh_str[128] = \"\";\n\t\t\tstatic std::string loaded_mesh_path;\n\t\t\tstatic int loaded_mesh_counter = 0;\n\n\t\t\tif(ImGui::Button(\"load Mesh##MeshesList\") && showFilePicker(loaded_mesh_path, FilePickerMode::Default, \"\", \"obj,ply\")) {\n\t\t\t\tMesh::Ptr mesh = std::make_shared<Mesh>();\t\t\t\n\n\t\t\t\tif (mesh->load(loaded_mesh_path)) {\t\t\t\n\t\t\t\t\tPath mesh_path = loaded_mesh_path;\t\t\t\n\t\t\t\t\tstd::string mesh_name = loaded_mesh_str;\n\t\t\t\t\tmesh_name = (mesh_name == \"\") ? mesh_path.stem().string() : loaded_mesh_str;\n\n\t\t\t\t\tfor (const auto & mesh_it : list_meshes) {\n\t\t\t\t\t\tif (mesh_name == mesh_it.name) {\n\t\t\t\t\t\t\tmesh_name += std::to_string(loaded_mesh_counter);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\taddMesh(mesh_name, mesh);\n\t\t\t\t\t++loaded_mesh_counter;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tImGui::SameLine();\n\t\t\tImGui::InputText(\"mesh name##MeshesList\", loaded_mesh_str, IM_ARRAYSIZE(loaded_mesh_str));\n\n\t\t\t// 0 name | 1 snapto delete | 2 active | 3 rendering mode | 4 alpha | 5 color | 6 Options\n\t\t\tImGui::Columns(7, \"mesh options\");\n\n\t\t\t//ImGui::SetColumnWidth(4, 50);\n\n\t\t\tImGui::Separator();\n\t\t\tif (ImGui::Button(\"Mesh##MeshesList\")) {\n\t\t\t\tlist_meshes.reverse();\n\t\t\t}\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::NextColumn();\n\n\t\t\tif (ImGui::Button(\"Active##MeshesList\")) {\n\t\t\t\tfor (auto & mesh : list_meshes) {\n\t\t\t\t\tmesh.active = !mesh.active;\n\t\t\t\t}\n\t\t\t}\n\t\t\tImGui::SameLine();\n\t\t\tif (ImGui::Button(\"All##MeshesList\")) {\n\t\t\t\tfor (auto & mesh : list_meshes) {\n\t\t\t\t\tmesh.active = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"Mode\");\n\t\t\tImGui::NextColumn();\n\n\t\t\tstatic bool full_alpha = false;\n\t\t\tif (ImGui::Button(\"Alpha##MeshesList\")) {\n\t\t\t\tfor (auto & mesh : list_meshes) {\n\t\t\t\t\tif (mesh.active) {\n\t\t\t\t\t\tmesh.alpha = full_alpha ? 1.0f : 0.0f;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfull_alpha = !full_alpha;\n\t\t\t}\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"Color\"); \n\t\t\tImGui::SameLine();\n\t\t\tImGui::ColorEdit3((\"Background##\" + name).c_str(), &backgroundColor[0], ImGuiColorEditFlags_NoInputs);\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"Options\");\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Separator();\n\n\t\t\tselected_mesh_it_is_valid = false;\n\t\t\tfor (auto mesh_it = list_meshes.begin(); mesh_it != list_meshes.end(); ++mesh_it) {\n\t\t\t\tauto & mesh = *mesh_it;\n\t\t\t\tif (ImGui::Selectable(mesh.name.c_str(), (selected_mesh_it_is_valid && mesh_it == selected_mesh_it))) {\n\t\t\t\t\tselected_mesh_it = mesh_it;\n\t\t\t\t\tselected_mesh_it_is_valid = true;\n\t\t\t\t}\n\t\t\t\tif (ImGui::IsItemActive()) {\n\t\t\t\t\tfloat threshold = ImGui::GetItemRectSize().y + 5.0f;\n\t\t\t\t\tImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);\n\n\t\t\t\t\tif (value_raw.y > threshold * num_swap) {\n\t\t\t\t\t\tswap_it_dst = swap_it_src = mesh_it;\n\t\t\t\t\t\t++swap_it_dst;\n\t\t\t\t\t\tif (swap_it_dst != list_meshes.end()) {\n\t\t\t\t\t\t\tdo_swap = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value_raw.y < -threshold * num_swap) {\n\t\t\t\t\t\tswap_it_dst = swap_it_src = mesh_it;\n\t\t\t\t\t\t--swap_it_dst;\n\t\t\t\t\t\tif (swap_it_src != list_meshes.begin()) {\n\t\t\t\t\t\t\tdo_swap = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (ImGui::IsItemHovered()) {\n\t\t\t\t\tImGui::BeginTooltip();\n\t\t\t\t\tImGui::Text(mesh.getInfos().c_str());\n\t\t\t\t\tImGui::EndTooltip();\n\t\t\t\t}\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tif (ImGui::Button((\"SnapTo##\" + mesh_it->name).c_str()) && mesh_it->meshPtr) {\n\t\t\t\t\tauto box = mesh_it->meshPtr->getBoundingBox();\n\t\t\t\t\tif ((box.diagonal().array() > 1e-6f ).all()) {\n\t\t\t\t\t\tInputCamera cam = camera_handler.getCamera();\n\t\t\t\t\t\tcam.setLookAt(box.center() + 2.0f*box.diagonal(), box.center(), { 0,1,0 });\n\t\t\t\t\t\tcamera_handler.fromCamera(cam);\n\t\t\t\t\t}\t\t\t\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif (ImGui::Button((\"X##\" + mesh_it->name).c_str())) {\n\t\t\t\t\tremoveMesh(mesh_it->name);\n\t\t\t\t}\n\t\t\t\tImGui::NextColumn();\n\t\t\t\t\n\t\t\t\tImGui::Checkbox((\"##active_\" + mesh.name).c_str(), &mesh.active);\n\t\t\t\tImGui::SameLine();\n\t\t\t\tif (ImGui::Button((\"OnlyMe##\" + mesh.name).c_str())) {\n\t\t\t\t\tfor (auto other_it = list_meshes.begin(); other_it != list_meshes.end(); ++other_it) {\n\t\t\t\t\t\tother_it->active = (other_it == mesh_it);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tmesh.onGUI(mesh.name);\n\t\t\t\tImGui::Separator();\n\t\t\t}\n\n\t\t\tImGui::Columns(1);\n\t\t}\n\t\tif (do_swap) {\n\t\t\tstd::swap(*swap_it_src, *swap_it_dst);\n\t\t\t++num_swap;\n\t\t}\n\t\tif (ImGui::IsMouseReleased(0)) {\n\t\t\tnum_swap = 1;\n\t\t}\n\t}\n\n\tMeshData & MultiMeshManager::addMesh(const std::string & name, Mesh::Ptr mesh, bool use_raycaster)\n\t{\n\t\tif (!mesh) {\n\t\t\tSIBR_WRG << \"no mesh ptr in \" << name;\n\t\t\treturn MeshData::dummy;\n\t\t}\n\n\t\treturn addMesh(name, mesh, 0, use_raycaster);\n\t}\n\n\tMeshData & MultiMeshManager::addMesh(const std::string & name, Mesh::Ptr mesh, Raycaster::Ptr raycaster, bool create_raycaster)\n\t{\n\t\tif (!mesh) {\n\t\t\tSIBR_WRG << \"no mesh ptr in \" << name;\n\t\t\treturn MeshData::dummy;\n\t\t}\n\n\t\tMeshData data(name, mesh, MeshData::TRIANGLES, Mesh::FillRenderMode);\n\t\tdata.colorMode = mesh->hasColors() ? MeshData::ColorMode::VERTEX : MeshData::ColorMode::USER_DEFINED;\n\t\tdata.normalMode = (mesh->hasNormals() ? MeshData::PER_VERTEX : MeshData::PER_TRIANGLE);\n\t\tdata.phongShading = mesh->hasNormals();\n\t\tdata.raycaster = raycaster;\n\n\t\treturn addMeshData(data, create_raycaster).setColorRandom();\n\t}\n\n\tMeshData & MultiMeshManager::addMeshAsLines(const std::string & name, Mesh::Ptr mesh)\n\t{\n\t\tif (!mesh) {\n\t\t\tSIBR_WRG << \"no mesh ptr in \" << name;\n\t\t\treturn MeshData::dummy;\n\t\t}\n\n\t\tMeshData data(name, mesh, MeshData::LINES, Mesh::LineRenderMode);\n\t\treturn addMeshData(data).setColorRandom().setDepthTest(false);\n\t}\n\n\tMeshData & MultiMeshManager::addLines(const std::string & name, const std::vector<Vector3f>& endPoints, const Vector3f & color)\n\t{\n\t\tMesh::Triangles tris(endPoints.size() / 2);\n\t\tfor (uint t = 0; t < tris.size(); ++t) {\n\t\t\ttris[t] = Vector3u(2 * t, 2 * t, 2 * t + 1);\n\t\t}\n\n\t\tMesh::Ptr mesh = std::make_shared<Mesh>();\n\t\tmesh->vertices(endPoints);\n\t\tmesh->triangles(tris);\n\n\t\tMeshData data(name, mesh, MeshData::LINES, Mesh::LineRenderMode);\n\t\tdata.userColor = color;\n\t\tdata.depthTest = false;\n\n\t\treturn addMeshData(data).setColorMode(MeshData::USER_DEFINED);\n\t}\n\n\tMeshData & MultiMeshManager::addPoints(const std::string & name, const std::vector<Vector3f>& points, const Vector3f & color)\n\t{\n\t\tMesh::Vertices vertices(points);\n\n\t\tMesh::Ptr mesh = std::make_shared<Mesh>();\n\t\tmesh->vertices(vertices);\n\n\t\tMeshData data(name, mesh, MeshData::POINTS, Mesh::PointRenderMode);\n\t\tdata.userColor = color;\n\t\tdata.depthTest = false;\n\n\t\treturn addMeshData(data).setColorMode(MeshData::USER_DEFINED);\n\t}\n\n\tMeshData & MultiMeshManager::getMeshData(const std::string & name)\n\t{\n\t\tfor (auto & m : list_meshes) {\n\t\t\tif (m.name == name ) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}\n\t\treturn MeshData::dummy;\n\t}\n\n\tMeshData & MultiMeshManager::addMeshData(MeshData & data, bool create_raycaster)\n\t{\n\t\tbool collision = false;\n\t\tIterator collision_it;\n\t\tfor (collision_it = list_meshes.begin(); collision_it != list_meshes.end(); ++collision_it) {\n\t\t\tif (collision_it->name == data.name) {\n\t\t\t\tcollision = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (collision) {\n\t\t\tcollision_it->meshPtr = data.meshPtr;\n\t\t\treturn MeshData::dummy;\n\t\t} else {\n\t\t\tRaycaster::Ptr raycaster;\n\t\t\tif (create_raycaster) {\n\t\t\t\traycaster = std::make_shared<Raycaster>();\n\t\t\t\traycaster->init();\n\t\t\t\traycaster->addMesh(*data.meshPtr);\n\t\t\t}\n\t\t\tdata.raycaster = raycaster;\n\n\t\t\tlist_meshes.push_back(data);\n\n\n\t\t\tauto box = data.meshPtr->getBoundingBox();\n\t\t\tif (!box.isEmpty()) {\n\t\t\t\tInputCamera cam = camera_handler.getCamera();\n\t\t\t\tcam.zfar(std::max(cam.zfar(), 5.0f*box.diagonal().norm()));\n\t\t\t\tcamera_handler.fromCamera(cam);\n\t\t\t}\n\n\t\t\treturn list_meshes.back();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/MultiMeshManager.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include <core/graphics/Mesh.hpp>\r\n#include <core/graphics/Shader.hpp>\r\n#include <core/view/ViewBase.hpp>\r\n#include <core/view/InteractiveCameraHandler.hpp>\r\n#include <core/raycaster/CameraRaycaster.hpp>\r\n\r\n#include <list>\r\n\r\nnamespace sibr {\r\n\r\n\tclass MultiMeshManager;\r\n\tclass MeshData;\r\n\r\n\t// Hierarchy of shader wrappers, so there is no duplication for uniforms, init(), set() and render().\r\n\r\n\t/** Shader wrapper for sending mesh display options to the GPU (while avoiding duplicated uniforms) . \r\n\t * Contains an MVP matrix and an opacity value.\r\n\t  \\ingroup sibr_view\r\n\t */\r\n\tclass SIBR_VIEW_EXPORT ShaderAlphaMVP {\r\n\t\tSIBR_CLASS_PTR(ShaderAlphaMVP);\r\n\tpublic:\r\n\t\t/** Initialize the shader. \r\n\t\t *\\param name the shader name\r\n\t\t *\\param vert the vertex shader content\r\n\t\t *\\param frag the fragment shader ocntent\r\n\t\t *\\param geom the geometry shader content\r\n\t\t */\r\n\t\tvirtual void initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom = \"\");\r\n\r\n\t\t/* Set uniforms based on the camera position and mesh options.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void setUniforms(const Camera & eye, const MeshData & data);\r\n\r\n\t\t/** Render using the passed information.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void render(const Camera & eye, const MeshData & data);\r\n\r\n\tprotected:\r\n\t\tGLShader\t\t\t\tshader; ///< Base shader object.\r\n\t\tGLuniform<Matrix4f>\t\tmvp; ///< MVP matrix.\r\n\t\tGLuniform<float>\t\talpha = 1.0; ///< Opacity.\r\n\t};\r\n\r\n\t/** Shader wrapper for sending mesh display options to the GPU (while avoiding duplicated uniforms) .\r\n\t * Adds a user-defined color. \\sa ShaderAlphaMVP\r\n\t  \\ingroup sibr_view\r\n\t */\r\n\tclass SIBR_VIEW_EXPORT ColorMeshShader : public ShaderAlphaMVP {\r\n\tpublic:\r\n\t\t/** Initialize the shader.\r\n\t\t *\\param name the shader name\r\n\t\t *\\param vert the vertex shader content\r\n\t\t *\\param frag the fragment shader ocntent\r\n\t\t *\\param geom the geometry shader content\r\n\t\t */\r\n\t\tvirtual void initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom = \"\");\r\n\r\n\t\t/* Set uniforms based on the camera position and mesh options.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void setUniforms(const Camera & eye, const MeshData & data);\r\n\r\n\tprotected:\r\n\t\tGLuniform<Vector3f>\tuser_color; ///< user-defined constant color.\r\n\t};\r\n\r\n\t/** Shader wrapper for sending mesh display options to the GPU (while avoiding duplicated uniforms) .\r\n\t * Adds a point size. \\sa ShaderAlphaMVP\r\n\t  \\ingroup sibr_view\r\n\t */\r\n\tclass SIBR_VIEW_EXPORT PointShader : public ColorMeshShader {\r\n\tpublic:\r\n\t\t/** Initialize the shader.\r\n\t\t *\\param name the shader name\r\n\t\t *\\param vert the vertex shader content\r\n\t\t *\\param frag the fragment shader ocntent\r\n\t\t *\\param geom the geometry shader content\r\n\t\t */\r\n\t\tvoid initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom = \"\") override;\r\n\r\n\t\t/* Set uniforms based on the camera position and mesh options.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void setUniforms(const Camera & eye, const MeshData & data) override;\r\n\r\n\t\t/** Render using the passed information.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void render(const Camera & eye, const MeshData & data) override;\r\n\r\n\tprotected:\r\n\t\tGLuniform<int> radius; ///< Point screenspace radius.\r\n\t};\r\n\r\n\t/** Shader wrapper for sending mesh display options to the GPU (while avoiding duplicated uniforms) .\r\n\t * Adds shading parameters. \\sa ShaderAlphaMVP\r\n\t  \\ingroup sibr_view\r\n\t */\r\n\tclass SIBR_VIEW_EXPORT MeshShadingShader : public ColorMeshShader {\r\n\tpublic:\r\n\t\t/** Initialize the shader.\r\n\t\t *\\param name the shader name\r\n\t\t *\\param vert the vertex shader content\r\n\t\t *\\param frag the fragment shader ocntent\r\n\t\t *\\param geom the geometry shader content\r\n\t\t */\r\n\t\tvoid initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom = \"\") override;\r\n\r\n\t\t/* Set uniforms based on the camera position and mesh options.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void setUniforms(const Camera & eye, const MeshData & data) override;\r\n\r\n\tprotected:\r\n\t\tGLuniform<Vector3f>\t\tlight_position; ///< Light position for shading.\r\n\t\tGLuniform<bool>\t\t\tphong_shading, use_mesh_color; ///< Should the mesh be shaded, which color should be used.\r\n\t};\r\n\r\n\t/** Shader wrapper for sending mesh display options to the GPU (while avoiding duplicated uniforms) .\r\n\t * Adds line length option. \\sa ShaderAlphaMVP\r\n\t  \\ingroup sibr_view\r\n\t */\r\n\tclass SIBR_VIEW_EXPORT NormalRenderingShader : public ColorMeshShader {\r\n\tpublic:\r\n\t\t/** Initialize the shader.\r\n\t\t *\\param name the shader name\r\n\t\t *\\param vert the vertex shader content\r\n\t\t *\\param frag the fragment shader ocntent\r\n\t\t *\\param geom the geometry shader content\r\n\t\t */\r\n\t\tvoid initShader(const std::string & name, const std::string & vert, const std::string & frag, const std::string & geom = \"\") override;\r\n\r\n\t\t/* Set uniforms based on the camera position and mesh options.\r\n\t\t * \\param eye the current viewpoint\r\n\t\t * \\param data the mesh display options\r\n\t\t */\r\n\t\tvirtual void setUniforms(const Camera & eye, const MeshData & data) override;\r\n\r\n\tprotected:\r\n\t\tGLuniform<float> normals_size; ///< Normal line length.\r\n\t};\r\n\r\n\r\n\t/** Helper class containing all information relative to how to render a mesh for debugging purpose in a MultiMeshManager.\r\n\t * You can chain setters to modify multiple properties sequentially (chaining).\r\n\t\\sa MultiMeshManager\r\n\t \\ingroup sibr_view\r\n\t*/\r\n\tclass SIBR_VIEW_EXPORT MeshData {\r\n\t\tSIBR_CLASS_PTR(MeshData);\r\n\r\n\tpublic:\r\n\t\tfriend class MultiMeshManager;\r\n\r\n\t\t/** Color mode: constant defined by the user, or per-vertex attribute. */\r\n\t\tenum ColorMode : int { USER_DEFINED, VERTEX };\r\n\t\t/** Type of mesh: points, lines or faces. Dummy is for unitialized objects. */\r\n\t\tenum MeshType : int { POINTS = 0, LINES = 1, TRIANGLES = 2, DUMMY };\r\n\t\t/** When displaying normals, use the per-face or per-vertices normals */\r\n\t\tenum NormalMode { PER_TRIANGLE, PER_VERTEX };\r\n\r\n\t\t/** COnstructor.\r\n\t\t *\\param _name the object name\r\n\t\t *\\param mesh_ptr the geoemtry to display\r\n\t\t *\\param mesh_type the type of mesh\r\n\t\t *\\param render_mode for triangle meshes, should they be displayed filled, as wireframes, or point clouds (\\sa Mesh).\r\n\t\t */\r\n\t\tMeshData(const std::string & _name = \"\", Mesh::Ptr mesh_ptr = {}, MeshType mesh_type = TRIANGLES, Mesh::RenderMode render_mode = Mesh::FillRenderMode);\r\n\r\n\t\t/** Render the geometry. */\r\n\t\tvoid\trenderGeometry() const;\r\n\r\n\t\t/** Display the GUI list item associated to this object.\r\n\t\t *\\param name additional display name\r\n\t\t */\r\n\t\tvoid\tonGUI(const std::string & name);\r\n\r\n\t\t/** \\return if the object is valid. */\r\n\t\toperator bool() const;\r\n\r\n\t\t/** \\return a string describing the geometry. */\r\n\t\tstd::string getInfos() const;\r\n\r\n\t\t/** Set the color.\r\n\t\t *\\param col the color to use\r\n\t\t *\\return the options object, for chaining.\r\n\t\t *\\note To see the color, you might also have to specify the color mode if your mesh has vertex colors.\r\n\t\t */\r\n\t\tMeshData & setColor(const Vector3f & col);\r\n\r\n\t\t/** Set the backface culling.\r\n\t\t *\\param bf should culling be performed\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData & setBackFace(bool bf);\r\n\r\n\t\t/** Set the depth test.\r\n\t\t *\\param dt should depth testing be enabled\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData & setDepthTest(bool dt);\r\n\r\n\t\t/** Set a random constant color.\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData & setColorRandom();\r\n\r\n\t\t/** Set the size of points for point-based display.\r\n\t\t *\\param rad the point radius in screenspace\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData & setRadiusPoint(int rad);\r\n\r\n\t\t/** Set the size of points for point-based display.\r\n\t\t *\\param scale\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData& setScale(float s);\r\n\r\n\t\t/** Set Transformation matrix.\r\n\t\t *\\param model matrix\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData& setTransformation(const sibr::Matrix4f& tr);\r\n\r\n\t\t/** Set the opacity.\r\n\t\t *\\param alpha the opacity value for the whole object\r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData & setAlpha(float alpha);\r\n\r\n\t\t/** Set the color mode (either user-defined constant or vertex color).\r\n\t\t *\\param mode the color mode \r\n\t\t *\\return the options object, for chaining.\r\n\t\t */\r\n\t\tMeshData & setColorMode(ColorMode mode);\r\n\r\n\t\t/** Get the display options of the additional normals geometry.\r\n\t\t *\\return the normals options.\r\n\t\t */\r\n\t\tMeshData getNormalsMeshData() const;\r\n\r\n\t\tstd::string\t\t\tname; ///< Mesh name.\r\n\r\n\t\tMesh::Ptr\t\t\tmeshPtr; ///< Geometry.\r\n\t\tMeshType\t\t\tmeshType; ///< Type of mesh.\r\n\t\tMesh::RenderMode\trenderMode; ///< Render mode for triangle meshes.\r\n\r\n\t\tMatrix4f\t\t\ttransformation = Matrix4f::Identity(); ///< Additional model transformation.\r\n\r\n\t\tRaycaster::Ptr\t\traycaster; ///< Associated raycaster (optional)\r\n\r\n\t\tbool\t\t\t\tdepthTest = true; ///< Perform depth test.\r\n\t\tbool\t\t\t\tbackFaceCulling = true; ///< Perform culling.\r\n\t\tbool\t\t\t\tfrontFaceCulling = false; ///< Swap front and back faces for culling.\r\n\t\tbool\t\t\t\tinvertDepthTest = false; ///< Switch the depth test to \"greater than\".\r\n\t\tbool\t\t\t\tactive = true; ///< Should the object be displayed.\r\n\t\tbool\t\t\t\tphongShading = false; ///< Apply Phong shading to the object.\r\n\r\n\t\t// Points\r\n\t\tint\t\t\t\t\tradius = 5; ///< Point screenspace radius.\r\n\t\tfloat\t\t\t\tscale = 1.0f;\r\n\r\n\t\t// Colors\r\n\t\tColorMode\t\t\tcolorMode = USER_DEFINED; ///< Color mode.\r\n\t\tVector3f\t\t\tuserColor = { 0.5,0.5,0.5 }; ///< Constant user-defined  color.\r\n\t\tfloat\t\t\t\talpha = 1.0f; ///< Opacity.\r\n\r\n\t\t// Normals\r\n\t\tVector3f normalsColor = { 1,0,1 }; ///< Normal lines color.\r\n\t\tfloat normalsLength = 1.0f; ///< Normal lines length.\r\n\t\tNormalMode normalMode = PER_TRIANGLE; ///< Which normals should be displayed.\r\n\t\tbool normalsInverted = false; ///< Flip the normal lines orientation.\r\n\t\tbool showNormals = false; ///< Should the normals be displayed.\r\n\r\n\tprotected:\r\n\t\t\r\n\t\tstatic MeshData dummy; ///< OPtions object used for non-existing objects.\r\n\t};\r\n\r\n\r\n\t/** Provide a view to render and interact with several meshes, \r\n\t * useful for debugging purposes for instance.\r\n\t * The API supports chaining when setting mesh display options. You can for instance do:\r\n\t * manager.addMesh(\"my mesh\", mesh).setDepthtest(true).setAlpha(0.5f); \r\n\t  \\ingroup sibr_view\r\n\t*/\r\n\tclass SIBR_VIEW_EXPORT MultiMeshManager : public ViewBase {\r\n\t\tSIBR_CLASS_PTR(MultiMeshManager);\r\n\r\n\tpublic:\r\n\r\n\t\t/** Constructor\r\n\t\t\\param _name Name used for GUI panels as a prefix to avoid collision.\r\n\t\t\\note Requires an OpenGL context setup\r\n\t\t*/\r\n\t\tMultiMeshManager(const std::string & _name = \"MultiMeshManager\");\r\n\t\r\n\t\t/** Add a mesh to the visualization.\r\n\t\t\\param name name used for the object, if it already exist it will update the geometry and preserve display options\r\n\t\t\\param mesh the mesh\r\n\t\t\\param use_raycaster should a raycaster be setup, for trackball centering for instance\r\n\t\t\\return a reference to the object display options (for chained modifications).\r\n\t\t*/\r\n\t\tMeshData & addMesh(const std::string & name, Mesh::Ptr mesh, bool use_raycaster = true);\r\n\r\n\t\t/** Add a mesh to the visualization.\r\n\t\t\\param name name used for the object, if it already exist it will update the geometry and preserve display options\r\n\t\t\\param mesh the mesh\r\n\t\t\\param raycaster existing raycaster, for trackball centering for instance\r\n\t\t\\param create_raycaster should a raycaster be setup if the passed raycaster is null\r\n\t\t\\return a reference to the object display options (for chained modifications).\r\n\t\t*/\r\n\t\tMeshData & addMesh(const std::string & name, Mesh::Ptr mesh, Raycaster::Ptr raycaster, bool create_raycaster = false);\r\n\r\n\t\t/** Add lines to the visualization, using the mesh vertices as line endpoints.\r\n\t\t\\param name name used for the object, if it already exist it will update the geometry and preserve display options\r\n\t\t\\param mesh the mesh\r\n\t\t\\return a reference to the object display options (for chained modifications).\r\n\t\t*/\r\n\t\tMeshData & addMeshAsLines(const std::string & name, Mesh::Ptr mesh);\r\n\r\n\t\t/** Add lines to the visualization, defined by their endpoints.\r\n\t\t\\param name name used for the object, if it already exist it will update the geometry and preserve display options\r\n\t\t\\param endPoints the line endpoints\r\n\t\t\\param color the display color to use\r\n\t\t\\return a reference to the object display options (for chained modifications).\r\n\t\t*/\r\n\t\tMeshData & addLines(const std::string & name, const std::vector<Vector3f> & endPoints, const Vector3f & color = { 0,1,0 });\r\n\r\n\t\t/** Add points to the visualization.\r\n\t\t\\param name name used for the mesh, if it already exist it will update the geometry and preserve display options\r\n\t\t\\param points the points\r\n\t\t\\param color the display color to use\r\n\t\t\\return a reference to the display options (for chained modifications).\r\n\t\t*/\r\n\t\tMeshData & addPoints(const std::string & name, const std::vector<Vector3f> & points, const Vector3f & color = { 1,0,0 });\r\n\r\n\t\t/** Accessor to the options of a visualized object.\r\n\t\t\t\\param name the object name to look for\r\n\t\t\t\\return a reference to the object options if it exists, or to MeshData::dummy if no match was found.\r\n\t\t*/\r\n\t\tMeshData & getMeshData(const std::string & name);\r\n\r\n\t\t/** Remove a object from the viewer.\r\n\t\t\\param name the name of the object to remove\r\n\t\t*/\r\n\t\tvoid\t\tremoveMesh(const std::string & name);\r\n\r\n\t\tvoid\t\tsetIntialView(const std::string& dataset_path);\r\n\r\n\t\t// ViewBase interface\r\n\r\n\t\t/** Update state based on user input.\r\n\t\t * \\param input user input\r\n\t\t * \\param vp input viewport\r\n\t\t * \\note Used when the view is in a multi-view system.\r\n\t\t */\r\n\t\tvirtual void\tonUpdate(Input& input, const Viewport & vp) override;\r\n\r\n\t\t/** Render content in the currently bound RT, using a specific viewport.\r\n\t\t * \\param viewport destination viewport\r\n\t\t * \\note Used when the view is in a multi-view system.\r\n\t\t */\r\n\t\tvirtual void\tonRender(const Viewport & viewport) override;\r\n\r\n\t\t/** Render content in a RT, using the RT viewport.\r\n\t\t * \\param dst destination RT\r\n\t\t */\r\n\t\tvirtual void\tonRender(IRenderTarget & dst) ;\r\n\r\n\t\t/** Display GUI. */\r\n\t\tvirtual void\tonGUI() override;\r\n\r\n\t\t/** \\return the view camera handler */\r\n\t\tInteractiveCameraHandler & getCameraHandler() { return camera_handler; }\r\n\r\n\t\t/** \\return the colored mesh shader */\r\n\t\tMeshShadingShader & getMeshShadingShader() { return colored_mesh_shader; }\r\n\r\n\tprotected:\r\n\r\n\t\t/** Helper to add some geometry to the view. \r\n\t\t\\param data the object to add\r\n\t\t\\param update_raycaster should the associated raycaster be updated with the new geometry\r\n\t\t\\return the update object options\r\n\t\t*/\r\n\t\tMeshData & addMeshData(MeshData & data, bool update_raycaster = false);\r\n\r\n\t\t/** Create the shaders */\r\n\t\tvoid initShaders();\r\n\r\n\t\t/** Render all the registered meshes. */\r\n\t\tvoid renderMeshes();\r\n\r\n\t\t/** Generate the list of objects in the GUI panel of the view. */\r\n\t\tvoid list_mesh_onGUI();\r\n\r\n\t\tusing ListMesh = std::list<MeshData>;\r\n\t\tusing Iterator = ListMesh::iterator;\r\n\t\r\n\t\tstd::string\t\t\t\t\t\t\tname; ///< View name.\r\n\t\tListMesh\t\t\t\t\t\t\tlist_meshes; ///< Meshes to display.\r\n\t\tIterator \t\t\t\t\t\t\tselected_mesh_it; ///< Currently selected mesh.\r\n\t\tbool\t\t\t\t\t\t\t\tselected_mesh_it_is_valid = false; ///< Is there a valid currently selected mesh.\r\n\t\t\r\n\t\tInteractiveCameraHandler\t\t\tcamera_handler; ///< View camera handler.\r\n\r\n\t\tPointShader\t\t\t\t\t\t\tpoints_shader; ///< Shader for points.\r\n\t\tMeshShadingShader\t\t\t\t\tcolored_mesh_shader; ///< Shader for meshes.\r\n\t\tNormalRenderingShader\t\t\t\tper_vertex_normals_shader, per_triangle_normals_shader; ///< Shaders for visualizing an object normals.\r\n\r\n\t\tVector3f\t\t\t\t\t\t\tbackgroundColor = { 0.7f, 0.7f, 0.7f }; ///< Background clear color.\r\n\t};\r\n\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/MultiViewManager.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n# include \"core/graphics/GUI.hpp\"\n# include \"core/view/MultiViewManager.hpp\"\n\nnamespace sibr\n{\n\tMultiViewBase::MultiViewBase(const Vector2i & defaultViewRes)\n\t{\n\t\t/// \\todo TODO: support launch arg for stereo mode.\n\t\trenderingMode(IRenderingMode::Ptr(new MonoRdrMode()));\n\n\t\t//Default view resolution.\n\t\tsetDefaultViewResolution(defaultViewRes);\n\n\t\t_timeLastFrame = std::chrono::steady_clock::now();\n\t\t_deltaTime = 0.0;\n\t\t_exportPath = \"./screenshots\";\n\t}\n\n\tvoid MultiViewBase::onUpdate(Input& input)\n\t{\n\t\tif (input.key().isActivated(Key::LeftControl) && input.key().isPressed(Key::LeftAlt) && input.key().isPressed(Key::P)) {\n\t\t\t_onPause = !_onPause;\n\t\t}\n\t\tif (_onPause) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Elapsed time since last rendering.\n\t\tconst auto timeNow = std::chrono::steady_clock::now();\n\t\t_deltaTime = (float)(std::chrono::duration_cast<std::chrono::microseconds>(timeNow - _timeLastFrame).count())/1000000.0f;\n\t\t_timeLastFrame = timeNow;\n\n\t\tfor (auto & subview : _subViews) {\n\t\t\tif (subview.second.view->active()) {\n\t\t\t\tauto subInput = !subview.second.view->isFocused() ? Input() : Input::subInput(input, subview.second.viewport, false);\n\n\t\t\t\tif (subview.second.handler) {\n\t\t\t\t\tsubview.second.handler->update(subInput, _deltaTime, subview.second.viewport);\n\t\t\t\t}\n\n\t\t\t\tsubview.second.updateFunc(subview.second.view, subInput, subview.second.viewport, _deltaTime);\n\n\t\t\t}\n\t\t}\n\n\t\tfor (auto & subview : _ibrSubViews) {\n\t\t\tMultiViewBase::IBRSubView & fView = subview.second;\n\n\t\t\tif (fView.view->active()) {\n\t\t\t\tauto subInput = !fView.view->isFocused() ? Input() : Input::subInput(input, fView.viewport, false);\n\n\t\t\t\tif (fView.handler) {\n\t\t\t\t\tfView.handler->update(subInput, _deltaTime, fView.viewport);\n\t\t\t\t}\n\n\t\t\t\tfView.cam = fView.updateFunc(fView.view, subInput, fView.viewport, _deltaTime);\n\n\t\t\t\t/// If we use the default update func and the integrated handler, \n\t\t\t\t/// we have to use the handler's camera.\n\t\t\t\tif (fView.defaultUpdateFunc && fView.handler) {\n\t\t\t\t\tfView.cam = fView.handler->getCamera();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\tfor (auto & subMultiView : _subMultiViews) {\n\t\t\tsubMultiView.second->onUpdate(input);\n\t\t}\n\t}\n\n\tvoid MultiViewBase::onRender(Window& win)\n\t{\n\t\t// Render all views.\n\t\tfor (auto & subview : _ibrSubViews) {\n\t\t\tif (subview.second.view->active()) {\n\n\t\t\t\trenderSubView(subview.second);\n\n\t\t\t\tif (_enableGUI && _showSubViewsGui) {\n\t\t\t\t\tsubview.second.view->onGUI();\n\t\t\t\t\tif (subview.second.handler) {\n\t\t\t\t\t\tsubview.second.handler->onGUI(\"Camera \" + subview.first);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (auto & subview : _subViews) {\n\t\t\tif (subview.second.view->active()) {\n\n\t\t\t\trenderSubView(subview.second);\n\t\t\t\t\n\t\t\t\tif (_enableGUI && _showSubViewsGui) {\n\t\t\t\t\tsubview.second.view->onGUI();\n\t\t\t\t\tif (subview.second.handler) {\n\t\t\t\t\t\tsubview.second.handler->onGUI(\"Camera \" + subview.first);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (auto & subMultiView : _subMultiViews) {\n\t\t\tsubMultiView.second->onRender(win);\n\t\t}\n\n\n\t}\n\n\tvoid MultiViewBase::onGui(Window & win)\n\t{\n\t}\n\n\tvoid MultiViewBase::addSubView(const std::string & title, ViewBase::Ptr view, const Vector2u & res, const ImGuiWindowFlags flags)\n\t{\n\t\tconst ViewUpdateFunc updateFunc =\n\t\t\t[](ViewBase::Ptr& vi, Input& in, const Viewport& vp, const float dt) {\n\t\t\tvi->onUpdate(in, vp);\n\t\t};\n\t\taddSubView(title, view, updateFunc, res, flags);\n\t}\n\n\tvoid MultiViewBase::addSubView(const std::string & title, ViewBase::Ptr view, const ViewUpdateFunc updateFunc, const Vector2u & res, const ImGuiWindowFlags flags)\n\t{\n\t\tfloat titleBarHeight = 0.0f;\n\t\tif(_enableGUI) titleBarHeight = ImGui::GetTitleBarHeight();\n\t\t// We have to shift vertically to avoid an overlap with the menu bar.\n\t\tconst Viewport viewport(0.0f, titleBarHeight,\n\t\t\tres.x() > 0 ? res.x() : (float)_defaultViewResolution.x(),\n\t\t\t(res.y() > 0 ? res.y() : (float)_defaultViewResolution.y()) + titleBarHeight);\n\t\tRenderTargetRGB::Ptr rtPtr(new RenderTargetRGB((uint)viewport.finalWidth(), (uint)viewport.finalHeight(), SIBR_CLAMP_UVS));\n\t\t_subViews[title] = {view, rtPtr, viewport, title, flags, updateFunc };\n\n\t}\n\n\tvoid MultiViewBase::addIBRSubView(const std::string & title, ViewBase::Ptr view, const IBRViewUpdateFunc updateFunc, const Vector2u & res, const ImGuiWindowFlags flags, const bool defaultFuncUsed)\n\t{\n\t\tfloat titleBarHeight = 0.0f;\n\t\tif(_enableGUI) titleBarHeight = ImGui::GetTitleBarHeight();\n\t\t// We have to shift vertically to avoid an overlap with the menu bar.\n\t\tconst Viewport viewport(0.0f, titleBarHeight,\n\t\t\tres.x() > 0 ? res.x() : (float)_defaultViewResolution.x(),\n\t\t\t(res.y() > 0 ? res.y() : (float)_defaultViewResolution.y()) + titleBarHeight);\n\t\tRenderTargetRGB::Ptr rtPtr(new RenderTargetRGB((uint)viewport.finalWidth(), (uint)viewport.finalHeight(), SIBR_CLAMP_UVS));\n\t\tif (_ibrSubViews.count(title) > 0){\n\t\t\tconst auto handler = _ibrSubViews[title].handler;\n\t\t\t_ibrSubViews[title] = { view, rtPtr, viewport, title, flags, updateFunc, defaultFuncUsed };\n\t\t\t_ibrSubViews[title].handler = handler;\n\t\t}\n\t\telse {\n\t\t\t_ibrSubViews[title] = { view, rtPtr, viewport, title, flags, updateFunc, defaultFuncUsed };\n\t\t}\n\t\t_ibrSubViews[title].shouldUpdateLayout = true;\n\t}\n\n\tvoid MultiViewBase::addIBRSubView(const std::string & title, ViewBase::Ptr view, const Vector2u & res, const ImGuiWindowFlags flags)\n\t{\n\t\tconst auto updateFunc = [](ViewBase::Ptr& vi, Input& in, const Viewport& vp, const float dt) {\n\t\t\tvi->onUpdate(in, vp);\n\t\t\treturn InputCamera();\n\t\t};\n\t\taddIBRSubView(title, view, updateFunc, res, flags, true);\n\t}\n\n\tvoid MultiViewBase::addIBRSubView(const std::string & title, ViewBase::Ptr view, const IBRViewUpdateFunc updateFunc, const Vector2u & res, const ImGuiWindowFlags flags)\n\t{\n\t\taddIBRSubView(title, view, updateFunc, res, flags, false);\n\t}\n\n\tvoid MultiViewBase::addSubMultiView(const std::string & title, MultiViewBase::Ptr multiview)\n\t{\n\t\t_subMultiViews[title] = multiview;\n\t}\n\n\tViewBase::Ptr & MultiViewBase::getIBRSubView(const std::string & title)\n\t{\n\t\tif (_subViews.count(title) > 0) {\n\t\t\treturn _subViews.at(title).view;\n\t\t}\n\t\tif (_ibrSubViews.count(title) > 0) {\n\t\t\treturn _ibrSubViews.at(title).view;\n\t\t}\n\n\t\tSIBR_ERR << \" No subview with name <\" << title << \"> found.\" << std::endl;\n\n\t\treturn _subViews.begin()->second.view;\n\t}\n\n\tViewport & MultiViewBase::getIBRSubViewport(const std::string & title)\n\t{\n\t\tif (_subViews.count(title) > 0) {\n\t\t\treturn _subViews.at(title).viewport;\n\t\t}\n\t\telse if (_ibrSubViews.count(title) > 0) {\n\t\t\treturn _ibrSubViews.at(title).viewport;\n\t\t}\n\n\t\tSIBR_ERR << \" No subviewport with name <\" << title << \"> found.\" << std::endl;\n\n\t\treturn _subViews.begin()->second.viewport;\n\t}\n\n\tvoid MultiViewBase::renderSubView(SubView & subview) \n\t{\n\t\t\n\t\tif (!_onPause) {\n\n\t\t\tconst Viewport renderViewport(0.0, 0.0, (float)subview.rt->w(), (float)subview.rt->h());\n\t\t\tsubview.render(_renderingMode, renderViewport);\n\n\t\t\t// Offline video dumping, continued. We ignore additional rendering as those often are GUI overlays.\n\t\t\tif (subview.handler != NULL && (subview.handler->getCamera().needVideoSave() || subview.handler->getCamera().needSave())) {\n\t\t\t\t\n\t\t\t\tImageRGB frame;\n\n\t\t\t\tsubview.rt->readBack(frame);\n\t\t\t\t\n\t\t\t\tif (subview.handler->getCamera().needSave()) {\n\t\t\t\t\tframe.save(subview.handler->getCamera().savePath());\n\t\t\t\t}\n\t\t\t\t_videoFrames.push_back(frame.toOpenCVBGR());\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\t// Additional rendering.\n\t\t\tsubview.renderFunc(subview.view, renderViewport, std::static_pointer_cast<IRenderTarget>(subview.rt));\n\n\t\t\t// Render handler if needed.\n\t\t\tif (subview.handler) {\n\t\t\t\tsubview.rt->bind();\n\t\t\t\trenderViewport.bind();\n\t\t\t\tsubview.handler->onRender(renderViewport);\n\t\t\t\tsubview.rt->unbind();\n\t\t\t}\n\t\t}\n\n\t\tif(_enableGUI)\n\t\t{\n\t\t\tImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));\n\t\t\tsubview.view->setFocus(showImGuiWindow(subview.view->name(), *subview.rt, subview.flags, subview.viewport, false, subview.shouldUpdateLayout));\n\t\t\tImGui::PopStyleVar();\n\t\t}\n\t\t// If we have updated the layout, don't do it next frame.\n\t\tsubview.shouldUpdateLayout = false;\n\t}\n\n\tViewBase::Ptr MultiViewBase::removeSubView(const std::string & title)\n\t{\n\t\tViewBase::Ptr viewPtr = nullptr;\n\t\tif (_subViews.count(title) > 0) {\n\t\t\tviewPtr = _subViews.at(title).view;\n\t\t\t_subViews.erase(title);\n\t\t}\n\t\telse if (_ibrSubViews.count(title) > 0) {\n\t\t\tviewPtr = _ibrSubViews.at(title).view;\n\t\t\t_ibrSubViews.erase(title);\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \"No view named <\" << title << \"> found.\" << std::endl;\n\t\t}\n\t\treturn viewPtr;\n\t}\n\n\tvoid MultiViewBase::renderingMode(const IRenderingMode::Ptr& mode)\n\t{\n\t\t_renderingMode = std::move(mode);\n\t}\n\n\tconst Viewport MultiViewBase::getViewport(void) const\n\t{\n\t\treturn Viewport(0.0f, 0.0f, (float)_defaultViewResolution.x(), (float)_defaultViewResolution.y());\n\t}\n\n\tvoid MultiViewBase::addCameraForView(const std::string & name, ICameraHandler::Ptr cameraHandler)\n\t{\n\t\tif (_subViews.count(name) > 0) {\n\t\t\t_subViews.at(name).handler = cameraHandler;\n\t\t}\n\t\telse if (_ibrSubViews.count(name) > 0) {\n\t\t\t_ibrSubViews.at(name).handler = cameraHandler;\n\n\t\t\tSubView & subview = _ibrSubViews.at(name);\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \"No view named <\" << name << \"> found.\" << std::endl;\n\t\t}\n\n\t}\n\n\tvoid MultiViewBase::addAdditionalRenderingForView(const std::string & name, const AdditionalRenderFunc renderFunc)\n\t{\n\t\tif (_subViews.count(name) > 0) {\n\t\t\t_subViews.at(name).renderFunc = renderFunc;\n\t\t}\n\t\telse if (_ibrSubViews.count(name) > 0) {\n\t\t\t_ibrSubViews.at(name).renderFunc = renderFunc;\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \"No view named <\" << name << \"> found.\" << std::endl;\n\t\t}\n\t}\n\n\tint MultiViewBase::numSubViews() const\n\t{\n\t\treturn static_cast<int>(_subViews.size() + _ibrSubViews.size() + _subMultiViews.size());\n\t}\n\n\tvoid MultiViewBase::captureView(const std::string & subviewName, const std::string & path, const std::string & filename)\n\t{\n\t\tif (_subViews.count(subviewName)) {\n\t\t\tcaptureView(_subViews[subviewName], path, filename);\n\t\t}\n\t\telse if (_ibrSubViews.count(subviewName)) {\n\t\t\tcaptureView(_ibrSubViews[subviewName], path, filename);\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \"No View in the MultiViewManager with \" << subviewName << \" as a name!\" << std::endl;\n\t\t}\n\t}\n\n\tvoid MultiViewBase::captureView(const SubView & view, const std::string& path, const std::string & filename) {\n\n\t\tconst uint w = view.rt->w();\n\t\tconst uint h = view.rt->h();\n\n\t\tImageRGB renderingImg(w, h);\n\n\t\tview.rt->readBack(renderingImg);\n\n\t\tstd::string finalPath = path + (!path.empty() ? \"/\" : \"\");\n\t\tif (!filename.empty()) {\n\t\t\tfinalPath.append(filename);\n\t\t}\n\t\telse {\n\t\t\tconst std::string autoName = view.view->name() + \"_\" + sibr::timestamp();\n\t\t\tfinalPath.append(autoName + \".png\");\n\t\t}\n\n\t\tmakeDirectory(path);\n\t\trenderingImg.save(finalPath, true);\n\t}\n\n\tvoid MultiViewBase::mosaicLayout(const Viewport & vp)\n\t{\n\t\tconst int viewsCount = numSubViews();\n\t\t\n\t\t// Do square decomposition for now.\n\t\t// Find the next square.\n\t\tconst int sideCount = int(std::ceil(std::sqrt(viewsCount)));\n\t\tint verticalShift = 0;\n\t\tif(_enableGUI) verticalShift = ImGui::GetTitleBarHeight();\n\n\t\tViewport usedVP = Viewport(vp.finalLeft(), vp.finalTop() + verticalShift, vp.finalRight(), vp.finalBottom());\n\t\tVector2f itemRatio = Vector2f(1, 1) / sideCount;\n\n\t\tint vid = 0;\n\t\tfor (auto & view : _ibrSubViews) {\n\t\t\t// Compute position on grid.\n\t\t\tconst int col = vid % sideCount;\n\t\t\tconst int row = vid / sideCount;\n\t\t\tview.second.viewport = Viewport(usedVP, col*itemRatio[0], row * itemRatio[1], (col + 1)*itemRatio[0], (row + 1)*itemRatio[1]);\n\t\t\tview.second.shouldUpdateLayout = true;\n\t\t\t++vid;\n\t\t}\n\t\tfor (auto & view : _subViews) {\n\t\t\t// Compute position on grid.\n\t\t\tconst int col = vid % sideCount;\n\t\t\tconst int row = vid / sideCount;\n\t\t\tview.second.viewport = Viewport(usedVP, col*itemRatio[0], row * itemRatio[1], (col + 1)*itemRatio[0], (row + 1)*itemRatio[1]);\n\t\t\tview.second.shouldUpdateLayout = true;\n\t\t\t++vid;\n\t\t}\n\t\tfor (auto & view : _subMultiViews) {\n\t\t\t// Compute position on grid.\n\t\t\tconst int col = vid % sideCount;\n\t\t\tconst int row = vid / sideCount;\n\t\t\tview.second->mosaicLayout(Viewport(usedVP, col*itemRatio[0], row * itemRatio[1], (col + 1)*itemRatio[0], (row + 1)*itemRatio[1]));\n\t\t\t++vid;\n\t\t}\n\n\t}\n\t\n\tvoid MultiViewBase::toggleSubViewsGUI()\n\t{\n\t\t_showSubViewsGui = !_showSubViewsGui;\n\n\t\tfor (auto & view : _subMultiViews) {\n\t\t\tview.second->toggleSubViewsGUI();\n\t\t}\n\t}\n\n\tvoid MultiViewBase::setExportPath(const std::string & path) {\n\t\t_exportPath = path;\n\t\tsibr::makeDirectory(path);\n\t}\n\n\tMultiViewBase::SubView::SubView(ViewBase::Ptr view_, RenderTargetRGB::Ptr rt_, const sibr::Viewport viewport_, const std::string& name_, const ImGuiWindowFlags flags_) :\n\t\tview(view_), rt(rt_), handler(), viewport(viewport_), flags(flags_), shouldUpdateLayout(false) {\n\t\trenderFunc = [](ViewBase::Ptr&, const Viewport&, const IRenderTarget::Ptr&) {};\n\t\tview->setName(name_);\n\t}\n\n\tMultiViewBase::BasicSubView::BasicSubView(ViewBase::Ptr view_, RenderTargetRGB::Ptr rt_, const sibr::Viewport viewport_, const std::string& name_, const ImGuiWindowFlags flags_, ViewUpdateFunc f_) :\n\t\tSubView(view_, rt_, viewport_, name_, flags_), updateFunc(f_) {\n\t}\n\n\tvoid MultiViewBase::BasicSubView::render(const IRenderingMode::Ptr& rm, const Viewport& renderViewport) const  {\n\t\trt->bind();\n\t\trenderViewport.bind();\n\t\trenderViewport.clear();\n\t\tview->onRender(renderViewport);\n\t\trt->unbind();\n\t}\n\n\tMultiViewBase::IBRSubView::IBRSubView(ViewBase::Ptr view_, RenderTargetRGB::Ptr rt_, const sibr::Viewport viewport_, const std::string& name_, const ImGuiWindowFlags flags_, IBRViewUpdateFunc f_, const bool defaultUpdateFunc_) :\n\t\tSubView(view_, rt_, viewport_, name_, flags_), updateFunc(f_), defaultUpdateFunc(defaultUpdateFunc_) {\n\t\tcam = sibr::InputCamera();\n\t}\n\n\tvoid MultiViewBase::IBRSubView::render(const IRenderingMode::Ptr& rm, const Viewport& renderViewport) const  {\n\t\tif (rm) {\n\t\t\trm->render(*view, cam, renderViewport, rt.get());\n\t\t}\n\t}\n\n\tMultiViewManager::MultiViewManager(Window& window, bool resize)\n\t\t: _window(window), _fpsCounter(false)\n\t{\n\t\t_enableGUI = window.isGUIEnabled();\n\t\t\n\t\tif (resize) {\n\t\t\twindow.size(\n\t\t\t\tWindow::desktopSize().x() - 200,\n\t\t\t\tWindow::desktopSize().y() - 200);\n\t\t\twindow.position(100, 100);\n\t\t}\n\n\t\t/// \\todo TODO: support launch arg for stereo mode.\n\t\trenderingMode(IRenderingMode::Ptr(new MonoRdrMode()));\n\n\t\t//Default view resolution.\n\t\tint w = int(window.size().x() * 0.5f);\n\t\tint h = int(window.size().y() * 0.5f);\n\t\tsetDefaultViewResolution(Vector2i(w, h));\n\n\t\tif(_enableGUI) ImGui::GetStyle().WindowBorderSize = 0.0;\n\t}\n\n\tvoid MultiViewManager::onUpdate(Input & input)\n\t{\n\t\tMultiViewBase::onUpdate(input);\n\n\t\tif (input.key().isActivated(Key::LeftControl) && input.key().isActivated(Key::LeftAlt) && input.key().isReleased(Key::G)) {\n\t\t\ttoggleGUI();\n\t\t}\n\t}\n\n\tvoid MultiViewManager::onRender(Window & win)\n\t{\n\t\twin.viewport().bind();\n\t\tglClearColor(37.f / 255.f, 37.f / 255.f, 38.f / 255.f, 1.f);\n\t\tglClear(GL_COLOR_BUFFER_BIT);\n\t\tglClearColor(1.f, 1.f, 1.f, 1.f);\n\n\t\tonGui(win);\n\n\t\tMultiViewBase::onRender(win);\n\n\t\t_fpsCounter.update(_enableGUI && _showGUI);\n\t}\n\n\tvoid MultiViewManager::onGui(Window & win)\n\t{\n\t\tMultiViewBase::onGui(win);\n\n\t\t// Menu\n\t\tif (_showGUI && ImGui::BeginMainMenuBar())\n\t\t{\n\t\t\tif (ImGui::BeginMenu(\"Menu\"))\n\t\t\t{\n\t\t\t\tImGui::MenuItem(\"Pause\", \"\", &_onPause);\n\t\t\t\tif (ImGui::BeginMenu(\"Display\")) {\n\t\t\t\t\tconst bool currentScreenState = win.isFullscreen();\n\t\t\t\t\tif (ImGui::MenuItem(\"Fullscreen\", \"\", currentScreenState)) {\n\t\t\t\t\t\twin.setFullscreen(!currentScreenState);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst bool currentSyncState = win.isVsynced();\n\t\t\t\t\tif (ImGui::MenuItem(\"V-sync\", \"\", currentSyncState)) {\n\t\t\t\t\t\twin.setVsynced(!currentSyncState);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst bool isHiDPI = ImGui::GetIO().FontGlobalScale > 1.0f;\n\t\t\t\t\tif (ImGui::MenuItem(\"HiDPI\", \"\", isHiDPI)) {\n\t\t\t\t\t\tif (isHiDPI) {\n\t\t\t\t\t\t\tImGui::GetStyle().ScaleAllSizes(1.0f / win.scaling());\n\t\t\t\t\t\t\tImGui::GetIO().FontGlobalScale = 1.0f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tImGui::GetStyle().ScaleAllSizes(win.scaling());\n\t\t\t\t\t\t\tImGui::GetIO().FontGlobalScale = win.scaling();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (ImGui::MenuItem(\"Hide GUI (!)\", \"Ctrl+Alt+G\")) {\n\t\t\t\t\t\ttoggleGUI();\n\t\t\t\t\t}\n\t\t\t\t\tImGui::EndMenu();\n\t\t\t\t}\n\n\n\t\t\t\tif (ImGui::MenuItem(\"Mosaic layout\")) {\n\t\t\t\t\tmosaicLayout(win.viewport());\n\t\t\t\t}\n\n\t\t\t\tif (ImGui::MenuItem(\"Row layout\")) {\n\t\t\t\t\tVector2f itemSize = win.size().cast<float>();\n\t\t\t\t\titemSize[0] = std::round(float(itemSize[0]) / float(_subViews.size() + _ibrSubViews.size()));\n\t\t\t\t\tconst float verticalShift = ImGui::GetTitleBarHeight();\n\t\t\t\t\tfloat vid = 0.0f;\n\t\t\t\t\tfor (auto & view : _ibrSubViews) {\n\t\t\t\t\t\t// Compute position on grid.\n\t\t\t\t\t\tview.second.viewport = Viewport(vid*itemSize[0], verticalShift, (vid + 1.0f)*itemSize[0] - 1.0f, verticalShift + itemSize[1] - 1.0f);\n\t\t\t\t\t\tview.second.shouldUpdateLayout = true;\n\t\t\t\t\t\t++vid;\n\t\t\t\t\t}\n\t\t\t\t\tfor (auto & view : _subViews) {\n\t\t\t\t\t\t// Compute position on grid.\n\t\t\t\t\t\tview.second.viewport = Viewport(vid*itemSize[0], verticalShift, (vid + 1.0f)*itemSize[0] - 1.0f, verticalShift + itemSize[1] - 1.0f);\n\t\t\t\t\t\tview.second.shouldUpdateLayout = true;\n\t\t\t\t\t\t++vid;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\n\t\t\t\tif (ImGui::MenuItem(\"Quit\", \"Escape\")) { win.close(); }\n\t\t\t\tImGui::EndMenu();\n\t\t\t}\n\n\t\t\tif (ImGui::BeginMenu(\"Views\"))\n\t\t\t{\n\t\t\t\tfor (auto & subview : _subViews) {\n\t\t\t\t\tif (ImGui::MenuItem(subview.first.c_str(), \"\", subview.second.view->active())) {\n\t\t\t\t\t\tsubview.second.view->active(!subview.second.view->active());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (auto & subview : _ibrSubViews) {\n\t\t\t\t\tif (ImGui::MenuItem(subview.first.c_str(), \"\", subview.second.view->active())) {\n\t\t\t\t\t\tsubview.second.view->active(!subview.second.view->active());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (ImGui::MenuItem(\"Metrics\", \"\", _fpsCounter.active())) {\n\t\t\t\t\t_fpsCounter.toggleVisibility();\n\t\t\t\t}\n\t\t\t\tif (ImGui::BeginMenu(\"Front when focus\"))\n\t\t\t\t{\n\t\t\t\t\tfor (auto & subview : _subViews) {\n\t\t\t\t\t\tconst bool isLockedInBackground = subview.second.flags & ImGuiWindowFlags_NoBringToFrontOnFocus;\n\t\t\t\t\t\tif (ImGui::MenuItem(subview.first.c_str(), \"\", !isLockedInBackground)) {\n\t\t\t\t\t\t\tif(isLockedInBackground) {\n\t\t\t\t\t\t\t\tsubview.second.flags &= ~ImGuiWindowFlags_NoBringToFrontOnFocus;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsubview.second.flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (auto & subview : _ibrSubViews) {\n\t\t\t\t\t\tconst bool isLockedInBackground = subview.second.flags & ImGuiWindowFlags_NoBringToFrontOnFocus;\n\t\t\t\t\t\tif (ImGui::MenuItem(subview.first.c_str(), \"\", !isLockedInBackground)) {\n\t\t\t\t\t\t\tif (isLockedInBackground) {\n\t\t\t\t\t\t\t\tsubview.second.flags &= ~ImGuiWindowFlags_NoBringToFrontOnFocus;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsubview.second.flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tImGui::EndMenu();\n\t\t\t\t}\n\t\t\t\tif (ImGui::MenuItem(\"Reset Settings to Default\", \"\")) {\n\t\t\t\t\t_window.resetSettingsToDefault();\n\t\t\t\t}\n\t\t\t\tImGui::EndMenu();\n\t\t\t}\n\n\t\t\tif (ImGui::BeginMenu(\"Capture\"))\n\t\t\t{\n\n\t\t\t\tif (ImGui::MenuItem(\"Set export directory...\")) {\n\t\t\t\t\tstd::string selectedDirectory;\n\t\t\t\t\tif (showFilePicker(selectedDirectory, FilePickerMode::Directory)) {\n\t\t\t\t\t\tif (!selectedDirectory.empty()) {\n\t\t\t\t\t\t\t_exportPath = selectedDirectory;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (auto & subview : _subViews) {\n\t\t\t\t\tif (ImGui::MenuItem(subview.first.c_str())) {\n\t\t\t\t\t\tcaptureView(subview.second, _exportPath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (auto & subview : _ibrSubViews) {\n\t\t\t\t\tif (ImGui::MenuItem(subview.first.c_str())) {\n\t\t\t\t\t\tcaptureView(subview.second, _exportPath);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (ImGui::MenuItem(\"Export Video\")) {\n\t\t\t\t\tstd::string saveFile;\n\t\t\t\t\tif (showFilePicker(saveFile, FilePickerMode::Save)) {\n\t\t\t\t\t\tconst std::string outputVideo = saveFile + \".mp4\";\n\t\t\t\t\t\tif(!_videoFrames.empty()) {\n\t\t\t\t\t\t\tSIBR_LOG << \"Exporting video to : \" << outputVideo << \" ...\" << std::flush;\n\t\t\t\t\t\t\tFFVideoEncoder vdoEncoder(outputVideo, 30, Vector2i(_videoFrames[0].cols, _videoFrames[0].rows));\n\t\t\t\t\t\t\tfor (int i = 0; i < _videoFrames.size(); i++) {\n\t\t\t\t\t\t\t\tvdoEncoder << _videoFrames[i];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t_videoFrames.clear();\n\t\t\t\t\t\t\tstd::cout << \" Done.\" << std::endl;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tSIBR_WRG << \"No frames to export!! Check save frames in camera options for the view you want to render and play the path and re-export!\" << std::endl;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tImGui::EndMenu();\n\t\t\t}\n\n\t\t\tImGui::EndMainMenuBar();\n\t\t}\n\t}\n\n\tvoid MultiViewManager::toggleGUI()\n\t{\n\t\t_showGUI = !_showGUI;\n\t\tif (!_showGUI) {\n\t\t\tSIBR_LOG << \"[MultiViewManager] GUI is now hidden, use Ctrl+Alt+G to toggle it back on.\" << std::endl;\n\t\t}\n\t\ttoggleSubViewsGUI();\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/MultiViewManager.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <type_traits>\n# include <chrono>\n\n# include \"core/view/Config.hpp\"\n# include \"core/graphics/Window.hpp\"\n# include \"core/graphics/Texture.hpp\"\n# include \"core/view/RenderingMode.hpp\"\n# include \"core/view/FPSCamera.hpp\"\n\n# include \"core/assets/InputCamera.hpp\"\n# include \"core/graphics/Input.hpp\"\n# include \"core/graphics/Image.hpp\"\n# include \"core/graphics/RenderUtility.hpp\"\n# include \"core/assets/CameraRecorder.hpp\"\n# include \"core/view/ViewBase.hpp\"\n# include \"core/graphics/Shader.hpp\"\n# include \"core/view/FPSCounter.hpp\"\n#include \"core/video/FFmpegVideoEncoder.hpp\"\n#include \"InteractiveCameraHandler.hpp\"\n#include <random>\n#include <map>\n\n\nnamespace sibr\n{\n\n\t/**\n\t * MultiViewBase is designed to provide\n\t * more flexibility and with a multi-windows system in mind.\n\t * Once a MultiViewBase is created, you can register standard and\n\t * IBR subviews, providing additional functions for update and\n\t * rendering if needed, along with support for ImGui interfaces.\n\t * MultiViewBase will wrap those views and manage them on screen.\n\t * To support legacy rendering modes and views, we introduce a\n\t * distinction between standard subviews, that will be rendered through\n\t * a call to onRender(Viewport&), and IBR subviews rendered through\n\t * a onRenderIBR(rt, eye) call. This also means that after updating\n\t * (via onUpdate) an IBR subview, you have to return the camera\n\t * that will be used for the onRenderIBR call.\n\t * Note: new IBR views don't have to implement this distinction.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT MultiViewBase\n\t{\n\t\tSIBR_CLASS_PTR(MultiViewBase);\n\n\tpublic:\n\n\t\t/// Update callback for a standard view. Passes the view pointer, the correct input state, and the correct viewport.\n\t\ttypedef  std::function<void(sibr::ViewBase::Ptr &, sibr::Input&, const sibr::Viewport&, const float)> ViewUpdateFunc;\n\t\t/// Update callback for an IBR view, see main description for the return value.\n\t\ttypedef  std::function<sibr::InputCamera(sibr::ViewBase::Ptr &, sibr::Input&, const sibr::Viewport&, const float)> IBRViewUpdateFunc;\n\t\t/// Additional render callback for a subview.\n\t\ttypedef  std::function<void(sibr::ViewBase::Ptr &, const sibr::Viewport&, const IRenderTarget::Ptr& )> AdditionalRenderFunc;\n\n\t\t/*\n\t\t * \\brief Creates a MultiViewBase in a given OS window.\n\t\t * \\param defaultViewRes the default resolution for each subview\n\t\t */\n\t\tMultiViewBase(const Vector2i & defaultViewRes = { 800, 600 });\n\n\t\t/**\n\t\t * \\brief Update subviews and the MultiViewBase.\n\t\t * \\param input The input state to use.\n\t\t */\n\t\tvirtual void\tonUpdate(Input & input);\n\n\t\t/**\n\t\t * \\brief Render the content of the MultiViewBase\n\t\t * \\param win The OS window into which the rendering should be performed.\n\t\t */\n\t\tvirtual void\tonRender(Window& win);\n\n\t\t/**\n\t\t * \\brief Render additional gui\n\t\t * \\param win The OS window into which the rendering should be performed.\n\t\t */\n\t\tvirtual void\tonGui(Window& win);\n\n\t\t/**\n\t\t * \\brief Register a standard subview (for instance a SceneDebugView). It will be rendered via a call to onRender(Viewport) in an implicit rendertarget managed by the MultiViewBase.\n\t\t * \\param title the title of the view.\n\t\t * \\param view a pointer to the view.\t\t\n\t\t * \\param res a custom resolution used for the internal rendering and display. If null, the default value is used.\n\t\t * \\param flags ImGui_WindowFlags to pass to the internal window manager.\n\t\t */\n\t\tvoid\taddSubView(const std::string& title, ViewBase::Ptr view,\n\t\t\t\t\t\tconst Vector2u & res = Vector2u(0,0),\n\t\t\t\t\t\tconst ImGuiWindowFlags flags = 0);\n\n\t\t/**\n\t\t* \\brief Register a standard subview (for instance a SceneDebugView). It will be rendered via a call to onRender(Viewport) in an implicit rendertarget managed by the MultiViewBase.\n\t\t* \\param title the title of the view.\n\t\t* \\param view a pointer to the view.\n\t\t* \\param updateFunc the function that will be called to update your view.\n\t\t*\t\t\t\t\tIt will pass you the view, the correct Input (mouse position\n\t\t*\t\t\t\t\tfrom 0,0 in the top left corner, key presses and mouse clicks\n\t\t*\t\t\t\t\tonly if the cursor is over the view), and the Viewport in the\n\t\t*\t\t\t\t\tOS window.\n\t\t* \\param res a custom resolution used for the internal rendering and display. If null, the default value is used.\n\t\t* \\param flags ImGui_WindowFlags to pass to the internal window manager.\n\t\t*/\n\t\tvoid\taddSubView(const std::string& title, ViewBase::Ptr view,\n\t\t\tconst ViewUpdateFunc updateFunc,\n\t\t\tconst Vector2u & res = Vector2u(0, 0),\n\t\t\tconst ImGuiWindowFlags flags = 0);\n\n\t\t/**\n\t\t* \\brief Register an IBR subview (for instance an ULRView). It will be rendered via a call to onRenderIBR(rt,cam,dst).\n\t\t* \\param title the title of the view.\n\t\t* \\param view a pointer to the view.\n\t\t* \\param res a custom resolution used for the internal rendering. If null, the default value is used.\n\t\t* \\param flags ImGui_WindowFlags to pass to the internal window manager.\n\t\t*/\n\t\tvoid\taddIBRSubView(const std::string& title, ViewBase::Ptr view, \n\t\t\t\t\t\tconst Vector2u & res = Vector2u(0, 0),\n\t\t\t\t\t\tconst ImGuiWindowFlags flags = 0);\n\n\t\t/**\n\t\t* \\brief Register an IBR subview (for instance an ULRView). It will be rendered via a call to onRenderIBR(rt,cam,dst).\n\t\t* \\param title the title of the view.\n\t\t* \\param view a pointer to the view.\n\t\t* \\param updateFunc the function that will be called to update your view.\n\t\t*\t\t\t\t\tIt will pass you the view, the correct Input (mouse position\n\t\t*\t\t\t\t\tfrom 0,0 in the top left corner, key presses and mouse clicks\n\t\t*\t\t\t\t\tonly if the cursor is over the view), and the Viewport in the\n\t\t*\t\t\t\t\tOS window. You should return the camera to use during rendering.\n\t\t* \\param res a custom resolution used for the internal rendering. If null, the default value is used.\n\t\t* \\param flags ImGui_WindowFlags to pass to the internal window manager.\n\t\t*/\n\t\tvoid\taddIBRSubView(const std::string& title, ViewBase::Ptr view,\n\t\t\tconst IBRViewUpdateFunc updateFunc,\n\t\t\tconst Vector2u & res = Vector2u(0, 0),\n\t\t\tconst ImGuiWindowFlags flags = 0);\n\n\t\t/** Add another multi-view system as a subsystem of this one.\n\t\t * \\param title a name for the multiview\n\t\t * \\param multiview the multiview system to add as a subview\n\t\t */\n\t\tvoid\taddSubMultiView(const std::string & title, MultiViewBase::Ptr multiview);\n\n\t\t/**\n\t\t* \\param title\n\t\t* \\return Return viewbase associated with title, will EXIT_ERROR if no view found\n\t\t* \\note This covers both basic and IBR subviews.\n\t\t* \\todo Rename without the IBR prefix\n\t\t*/\n\t\tViewBase::Ptr &\tgetIBRSubView(const std::string& title);\n\n\t\t/**\n\t\t* \\param title\n\t\t* \\return the Viewport associated with title, will EXIT_ERROR if no viewport found\n\t\t* \\note This covers both basic and IBR subviews.\n\t\t* \\todo Rename without the IBR prefix\n\t\t*/\n\t\tViewport & getIBRSubViewport(const std::string &title);\n\n\t\t/**\n\t\t* \\brief Unregister a subview.\n\t\t* \\param title the title of the view to remove.\n\t\t* \\return the view removed from the MultiViewManager.\n\t\t*/\n\t\tViewBase::Ptr removeSubView(const std::string& title);\n\t\n\t\t/**\n\t\t * \\brief Change the rendering mode.\n\t\t * \\param mode The rendering mode to use.\n\t\t */\n\t\tvoid renderingMode(const IRenderingMode::Ptr& mode);\n\n\n\t\t/**\n\t\t * \\brief Define the default rendering and display size for new subviews.\n\t\t * \\param size the default size to use.\n\t\t */\n\t\tvoid setDefaultViewResolution(const Vector2i& size);\n\n\t\t/**\n\t\t * \\brief Returns the default viewport used for subviews rendering.\n\t\t * \\return the current default subview viewport\n\t\t */\n\t\tconst Viewport getViewport(void) const;\n\n\t\t/**\n\t\t * \\brief Returns the last frame time.\n\t\t * \\return the last frame time.\n\t\t */\n\t\tconst float & deltaTime() const { return _deltaTime; }\n\n\t\t/**\n\t\t * \\brief Add a camera handler that will automatically be updated and used by the MultiViewManager for the given subview.\n\t\t * \\param name the name of the subview to which the camera should be associated.\n\t\t * \\param cameraHandler a pointer to the camera handler to register.\n\t\t */\n\t\tvoid addCameraForView(const std::string & name, ICameraHandler::Ptr cameraHandler);\n\n\t\t/**\n\t\t* \\brief Register a function performing additional rendering for a given subview, \n\t\t* called by the MultiViewManager after calling onRender() on the subview.\n\t\t* \\param name the name of the subview to which the function should be associated.\n\t\t* \\param renderFunc the function performing additional rendering..\n\t\t*/\n\t\tvoid addAdditionalRenderingForView(const std::string & name, const AdditionalRenderFunc renderFunc);\n\n\t\t/**\n\t\t* \\brief Count NOT recursively the number of subviews.\n\t\t*/\n\t\tint numSubViews() const;\n\n\t\t/** Place all subviews on a regular grid in the given viewport.\n\t\t * \\param vp the region in which the views should be layed out.\n\t\t */\n\t\tvoid mosaicLayout(const Viewport & vp);\n\n\t\t/** Toggle the display of sub-managers GUIs. */\n\t\tvoid toggleSubViewsGUI();\n\n\t\t/**\n\t\t* \\brief Set the export path.\n\t\t* \\param path path to the directory to use.\n\t\t*/\n\t\tvoid setExportPath(const std::string & path);\n\t\t/**\n\t\t* \\brief captures a View content into an image file.\n\t\t* \\param subviewName a string with the name of the subview.\n\t\t* \\param path the path to save the output.\n\t\t* \\param filename the name of the output file, needs to have an OpenCV compatible file type.\n\t\t*/\n\t\tvoid captureView(const std::string& subviewName, const std::string& path = \"./screenshots\", const std::string& filename = \"\");\n\tprotected:\n\n\t\t/** Internal representation of a subview.\n\t\t * Note: this representation should remain *internal* to the multi view system, avoid any abstraction leak.\n\t\t */\n\t\tstruct SubView {\n\t\t\tViewBase::Ptr view; ///< Pointer to the view.\n\t\t\tRenderTargetRGB::Ptr rt; ///< Destination RT.\n\t\t\tICameraHandler::Ptr handler; ///< Potential camera handler.\n\t\t\tAdditionalRenderFunc renderFunc; ///< Optional additonal rendering function.\n\t\t\tsibr::Viewport viewport; ///< Viewport in the global window.\n\t\t\tImGuiWindowFlags flags = 0; ///< ImGui flags.\n\t\t\tbool shouldUpdateLayout = false; ///< Should the layout be updated at the next frame.\n\n\t\t\t/// Default constructor.\n\t\t\tSubView() = default;\n\n\t\t\t/// Destructor.\n\t\t\tvirtual ~SubView() = default;\n\t\t\t\n\t\t\t/** Constructor.\n\t\t\t *\\param view_ the view\n\t\t\t *\\param rt_ the destination RT\n\t\t\t *\\param viewport_ the viewport\n\t\t\t *\\param name_ the view name\n\t\t\t *\\param flags_ the ImGui flags\n\t\t\t */\n\t\t\tSubView(ViewBase::Ptr view_, RenderTargetRGB::Ptr rt_, const sibr::Viewport viewport_, \n\t\t\t\tconst std::string & name_, const ImGuiWindowFlags flags_);\n\n\t\t\t/** Render the subview.\n\t\t\t *\\param rm the rendering mode to use\n\t\t\t *\\param renderViewport the viewport to use in the destination RT\n\t\t\t */\n\t\t\tvirtual void render(const IRenderingMode::Ptr & rm, const Viewport & renderViewport) const = 0;\n\t\t};\n\n\t\t/** Specialization of Subview for basic views. */\n\t\tstruct BasicSubView final : SubView {\n\t\t\tViewUpdateFunc updateFunc; ///< The update function.\n\n\t\t\t/// Default constructor.\n\t\t\tBasicSubView() : SubView() {};\n\n\t\t\t/// Destructor.\n\t\t\tvirtual ~BasicSubView() = default;\n\t\t\t\n\t\t\t/** Constructor.\n\t\t\t *\\param view_ the view\n\t\t\t *\\param rt_ the destination RT\n\t\t\t *\\param viewport_ the viewport\n\t\t\t *\\param name_ the view name\n\t\t\t *\\param flags_ the ImGui flags\n\t\t\t *\\param f_ the update function\n\t\t\t */\n\t\t\tBasicSubView(ViewBase::Ptr view_, RenderTargetRGB::Ptr rt_, const sibr::Viewport viewport_, \n\t\t\t\tconst std::string & name_, const ImGuiWindowFlags flags_, ViewUpdateFunc f_);\n\n\t\t\t/** Render the subview.\n\t\t\t *\\param rm the rendering mode to use (unused)\n\t\t\t *\\param renderViewport the viewport to use in the destination RT\n\t\t\t */\n\t\t\tvoid render(const IRenderingMode::Ptr & rm, const Viewport & renderViewport) const override;\n\t\t};\n\n\t\t/** Specialization of Subview for views using a render mode (IBR views mainly). */\n\t\tstruct IBRSubView final : SubView {\n\t\t\tIBRViewUpdateFunc updateFunc; ///< The update function.\n\t\t\tsibr::InputCamera cam; ///< The current camera.\n\t\t\tbool defaultUpdateFunc = true; ///< Was the default update function used.\n\n\t\t\t/// Default constructor.\n\t\t\tIBRSubView() : SubView() {};\n\n\t\t\t/// Destructor.\n\t\t\tvirtual ~IBRSubView() = default;\n\t\t\t\n\t\t\t/** Constructor.\n\t\t\t *\\param view_ the view\n\t\t\t *\\param rt_ the destination RT\n\t\t\t *\\param viewport_ the viewport\n\t\t\t *\\param name_ the view name\n\t\t\t *\\param flags_ the ImGui flags\n\t\t\t *\\param f_ the update function\n\t\t\t *\\param defaultUpdateFunc_ was the default update function use (to avoid some collisions)\n\t\t\t */\n\t\t\tIBRSubView(ViewBase::Ptr view_, RenderTargetRGB::Ptr rt_, const sibr::Viewport viewport_, \n\t\t\t\tconst std::string & name_, const ImGuiWindowFlags flags_, IBRViewUpdateFunc f_, const bool defaultUpdateFunc_);\n\n\t\t\t/** Render the subview.\n\t\t\t *\\param rm the rendering mode to use\n\t\t\t *\\param renderViewport the viewport to use in the destination RT\n\t\t\t */\n\t\t\t void render(const IRenderingMode::Ptr & rm, const Viewport & renderViewport) const override;\n\t\t};\n\n\tprotected:\n\n\t\t/** Helper to add an IBR subview.\n\t\t* \\param title the title of the view.\n\t\t* \\param view a pointer to the view.\n\t\t* \\param updateFunc the function that will be called to update your view.\n\t\t*\t\t\t\t\tIt will pass you the view, the correct Input (mouse position\n\t\t*\t\t\t\t\tfrom 0,0 in the top left corner, key presses and mouse clicks\n\t\t*\t\t\t\t\tonly if the cursor is over the view), and the Viewport in the\n\t\t*\t\t\t\t\tOS window. You should return the camera to use during rendering.\n\t\t* \\param res a custom resolution used for the internal rendering. If null, the default value is used.\n\t\t* \\param flags ImGui_WindowFlags to pass to the internal window manager.\n\t\t* \\param defaultFuncUsed a flag denoting if the default function had to be used\n\t\t* */\n\t\tvoid addIBRSubView(const std::string & title, ViewBase::Ptr view, \n\t\t\t\t\t\t\t\t\t\t\tconst IBRViewUpdateFunc updateFunc, const Vector2u & res, \n\t\t\t\t\t\t\t\t\t\t\tconst ImGuiWindowFlags flags, const bool defaultFuncUsed);\n\n\t\t/** Perform rendering for a given subview.\n\t\t *\\param subview the subview to render\n\t\t **/\n\t\tvoid renderSubView(SubView & subview);\n\n\t\t/** Capture a view as an image on disk.\n\t\t *\\param view the view to capture\n\t\t *\\param path the destination direcotry path\n\t\t *\\param filename an optional filename, a timestamp will be appended\n\t\t *\\note if the filename is empty, the name of the view is used, with a timestamp appended.\n\t\t **/\n\t\tstatic void captureView(const SubView & view, const std::string & path = \"./screenshots/\", const std::string & filename = \"\");\n\t\t\n\t\tIRenderingMode::Ptr _renderingMode = nullptr; ///< Rendering mode.\n\t\tstd::map<std::string, BasicSubView> _subViews; ///< Regular subviews.\n\t\tstd::map<std::string, IBRSubView> _ibrSubViews; ///< IBR subviews.\n\t\tstd::map<std::string, std::shared_ptr<MultiViewBase> > _subMultiViews; ///< Nested multi-views.\n\n\t\tVector2i _defaultViewResolution; ///< Default view resolution.\n\n\t\tstd::string _exportPath; ///< Capture output path.\n\t\tstd::vector<cv::Mat> _videoFrames; ///< Video frames.\n\n\t\tstd::chrono::time_point<std::chrono::steady_clock> _timeLastFrame; ///< Last frame time point.\n\t\tfloat _deltaTime; ///< Elapsed time.\n\t\tbool _showSubViewsGui = true; ///< Show the GUI of the subviews.\n\t\tbool _onPause = false; ///< Paused interaction and update.\n\t\tbool _enableGUI = true; ///< Should the GUI be enabled.\n\t};\n\n\t/** A multiview manager is a multi-view system that displays its subviews in an OS window.\n\tuse it as a based for applications with multiple subviews.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT MultiViewManager : public MultiViewBase\n\t{\n\tpublic:\n\t\t/*\n\t\t * \\brief Creates a MultiViewManager in a given OS window.\n\t\t * \\param window The OS window to use.\n\t\t * \\param resize Should the window be resized by the manager to maximize usable space.\n\t\t */\n\t\tMultiViewManager(Window& window, bool resize = true);\n\n\t\t/**\n\t\t * \\brief Update subviews and the MultiViewManager.\n\t\t * \\param input The Input state to use.\n\t\t */\n\t\tvoid\tonUpdate(Input & input) override;\n\n\t\t/**\n\t\t * \\brief Render the content of the MultiViewManager and its interface\n\t\t * \\param win The OS window into which the rendering should be performed.\n\t\t */\n\t\tvoid\tonRender(Window& win) override;\n\n\t\t/**\n\t\t * \\brief Render menus and additional gui\n\t\t * \\param win The OS window into which the rendering should be performed.\n\t\t */\n\t\tvoid\tonGui(Window& win) override;\n\n\tprivate:\n\n\t\t/** Show/hide the GUI. */\n\t\tvoid toggleGUI();\n\n\t\tWindow& _window; ///< The OS window.\n\t\tFPSCounter _fpsCounter; ///< A FPS counter.\n\t\tbool _showGUI = true; ///< Should the GUI be displayed.\n\n\t};\n\n\t///// INLINE /////\n\n\tinline void MultiViewBase::setDefaultViewResolution(const Vector2i& size) {\n\t\t_defaultViewResolution = size;\n\t}\n\n\t\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/Orbit.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"Orbit.hpp\"\n#include <boost/filesystem.hpp>\n#include \"core/graphics/Input.hpp\"\n#include \"core/graphics/Viewport.hpp\"\n#include \"core/raycaster/CameraRaycaster.hpp\" \n#include \"core/graphics/Window.hpp\"\n#include \"core/graphics/Mesh.hpp\"\n#include \"core/view/UIShortcuts.hpp\"\n\n# define SIBR_ORBIT_INTERPOLATE_FRAMES 900\n\nnamespace sibr {\n\n\tOrbit::Orbit(void) : _hasBeenInitialized(false), _orbitPointClicked(false) {\n\t\tUIShortcuts::global().add(\"[Orbit cam] alt+click\", \"Select new orbit center.\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 4\", \"move left\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 6\", \"move right\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 8\", \"move down\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 2\", \"move up\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 7\", \"rotate left \");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 9\", \"rotate right \");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 1\", \"get closer\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 3\", \"get further\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 5\", \"flip up vector (look upside down)\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] alt+1-9\", \"automatic move\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] 0\", \"stop automatic move, restore previous cam\");\n\t\tUIShortcuts::global().add(\"[Orbit cam] .\", \"stop automatic move, keep current cam\");\n\t}\n\n\tvoid Orbit::update(const sibr::Input & input, const std::shared_ptr<sibr::Raycaster> raycaster) {\n\t\n\t\tif (!_hasBeenInitialized) { return; }\n\n\t\tif (raycaster != nullptr && input.mouseButton().isReleased(sibr::Mouse::Left)\n\t\t\t&& input.key().isActivated(sibr::Key::LeftAlt)) {\n\t\t\tupdateOrbitParameters(input, raycaster);\n\t\t}\n\n\t\tconst float sensibility = 64.0f;\n\n\t\tif (input.key().isActivated(sibr::Key::LeftAlt)) {\n\t\t\t//orbit.factor = 0;\n\t\t\tif (input.key().isReleased(sibr::Key::KPNum4) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::Num4) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::F)  // for laptops\n\t\t\t\t) {\n\t\t\t\t_orbit.status = OrbitParameters::FORWARD_X;\n\t\t\t\t_orbit.direction = OrbitParameters::ACW;\n\t\t\t}\n\t\t\telse if (input.key().isReleased(sibr::Key::KPNum6) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::Num6)) {\n\t\t\t\t_orbit.status = OrbitParameters::FORWARD_X;\n\t\t\t\t_orbit.direction = OrbitParameters::CW;\n\t\t\t}\n\t\t\telse if (input.key().isReleased(sibr::Key::KPNum2) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::Num2)) {\n\t\t\t\t_orbit.status = OrbitParameters::FORWARD_Y;\n\t\t\t\t_orbit.direction = OrbitParameters::ACW;\n\t\t\t}\n\t\t\telse if (input.key().isReleased(sibr::Key::KPNum8) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::Num8)) {\n\t\t\t\t_orbit.status = OrbitParameters::FORWARD_Y;\n\t\t\t\t_orbit.direction = OrbitParameters::CW;\n\t\t\t}\n\t\t\telse if (input.key().isReleased(sibr::Key::KPNum7) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::Num7)) {\n\t\t\t\t_orbit.status = OrbitParameters::FORWARD_Z;\n\t\t\t\t_orbit.direction = OrbitParameters::ACW;\n\t\t\t}\n\t\t\telse if (input.key().isReleased(sibr::Key::KPNum9) ||\n\t\t\t\tinput.key().isReleased(sibr::Key::Num9)) {\n\t\t\t\t_orbit.status = OrbitParameters::FORWARD_Z;\n\t\t\t\t_orbit.direction = OrbitParameters::CW;\n\t\t\t}\n\t\t}\n\t\telse if ((input.key().isReleased(sibr::Key::KPNum0) || input.key().isReleased(sibr::Key::Num0))\n\t\t\t&& _orbit.status != OrbitParameters::STATIC) {\n\t\t\t_orbit.status = OrbitParameters::STATIC;\n\t\t}\n\t\telse if (input.key().isReleased(sibr::Key::KPDecimal)\n\t\t\t&& _orbit.status != OrbitParameters::STATIC) {\n\t\t\t_orbit.keepCamera = true;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum4) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num4)) {\n\t\t\t_orbit.theta = -(float)M_2_PI / sensibility;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum6) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num6)) {\n\t\t\t_orbit.theta = (float)M_2_PI / sensibility;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum2) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num2)) {\n\t\t\t_orbit.phi = -(float)M_2_PI / sensibility;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum8) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num8)) {\n\t\t\t_orbit.phi = (float)M_2_PI / sensibility;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum7) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num7)) {\n\t\t\t_orbit.roll = -(float)M_2_PI / sensibility;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum9) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num9)) {\n\t\t\t_orbit.roll = (float)M_2_PI / sensibility;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum1) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num1)) {\n\t\t\t_orbit.radius *= 0.98f;\n\t\t}\n\t\telse if (input.key().isActivated(sibr::Key::KPNum3) ||\n\t\t\tinput.key().isActivated(sibr::Key::Num3)) {\n\t\t\t_orbit.radius *= 1.02f;\n\t\t}\n\t\telse if (input.key().isReleased(sibr::Key::KPNum5) ||\n\t\t\tinput.key().isReleased(sibr::Key::Num5)) {\n\t\t\tif (_orbit.status == OrbitParameters::STATIC) {\n\t\t\t\t_orbit.flip();\n\t\t\t\tstd::cout << \"\\t orbit flip ! \" << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (_orbit.direction == OrbitParameters::CW) {\n\t\t\t\t\t_orbit.direction = OrbitParameters::ACW;\n\t\t\t\t\tstd::cout << \"\\t orbit anti clockwise  \" << std::endl;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_orbit.direction = OrbitParameters::CW;\n\t\t\t\t\tstd::cout << \"\\t orbit clockwise  \" << std::endl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinterpolateOrbit();\n\t}\n\n\tvoid Orbit::interpolateOrbit() {\n\t\tusing namespace Eigen;\n\n\t\tfloat k = (_orbit.factor) / (float)(SIBR_ORBIT_INTERPOLATE_FRAMES);\n\t\tbool keepCam = _orbit.keepCamera;\n\n\t\tfloat theta = (_orbit.status == OrbitParameters::FORWARD_X ? (float)(SIBR_2PI  * k) : _orbit.theta);\n\t\tfloat phi = (_orbit.status == OrbitParameters::FORWARD_Y ? (float)(SIBR_2PI  * k) : _orbit.phi);\n\t\tfloat roll = (_orbit.status == OrbitParameters::FORWARD_Z ? (float)(SIBR_2PI  * k) : _orbit.roll);\n\n\t\tsibr::Vector3f dir = -(_orbit.zAxis);\n\t\tsibr::Quaternionf qRoll(AngleAxisf(roll, _orbit.zAxis));\n\t\tsibr::Quaternionf qTheta(AngleAxisf(theta, _orbit.yAxis));\n\t\tsibr::Quaternionf qPhi(AngleAxisf(phi, _orbit.xAxis));\n\n\t\tsibr::Vector3f center = _orbit.center;\n\t\tsibr::Vector3f Eye = center + _orbit.radius*((qTheta*qPhi)*(dir));\n\t\tsibr::Vector3f up(qRoll*_orbit.yAxis);\n\n\t\tsibr::Camera n(_orbit.initialCamera);\n\t\tn.setLookAt(Eye, center, up);\n\t\tn.aspect(_orbit.initialCamera.aspect());\n\t\n\n\t\tif (_orbit.status == OrbitParameters::STATIC || keepCam) {\n\t\t\tsibr::Quaternionf qTot = qTheta*qPhi*qRoll;\n\t\t\t_orbit.xAxis = qTot*_orbit.xAxis;\n\t\t\t_orbit.yAxis = qTot*_orbit.yAxis;\n\t\t\t_orbit.zAxis = qTot*_orbit.zAxis;\n\n\t\t\t_orbit.theta = 0;\n\t\t\t_orbit.phi = 0;\n\t\t\t_orbit.roll = 0;\n\t\t}\n\t\telse {\n\t\t\t_orbit.factor += _orbit.direction;\n\t\t}\n\n\t\tif (keepCam) {\n\t\t\t_orbit.status = OrbitParameters::STATIC;\n\t\t\t_orbit.keepCamera = false;\n\t\t}\n\n\t\t_currentCamera = sibr::InputCamera(n, _currentCamera.w(), _currentCamera.h());\n\t}\n\n\tvoid Orbit::updateOrbitParameters(const sibr::Input& input, std::shared_ptr<sibr::Raycaster> raycaster)\n\t{\n\t\n\t\t// Clicked pixel (might need to check against viewport ?)\n\t\tconst float px = (float)input.mousePosition().x();\n\t\tconst float py = (float)input.mousePosition().y();\n\n\t\tsibr::Vector3f dx;\n\t\tsibr::Vector3f dy;\n\t\tsibr::Vector3f upLeftOffset;\n\n\t\tsibr::CameraRaycaster::computePixelDerivatives(_currentCamera, dx, dy, upLeftOffset);\n\t\tconst sibr::Vector3f worldPos = px*dx + py*dy + upLeftOffset;\n\t\t\n\t\t// Cast a ray.\n\t\tif (raycaster != nullptr) {\n\t\t\tsibr::Vector3f dir =  worldPos - _currentCamera.position();\n\t\t\t//sibr::Vector3f dir = sibr::CameraRaycaster::computeRayDir(_currentCamera, input.mousePosition().cast<float>()).normalized();\n\t\t\tsibr::RayHit hit = raycaster->intersect(sibr::Ray(_currentCamera.position(), dir));\n\t\n\t\t\t// If hit at the proxy surface, compute the corresponding worls position, save it.\n\t\t\tif (hit.hitSomething()) {\n\t\t\t\t_orbit.center = _currentCamera.position() + hit.dist()*dir.normalized();\n\n\t\t\t\t// \\todo TODO: SR reimplement the fitting of planes by either passing cameras all the way down, or something else.\n\t\t\t\t//_orbit.planePointCams = computeFittingPlaneCameras(_orbit.center);\n\t\t\t\t_orbit.yAxis = _currentCamera.up(); // _orbit.planePointCams.xyz();\n\n\t\t\t\t//cheap trick to solve the ambiguity of the up direction\n\t\t\t\tif (_orbit.yAxis.dot(_currentCamera.up()) < 0) {\n\t\t\t\t\t_orbit.yAxis = -_orbit.yAxis;\n\t\t\t\t}\n\n\t\t\t\t_orbit.zAxis = dir.normalized();\n\t\t\t\t_orbit.xAxis = _orbit.yAxis.cross(_orbit.zAxis);\n\t\t\t\t_orbit.radius = (_orbit.initialCamera.position() - _orbit.center).norm();\n\t\t\t\t_orbit.initialCamera = _currentCamera;\n\n\t\t\t\t_orbitPointClicked = true;\n\t\t\t}\n\n\t\t}\n\t\t\n\t}\n\n\tvoid Orbit::update(const sibr::Input & input, const float deltaTime, const Viewport & viewport)\n\t{\n\t\tupdate(input);\n\t}\n\n\tconst sibr::InputCamera & Orbit::getCamera( void ) const\n\t{\n\t\tif( !_hasBeenInitialized ){\n\t\t\tSIBR_ERR << \" Orbit : camera not initialized before use\" << std::endl\n\t\t\t\t<< \"\\t you should use either fromMesh(), fromCamera() or load() \" << std::endl;\n\t\t}\n\t\treturn _currentCamera;\n\n\t}\n\n\tvoid Orbit::fromCamera( const sibr::InputCamera & cam,  const std::shared_ptr<sibr::Raycaster> raycaster )\n\t{\n\t\t_orbit.initialCamera = cam;\n\t\t_currentCamera = cam;\n\t\t_hasBeenInitialized = true;\n\n\t\t// If no point has already been selected by the user, we simply pick it automatically by intersecting cam dir and the mesh.\n\t\tif (!_orbitPointClicked) {\n\t\t\t// We need to transfer the camera parameters to the orbit.\n\t\t\tupdateOrbitParametersCentered(raycaster);\n\t\t}\n\t}\n\n\tvoid\tOrbit::updateOrbitParametersCentered(const std::shared_ptr<sibr::Raycaster> raycaster)\n\t{\n\t\tif (raycaster != nullptr) {\n\t\t\tsibr::RayHit hit = raycaster->intersect(sibr::Ray(_currentCamera.position(), _currentCamera.dir()));\n\t\t\t// If hit at the proxy surface, compute the corresponding world position, save it.\n\t\t\tif (hit.hitSomething()) {\n\t\t\t\tsibr::Vector3f intersection(_currentCamera.position() + hit.dist()* _currentCamera.dir().normalized());\n\t\t\t\t_orbit.center = intersection;\n\t\t\t\t_orbit.yAxis = _currentCamera.up();\n\t\t\t\t_orbit.zAxis = _currentCamera.dir();\n\t\t\t\t_orbit.xAxis = _currentCamera.right();\n\t\t\t\t_orbit.radius = (_currentCamera.position() - _orbit.center).norm();\n\t\t\t\t_orbit.initialCamera = _currentCamera;\n\t\t\t\t//orbitPointClicked -->; don't set it, the center is picked automatically.\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\n\tsibr::Vector4f Orbit::computeFittingPlaneCameras(sibr::Vector3f & clickedPoint, const std::vector<InputCamera::Ptr> & cams)\n\t{\n\t\tusing namespace Eigen;\n\n\t\tstd::vector<sibr::Vector3f> positions(cams.size());\n\n\t\tfor (int i = 0; i<(int)cams.size(); ++i) {\n\t\t\tpositions.at(i) = cams.at(i)->position();\n\t\t}\n\t\tpositions.push_back(clickedPoint);\n\n\t\tstd::vector<sibr::Vector3f> colors(positions.size(), sibr::Vector3f(1, 0, 0));\n\n\t\tMatrixXf data(3, positions.size());\n\t\tint posId = 0;\n\t\tfor (auto & pos : positions) {\n\t\t\tdata(0, posId) = pos.x();\n\t\t\tdata(1, posId) = pos.y();\n\t\t\tdata(2, posId) = pos.z();\n\t\t\t++posId;\n\t\t}\n\n\t\tsibr::Vector3f center = data.rowwise().mean();\n\t\tEigen::MatrixXf dataCentered = data.colwise() - center;\n\n\t\tJacobiSVD<MatrixXf> svd(dataCentered, ComputeFullU | ComputeThinV);\n\n\t\t//the normal to the fitting plane is the eigenvector associated to the smallest eigenvalue (i.e. the direction in which the variance of all points is the smallest)\n\t\tsibr::Vector3f  normal = svd.matrixU().col(2);\n\t\tnormal.normalize();\n\t\tsibr::Vector3f n(normal);\n\n\t\t//the fitting plane contains the mean point\n\t\tfloat d = -center.dot(normal);\n\n\t\t\n\t\tstd::cout << \" \\t  plane ( clicked point + input cams ) : \" << n.x() << \"*x + \" << n.y() << \"*y + \" << n.z() << \"*z + \" << d << std::endl;\n\n\t\treturn sibr::Vector4f(n.x(), n.y(), n.z(), d);\n\t}\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/Orbit.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include <memory>\n#include <fstream>\n\n#include \"Config.hpp\"\n#include \"core/graphics/Shader.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include \"ICameraHandler.hpp\"\n\n\nnamespace sibr {\n\n\tclass Viewport;\n\tclass Mesh;\n\tclass Input;\n\tclass Raycaster;\n\n\t/**\n\t * Interactive camera that allow the user to roate around an object using the keypad.\n\t * Commands:\n\n\t\tto enable/disable the orbit (note that using at least once ( atl + click ) to retrieve a 3D point on the proxy is mandatory before enabling the orbit) :\n\t\tb\n\t\tin static mode (default mode) :\n\t\t5 to flip the orbit (might be the first thing to do if all commands seem broken/reversed, it is needed because there is an ambiguity when using the normal of the plan containing the input cameras and the clicked point)\n\t\t4 or 6 to rotate towards current camera x-axis\n\t\t2 or 8 to rotate towards current camera y-axis\n\t\t7 or 9 to rotate towards current camera z-axis\n\t\t1 or 3 to zoom in or out\n\t\tin dynamic mode ( rotates without interruption around an axis )  :\n\t\talt + ( 4 or 6 ) to rotate towards current camera x-axis\n\t\talt + ( 2 or 8 ) to rotate towards current camera y-axis\n\t\talt + ( 7 or 9 ) to rotate towards current camera z-axis\n\t\t5 to inverse the direction (same axis)\n\t\t0 to switch back to static mode with initial camera\n\t\t. to switch back to static mode with current camera\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT Orbit : public ICameraHandler\n\t{\n\t\n\tpublic:\n\n\t\t/**\n\t\t Create an orbit centered at (0,0,0) with basic initial parameters.\n\t\t It is recommended to call fromCamera after that to setup the orbit with a valid set of parameters.\n\t\t*/\n\t\tOrbit( void );\n\n\t\t/**\n\t\t Setup the orbit so that its camera has the same pose as the argument camera. A raycaster is required to find the center of the orbit.\n\t\t\\param cam the reference camera\n\t\t\\param raycaster raycaster to use for centering intersection tests.\n\t\t*/\n\t\tvoid fromCamera(const sibr::InputCamera & cam, const std::shared_ptr<sibr::Raycaster> raycaster);\n\n\t\t/**\n\t\t\tUpdate the orbit camera based on the user input (keyboard). Can require a raycaster if the user is alt-clicking to select a new orbit center.\n\t\t\\param input user input\n\t\t\\param raycaster optional raycaster\n\t\t*/\n\t\tvoid update( const sibr::Input & input, const std::shared_ptr<sibr::Raycaster> raycaster = std::shared_ptr<sibr::Raycaster>());\n\n\t\t/** Update the camera handler state.\n\t\t\\param input user input\n\t\t\\param deltaTime time elapsed since last udpate\n\t\t\\param viewport view viewport\n\t\t*/\n\t\tvirtual void update(const sibr::Input & input, const float deltaTime, const Viewport & viewport) override;\n\t\t\n\t\t/** \\return the current camera. */\n\t\tvirtual const sibr::InputCamera & getCamera( void ) const override;\n\n\tprivate:\n\n\t\t/** Internal orbit parameters. */\n\t\tstruct OrbitParameters\n\t\t{\n\t\t\t/** Motion direction: Clockwise, AntiClockWis e*/\n\t\t\tenum OrbitDirection { CW = 1, ACW = -1 };\n\t\t\t/** Orbit current motion status. */\n\t\t\tenum OrbitStatus { STATIC, FORWARD_X, FORWARD_Y, FORWARD_Z };\n\n\t\t\t/** Default constructor. */\n\t\t\tOrbitParameters(void) : factor(0), status(STATIC),\n\t\t\t\tcenter(sibr::Vector3f(0.0f, 0.0f, 0.0f)), radius(1.0f), theta(0), phi(0), roll(0), direction(CW), keepCamera(false)\n\t\t\t{}\n\n\t\t\t/** Flip motion. */\n\t\t\tvoid flip(void) {\n\t\t\t\tyAxis = -yAxis;\n\t\t\t\txAxis = yAxis.cross(zAxis);\n\t\t\t}\n\n\t\t\tbool\t\t\t\t\t\t\t\tkeepCamera; ///< ?\n\t\t\tint\t\t\t\t\t\t\t\t\tfactor; ///< Interpolation ID.\n\n\t\t\tOrbitStatus\t\t\t\t\t\t\tstatus; ///< Current status.\n\t\t\tOrbitDirection\t\t\t\t\t\tdirection; ///< Current motion direction.\n\n\t\t\tsibr::Vector3f\t\t\t\t\t\tcenter; ///< Orbit center.\n\t\t\tsibr::Vector3f\t\t\t\t\t\txAxis; ///< Orbit X axis.\n\t\t\tsibr::Vector3f\t\t\t\t\t\tyAxis; ///< Orbit Y axis.\n\t\t\tsibr::Vector3f\t\t\t\t\t\tzAxis; ///< Orbit Z axis.\n\n\t\t\tfloat\t\t\t\t\t\t\t\tradius; ///< Orbit radius.\n\t\t\tfloat\t\t\t\t\t\t\t\ttheta, phi, roll; ///< Orbit angles.\n\n\t\t\tsibr::Camera\t\t\t\t\t\tinitialCamera; ///< Starting camera.\n\t\t\tsibr::Vector4f\t\t\t\t\t\tplanePointCams; ///< Fitted plane points.\n\n\t\t};\n\n\t\t/**\n\t\t*\tCompute new camera pose from current orbit parameters.\n\t\t*/\n\t\tvoid interpolateOrbit();\n\n\t\t/**\n\t\t*\tUpdates the orbit's center and camera pose, by casting a ray from the clicked point (in Input) to the mesh.\n\t\t*\t\\param input user input\n\t\t*\t\\param raycaster scene raycaster\n\t\t*/\n\t\tvoid updateOrbitParameters(const sibr::Input& input, const std::shared_ptr<sibr::Raycaster> raycaster);\n\n\t\t/**\n\t\t*\tUpdates the orbit's center and camera pose, by casting a ray from the center of the screen to the mesh.\n\t\t*\t\\param raycaster scene raycaster\n\t\t*/\n\t\tvoid updateOrbitParametersCentered(const std::shared_ptr<sibr::Raycaster> raycaster);\n\n\t\t/** \n\t\t*\tCompute the best fitting plane of the clicked points plus the input cams positions.\n\t\t*\t\\param clickedPoint point clicked by the user\n\t\t*\t\\param cams reference cameras\n\t\t*/\n\t\tstatic sibr::Vector4f computeFittingPlaneCameras(sibr::Vector3f& clickedPoint, const std::vector<InputCamera::Ptr>& cams);\n\n\t\tbool _hasBeenInitialized; ///< Has the orbit been initialized.\n\t\tbool _orbitPointClicked; ///< Has the user clicked on a point in the scene.\n\t\tsibr::InputCamera _currentCamera; ///< Current camera.\n\t\tOrbitParameters _orbit; ///< Parameters.\n\t\n\t};\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/RenderingMode.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/RenderUtility.hpp\"\n#include \"core/view/RenderingMode.hpp\"\n#include \"core/assets/Resources.hpp\"\n#include \"core/graphics/Image.hpp\"\n\nnamespace sibr\n{\n\tMonoRdrMode::MonoRdrMode( void )\n\t{\n\t\t_clear = true;\n\t\t_quadShader.init(\"Texture\",\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"texture.vp\")),\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"texture.fp\")));\n\t}\n\n\tvoid\tMonoRdrMode::render( ViewBase& view, const sibr::Camera& eye, const sibr::Viewport& viewport, IRenderTarget* optDest )\n\t{\n\t\t/// TODO: clean everything. Resolution handling.\n\n\t\t//int w = (int)viewport.finalWidth();\n\t\t//int h = (int)viewport.finalHeight();\n\n\t\t//if (!_destRT || _destRT->w() != w || _destRT->h() != h)\n\t\t//\t_destRT.reset( new RenderTarget(w, h) );\n\t\t//\n\t\t//view.onRenderIBR(*_destRT, eye);\n\t\t//_destRT->unbind();\n\n\t\t//_quadShader.begin();\n\t\t////if(_ibr->isPortraitAcquisition() && !_ibr->args().fullscreen)\n\t\t////\tglViewport(0,0, _h, _w);\n\t\t////else\n\t\t////\tglViewport(0,0, _w * _ibr->args().rt_factor, (_ibr->args().fullscreen ? screenHeight : _h) * _ibr->args().rt_factor);\n\t\t//viewport.use();\n\t\t////glViewport(0,0, size().x(), size().y());\n\n\t\t//glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _destRT->texture());\n\t\t//RenderUtility::renderScreenQuad(false /*_ibr->isPortraitAcquisition()*/);\n\t\t//_quadShader.end();\n\n\t\tint w = (int)viewport.finalWidth();\n\t\tint h = (int)viewport.finalHeight();\n\n\t\tif (!_destRT)// || _destRT->w() != w || _destRT->h() != h)\n\t\t\t_destRT.reset( new RenderTarget(w, h, SIBR_GPU_LINEAR_SAMPLING) );\n\t\tglViewport(0, 0, w, h);\n\t\t_destRT->bind();\n\n\t\tif( _clear ) {\n\t\t\tviewport.clear();\n\t\t\t// blend with previous\n\t\t\tview.preRender(*_destRT);\n\t\t}\n\t\telse {\n\t\t\t// can come from somewhere else\n\t\t\tview.preRender(*_prevR);\n\t\t}\n\n\t\tview.onRenderIBR(*_destRT, eye);\n\t\t_destRT->unbind();\n\n\t\t//show(*_destRT, \"before\");\n\n\t\t//glEnable (GL_BLEND);\n\t\t//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\n\t\tglDisable (GL_BLEND);\n\t\tglDisable(GL_DEPTH_TEST);\n\t\t//glDepthMask(GL_FALSE);\n\n\t\t//glEnable (GL_BLEND);\n\t\t//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\t_quadShader.begin();\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _destRT->texture());\n\n\t\tif (optDest) // Optionally you can render to another RenderTarget\n\t\t{\n\t\t\tglViewport(0, 0, optDest->w(), optDest->h());\n\t\t\toptDest->bind();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tviewport.bind();\n\t\t}\n\n\t\tRenderUtility::renderScreenQuad(/*_ibr->isPortraitAcquisition()*/);\n\n\t\tif (optDest) // Optionally you can render to another RenderTarget\n\t\t\toptDest->unbind();\n\n\t\t_quadShader.end();\n\n#if 0\nstd::cerr <<\"End of render pass 1\" << std::endl;\n\t\tshow(*(_destRT));\n#endif\n\n\t}\n\n\tStereoAnaglyphRdrMode::StereoAnaglyphRdrMode( void )\n\t{\n\t\t_clear = true;\n\t\t_stereoShader.init(\"StereoAnaglyph\",\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"anaglyph.vp\")),\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"anaglyph.fp\")));\n\t\t_leftRT.reset(), _rightRT.reset();\n/* default values good for Street-10 scene */\n\n\t\t_focalDist = 100;\n\t\t_eyeDist = (float)0.065; /* meters */\n\t}\n\n\tvoid\tStereoAnaglyphRdrMode::render( ViewBase& view, const sibr::Camera& eye, const sibr::Viewport& viewport, IRenderTarget* optDest)\n\t{\n\t\tint w = (int)viewport.finalWidth();\n\t\tint h = (int)viewport.finalHeight();\n\n\t\tif (!_leftRT || _leftRT->w() != w || _leftRT->h() != h)\n\t\t\t_leftRT.reset( new RenderTarget(w, h) );\n\t\tif (!_rightRT || _rightRT->w() != w || _rightRT->h() != h)\n\t\t\t_rightRT.reset( new RenderTarget(w, h) );\n\n\t\tInputCamera leye(eye, w, h);\n\t\tInputCamera reye(eye, w, h);\n\t\tleye.size(w, h); reye.size(w, h);\n\t\tleye.position(eye.position()-_eyeDist*eye.right());\n\n\t\t// setup left eye\n\t\tleye.setStereoCam(true, _focalDist, _eyeDist);\n\t\t_leftRT->bind();\n\t\tif( _clear )  {\n\t\t\tviewport.clear();\n\t\t\tview.preRender(*_leftRT);\n\t\t}\n\t\telse {\n\t\t\t// can come from somewhere else\n\t\t\tview.preRender(*_prevL);\n\t\t}\n\n\t\tview.onRenderIBR(*_leftRT, leye);\n\t\t_leftRT->unbind();\n\n\t\t// setup right eye\n\t\treye.position(eye.position()+_eyeDist*eye.right());\n\t\treye.setStereoCam(false, _focalDist, _eyeDist);\n\n\t\t// render right eye\n\t\t_rightRT->bind();\n\t\tif( _clear ) {\n\t\t\tviewport.clear();\n\t\t\tview.preRender(*_rightRT);\n\t\t}\n\t\telse {\n\t\t\t// can come from somewhere else\n\t\t\tview.preRender(*_prevR);\n\t\t}\n\t\tview.onRenderIBR(*_rightRT, reye);\n\t\t_rightRT->unbind();\n\n\t\tglDisable (GL_BLEND);\n\t\tglDisable(GL_DEPTH_TEST);\n\n\t\t_stereoShader.begin();\n\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _leftRT->texture());\n\t\tglActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _rightRT->texture());\n\n\t\tif (optDest) // Optionally you can render to another RenderTarget\n\t\t{\n\t\t\tglViewport(0, 0, optDest->w(), optDest->h());\n\t\t\toptDest->bind();\n\t\t}\n\n\t\tRenderUtility::renderScreenQuad();\n\n\t\tif (optDest) // Optionally you can render to another RenderTarget\n\t\t\toptDest->unbind();\n\n\t\t_stereoShader.end();\n\n\t}\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/RenderingMode.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/graphics/Camera.hpp\"\n# include \"core/graphics/Viewport.hpp\"\n# include \"core/graphics/Texture.hpp\"\n# include \"core/view/Config.hpp\"\n# include \"core/view/ViewBase.hpp\"\n# include \"core/graphics/Image.hpp\"\n# include \"core/graphics/Shader.hpp\"\n# include \"core/assets/InputCamera.hpp\"\n\nnamespace sibr\n{\n\t/**\n\t*\tRendering mode manages the rendertarget and camera fed to an IBR view. Can be used to render a view using a stereoscopic mode (anaglyph or VR).\n\t*   \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT IRenderingMode\n\t{\n\t\tSIBR_CLASS_PTR(IRenderingMode);\n\n\tpublic:\n\t\ttypedef RenderTargetRGB RenderTarget;\n\tpublic:\n\t\t/// Destructor.\n\t\tvirtual ~IRenderingMode( void ) { }\n\n\t\t/** Perform rendering of a view.\n\t\t *\\param view the view to render\n\t\t *\\param eye the current camera\n\t\t *\\param viewport the current viewport\n\t\t *\\param optDest an optional destination RT \n\t\t */\n\t\tvirtual void\trender( \n\t\t\tViewBase& view, const sibr::Camera& eye, const sibr::Viewport& viewport, \n\t\t\tIRenderTarget* optDest = nullptr) = 0;\n\n\t\t/** Get the current rendered image as a CPU image\n\t\t *\\param current_img will contain the content of the RT */\n\t\tvirtual void destRT2img( sibr::ImageRGB& current_img ) = 0;\n\n\tprotected:\n\t\tstd::unique_ptr<RenderTargetRGB>\t_prevL, _prevR; ///< prev RT to link renderers across different views in multipass\n\n\tpublic:\n\t\tbool _clear; ///< Should the dst RT be cleared before rendering.\n\n\t\t/** Set common previous step RT.\n\t\t *\\param p the RT\n\t\t */\n\t\tvoid\tsetPrev(const std::unique_ptr<RenderTargetRGB>& p) { std::cerr<<\"ERROR \" << std::endl; }\n\t\t/** Set left and right previous step RTs.\n\t\t *\\param pl the left eye RT\n\t\t *\\param pr the right eye RT\n\t\t */\n\t\tvoid\tsetPrevLR(const std::unique_ptr<RenderTargetRGB>& pl, const std::unique_ptr<RenderTargetRGB>& pr) { std::cerr<<\"ERROR \" << std::endl;}\n\n\t\t/** \\return the left eye (or common) RT. */\n\t\tvirtual const std::unique_ptr<RenderTargetRGB>&\tlRT() = 0;\n\t\t/** \\return the right eye (or common) RT. */\n\t\tvirtual const std::unique_ptr<RenderTargetRGB>&\trRT() = 0;\n\n\t};\n\n\t/** Default rendering mode: monoview, passthrough.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT MonoRdrMode : public IRenderingMode\n\t{\n\tpublic:\n\n\t\t/// Constructor.\n\t\tMonoRdrMode( void );\n\n\t\t/** Perform rendering of a view.\n\t\t *\\param view the view to render\n\t\t *\\param eye the current camera\n\t\t *\\param viewport the current viewport\n\t\t *\\param optDest an optional destination RT\n\t\t */\n\t\tvoid\trender( ViewBase& view, const sibr::Camera& eye, const sibr::Viewport& viewport, IRenderTarget* optDest = nullptr);\n\n\t\t/** Get the current rendered image as a CPU image\n\t\t *\\param current_img will contain the content of the RT */\n\t\tvoid destRT2img( sibr::ImageRGB& current_img )\n\t\t{\n\t\t\t_destRT->readBack(current_img);\n\t\t\treturn;\n\t\t}\n\n\t\t/** \\return the common RT. */\n\t\tvirtual const std::unique_ptr<RenderTargetRGB>&\tlRT() { return _destRT; }\n\t\t/** \\return the common RT. */\n\t\tvirtual const std::unique_ptr<RenderTargetRGB>&\trRT() { return _destRT; }\n\n\tprivate:\n\t\tsibr::GLShader\t\t\t\t\t\t\t_quadShader; ///< Passthrough shader.\n\t\tstd::unique_ptr<RenderTarget>\t\t_destRT; ///< Common destination RT.\n\t};\n\n\t/**\n\t *Stereo rendering mode: two slightly shifted views are rendered and composited as anaglyphs.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT StereoAnaglyphRdrMode : public IRenderingMode\n\t{\n\tpublic:\n\n\t\t/// Constructor.\n\t\tStereoAnaglyphRdrMode( void );\n\n\t\t/** Perform rendering of a view.\n\t\t *\\param view the view to render\n\t\t *\\param eye the current camera\n\t\t *\\param viewport the current viewport\n\t\t *\\param optDest an optional destination RT\n\t\t */\n\t\tvoid\trender( ViewBase& view, const sibr::Camera& eye, const sibr::Viewport& viewport, IRenderTarget* optDest = nullptr);\n\n\t\t/** Set the focal distance.\n\t\t\\param focal focal distance\n\t\t*/\n\t\tvoid\tsetFocalDist(float focal) { _focalDist = focal; }\n\n\t\t/** Set the distance between the two eyes.\n\t\t\\param iod intra-ocular distance\n\t\t*/\n\t\tvoid\tsetEyeDist(float iod) { _eyeDist = iod; }\n\n\t\t/** \\return the focal distance */\n\t\tfloat\tfocalDist()\t{ return _focalDist; }\n\t\t/** \\return the intra-ocular distance */\n\t\tfloat\teyeDist()\t{ return _eyeDist; }\n\n\t\t/** Get the current rendered image as a CPU image (empty).\n\t\t *\\param current_img will contain the content of the RT */\n\t\tvoid destRT2img( sibr::ImageRGB& current_img ){};\n\n\t\t/** \\return the left eye RT. */\n\t\tvirtual const std::unique_ptr<RenderTargetRGB>&\tlRT() { return _leftRT; }\n\t\t/** \\return the right eye RT. */\n\t\tvirtual const std::unique_ptr<RenderTargetRGB>&\trRT() { return _rightRT; }\n\n\tprivate:\n\t\tsibr::GLShader\t\t_stereoShader; ///< Anaglyph shader.\n\t\tRenderTarget::UPtr\t_leftRT, _rightRT; ///< Each eye RT.\n\t\tfloat\t\t\t\t_focalDist, _eyeDist; ///< Focal and inter-eyes distances.\n\t};\n\n\t///// DEFINITIONS /////\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/SceneDebugView.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n# include \"core/view/SceneDebugView.hpp\"\n# include \"core/graphics/RenderUtility.hpp\"\n# include \"core/graphics/Input.hpp\"\n# include \"core/graphics/GUI.hpp\"\n#include <core/raycaster/CameraRaycaster.hpp>\n\n#include <sstream>\n\nnamespace sibr\n{\n\n\tMesh::Ptr generateCamFrustum(const InputCamera & cam, float near, float far, bool useCam)\n\t{\n\t\tstatic const Mesh::Triangles tris = {\n\t\t\t{0,0,1},{1,1,2},{2,2,3},{3,3,0},\n\t\t\t{4,4,5},{5,5,6},{6,6,7},{7,7,4},\n\t\t\t{0,0,4},{1,1,5},{2,2,6},{3,3,7},\n\t\t};\n\n\t\tstd::vector<Vector3f> dirs;\n\t\tif (!useCam) {\n\t\t\tdirs.resize(4);\n\t\t\tdirs[0] = Vector3f(-1, -0.8, -1);\n\t\t\tdirs[1] = Vector3f(1, -0.8, -1);\n\t\t\tdirs[2] = Vector3f(1, 0.8, -1);\n\t\t\tdirs[3] = Vector3f(-1, 0.8, -1);\n\t\t} else {\n\t\t\tfor (const auto& c : cam.getImageCorners())\n\t\t\t\tdirs.push_back(CameraRaycaster::computeRayDir(cam, c.cast<float>() + 0.5f * Vector2f(1, 1)));\n\t\t\t\n\t\t}\n\n\t\tfloat znear = (near >= 0 ? near : cam.znear());\n\t\tfloat zfar = (far >= 0 ? far : cam.zfar());\n\t\tMesh::Vertices vertices;\n\t\tfor (int k = 0; k < 2; k++) {\n\t\t\tfloat dist = (k == 0 ? znear : zfar);\n\t\t\tfor (const auto & d : dirs) {\n\t\t\t\tif (useCam)\n\t\t\t\t\tvertices.push_back(cam.position() + dist * d);\n\t\t\t\telse \n\t\t\t\t\tvertices.push_back(dist * d);\n\t\t\t}\n\t\t}\n\n\t\tauto out = std::make_shared<Mesh>();\n\t\tout->vertices(vertices);\n\t\tout->triangles(tris);\n\t\treturn out;\n\t}\n\n\tMesh::Ptr generateCamFrustumColored(const InputCamera & cam, const Vector3f & col, float znear, float zfar)\n\t{\n\t\tauto out = generateCamFrustum(cam, znear, zfar);\n\t\tMesh::Colors cols(out->vertices().size(), col);\n\t\tout->colors(cols);\n\t\treturn out;\n\t}\n\n\tMesh::Ptr generateCamQuadWithUvs(const InputCamera & cam, float dist)\n\t{\n\t\tstatic const Mesh::Triangles quadTriangles = {\n\t\t\t{ 0,1,2 },{ 0,2,3 }\n\t\t};\n\t\tstatic const Mesh::UVs quadUVs = {\n\t\t\t{ 0,1 } ,{ 1,1 } ,{ 1,0 } ,{ 0,0 }\n\t\t};\n\n\t\tstd::vector<Vector3f> dirs;\n\t\tfor (const auto & c : cam.getImageCorners()) {\n\t\t\tdirs.push_back(CameraRaycaster::computeRayDir(cam, c.cast<float>() + 0.5f*Vector2f(1, 1)));\n\t\t}\n\t\tstd::vector<Vector3f> vertices;\n\t\tfor (const auto & d : dirs) {\n\t\t\tvertices.push_back(cam.position() + dist * d);\n\t\t}\n\n\t\tauto out = std::make_shared<Mesh>();\n\t\tout->vertices(vertices);\n\t\tout->triangles(quadTriangles);\n\t\tout->texCoords(quadUVs);\n\t\treturn out;\n\t}\n\n\n\tLabelsManager::CameraInfos::CameraInfos(const InputCamera& cam, uint id, bool highlight)\n\t\t: cam(cam), id(id), highlight(highlight) {\n\t}\n\n\tvoid LabelsManager::setupLabelsManagerShader()\n\t{\n\t\t_labelShader.init(\"text-imgui\",\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"text-imgui.vp\")),\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"text-imgui.fp\")));\n\t\t_labelShaderPosition.init(_labelShader, \"position\");\n\t\t_labelShaderScale.init(_labelShader, \"scale\");\n\t\t_labelShaderViewport.init(_labelShader, \"viewport\");\n\t}\n\n\tvoid LabelsManager::setupLabelsManagerMeshes(const std::vector<InputCamera::Ptr> & cams)\n\t{\n\t\t_labelMeshes.clear();\n\t\tfor (const auto & cam : cams) {\n\t\t\tunsigned int sepIndex = 0;\n\t\t\t_labelMeshes[cam->id()] = {};\n\t\t\t_labelMeshes[cam->id()].mesh = generateMeshForText(std::to_string(cam->id()), sepIndex);\n\t\t\t_labelMeshes[cam->id()].splitIndex = sepIndex;\n\t\t}\n\t}\n\n\tvoid LabelsManager::renderLabels(const Camera & eye, const Viewport & vp, const std::vector<CameraInfos>& cams_info)\n\t{\n\t\tglEnable(GL_BLEND);\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\t_labelShader.begin();\n\t\t// Bind the ImGui font texture.\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)(ImGui::GetFont()->ContainerAtlas->TexID));\n\t\t_labelShaderViewport.set(Vector2f(vp.finalWidth(), vp.finalHeight()));\n\n\t\tfor (const auto & camInfos : cams_info) {\n\t\t\tconst auto & inputCam = camInfos.cam;\n\t\t\tif (!inputCam.isActive()) { continue; }\n\t\t\tconst uint uid = camInfos.id;\n\t\t\tif (_labelMeshes.count(uid) == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Draw the label.\n\t\t\t// TODO: we could try to use depth testing to have the labels overlap properly.\n\t\t\t// As the label is put at the position of the camera, the label will intersect with the frustum mesh, causing artifacts.\n\t\t\t// One way of solving this would be to just shift the label away a bit and enable depth testing (+ GL_LEQUAl for the text).\n\t\t\tconst Vector3f camProjPos = eye.project(inputCam.position());\n\t\t\tif (!eye.frustumTest(inputCam.position(), camProjPos.xy())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t_labelShaderPosition.set(camProjPos);\n\t\t\tconst auto & label = _labelMeshes[uid];\n\t\t\t// Render the background label.\n\t\t\t_labelShaderScale.set(0.8f*_labelScale);\n\t\t\tlabel.mesh->renderSubMesh(0, label.splitIndex, false, false);\n\t\t\t// Render the text label.\n\t\t\t_labelShaderScale.set(1.0f*_labelScale);\n\t\t\tlabel.mesh->renderSubMesh(label.splitIndex, int(label.mesh->triangles().size()) * 3, false, false);\n\n\t\t}\n\t\t_labelShader.end();\n\t\tglDisable(GL_BLEND);\n\t}\n\n\tvoid ImageCamViewer::initImageCamShaders()\n\t{\n\t\tconst std::string vertex_str = loadFile(Resources::Instance()->getResourceFilePathName(\"uv_mesh.vert\"));\n\n\t\t_shader2D.init(\"cameraImageShader\", vertex_str, loadFile(Resources::Instance()->getResourceFilePathName(\"alpha_uv_tex.frag\")));\n\t\t_mvp2D.init(_shader2D, \"mvp\");\n\t\t_alpha2D.init(_shader2D, \"alpha\");\n\n\t\t_shaderArray.init(\"cameraImageShaderArray\", vertex_str, loadFile(Resources::Instance()->getResourceFilePathName(\"alpha_uv_tex_array.frag\")));\n\t\t_mvpArray.init(_shaderArray, \"mvp\");\n\t\t_alphaArray.init(_shaderArray, \"alpha\");\n\t\t_sliceArray.init(_shaderArray, \"slice\");\n\t}\n\n\tvoid ImageCamViewer::renderImage(const Camera & eye, const InputCamera & cam,\n\t\tconst std::vector<RenderTargetRGBA32F::Ptr> & rts, int cam_id)\n\t{\n\t\tconst auto quad = generateCamQuadWithUvs(cam, _pathScaling);\n\t\tif (cam_id < rts.size() && rts[cam_id]) {\n\t\t\t_shader2D.begin();\n\t\t\t_mvp2D.set(eye.viewproj());\n\t\t\t_alpha2D.set(_alphaImage);\n\t\t\tglActiveTexture(GL_TEXTURE0);\n\t\t\tglBindTexture(GL_TEXTURE_2D, rts[cam_id]->handle());\n\t\t\tquad->render(true, false, Mesh::FillRenderMode, false, false);\n\t\t\t_shader2D.end();\n\t\t}\n\t}\n\n\tvoid ImageCamViewer::renderImage(const Camera & eye, const InputCamera & cam, uint tex2Darray_handle, int cam_id)\n\t{\n\t\tconst auto quad = generateCamQuadWithUvs(cam, _pathScaling);\n\t\t_shaderArray.begin();\n\t\t_mvpArray.set(eye.viewproj());\n\t\t_alphaArray.set(_alphaImage);\n\t\t_sliceArray.set(cam_id);\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, tex2Darray_handle);\n\t\tquad->render(true, false, Mesh::FillRenderMode, false, false);\n\t\t_shaderArray.end();\n\t}\n\tvoid ImageCamViewer::renderImage(const Camera& eye, const InputCamera& cam, const RenderTargetRGBA32F::Ptr& rt)\n\t{\n\t\tconst auto quad = generateCamQuadWithUvs(cam, _pathScaling);\n\t\t_shader2D.begin();\n\t\t_mvp2D.set(eye.viewproj());\n\t\t_alpha2D.set(_alphaImage);\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D, rt->handle());\n\t\tquad->render(true, false, Mesh::FillRenderMode, false, false);\n\t\t_shader2D.end();\n\t}\n\n\tvoid ImageCamViewer::updateImgRt(const InputCamera& cam, const sibr::ImageRGBA& img)\n\t{\n\t\tuint w = cam.w();\n\t\tuint h = cam.h();\n\n\t\t//Force using image aspect ratio\n\t\tif (cam.w() >= cam.h()) h = cam.w(), w = cam.h();\n\t\telse w = cam.w(), h = cam.h();\n\n\t\tGLShader textureShader;\n\t\ttextureShader.init(\"Texture\",\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"texture.vp\")),\n\t\t\tloadFile(Resources::Instance()->getResourceFilePathName(\"texture.fp\")));\n\t\tuint interpFlag = (SIBR_SCENE_LINEAR_SAMPLING & SIBR_SCENE_LINEAR_SAMPLING) ? SIBR_GPU_LINEAR_SAMPLING : 0; // LINEAR_SAMPLING Set to default\n\n\t\tstd::cerr << \".\";\n\t\tImageRGBA cloned_img = std::move(img.clone());\n\t\tcloned_img.flipH();\n\n\t\tstd::shared_ptr<Texture2DRGBA> rawInputImage(new Texture2DRGBA(cloned_img, interpFlag));\n\n\t\tglViewport(0, 0, w, h);\n\t\t_imgRt.reset(new RenderTargetRGBA32F(w, h, interpFlag));\n\t\t_imgRt->clear();\n\t\t_imgRt->bind();\n\n\t\tglActiveTexture(GL_TEXTURE0);\n\t\tglBindTexture(GL_TEXTURE_2D, rawInputImage->handle());\n\n\t\t//Render texture (mapped to screenquad geometry) in the framebuffer (renderTarget)\n\t\t// so that it matches afterwards the same camera settings when applied as a texture\n\t\tglDisable(GL_DEPTH_TEST);\n\t\ttextureShader.begin();\n\t\tRenderUtility::renderScreenQuad();\n\t\ttextureShader.end();\n\t\t_imgRt->unbind();\n\t}\n\n\tSceneDebugView::SceneDebugView(const IIBRScene::Ptr & scene, \n\t\tconst InteractiveCameraHandler::Ptr & camHandler, const BasicDatasetArgs & myArgs, const std::string& imagesPath)\n\t{\n\n\t\tinitImageCamShaders();\n\t\tsetupLabelsManagerShader();\n\t\t\n\t\tconst std::string chunksFile = myArgs.dataset_path.get() + \"/chunks.txt\";\n\t\n\t\tif (fileExists(chunksFile)) {\n\t\t\tloadChunksData(chunksFile.c_str());\n\t\t}\n\n\t\t_images_path = imagesPath;\n\t\tstd::cout << \"images_path: \" << _images_path << std::endl;\n\t\t_scene = scene;\n\t\t_userCurrentCam = camHandler;\n\n\t\tif (!_scene->cameras()->inputCameras().empty()) {\n\t\t\tcamera_handler.fromTransform(_scene->cameras()->inputCameras()[0]->transform(), true, false);\n\t\t\tcamera_handler.setupInterpolationPath(_scene->cameras()->inputCameras());\n\t\t}\n\n\t\t_showImages = false;\n\t\tif (directoryExists(imagesPath)) {\n\t\t\t_images_path = imagesPath;\n\t\t}\n\t\telse {\n\t\t\t_images_path = \"\";\n\t\t}\n\n\t\tconst std::string camerasDir = myArgs.dataset_path.get() + \"/cameras\";\n\t\tif (directoryExists(camerasDir)) {\n\t\t\t_topViewPath = camerasDir + \"/topview.txt\";\n\t\t\t\tif (!directoryExists(camerasDir)) {\n\t\t\t\t\tmakeDirectory(camerasDir);\n\t\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t_topViewPath = parentDirectory(myArgs.dataset_path) + \"/topview.txt\";\n\t\t}\n\n\t\tsetup();\n\t}\n\n\tSceneDebugView::SceneDebugView(const IIBRScene::Ptr & scene, const Viewport & viewport,\n\t\tconst InteractiveCameraHandler::Ptr & camHandler, const BasicDatasetArgs & myArgs) : SceneDebugView(scene, camHandler, myArgs) {\n\t\tSIBR_WRG << \"Deprecated SceneDebugView constructor, use the version without viewport passed as argument.\" << std::endl;\n\t}\n\n\tvoid SceneDebugView::onUpdate(Input & input, const float deltaTime, const Viewport & viewport)\n\t{\n\t\tMultiMeshManager::onUpdate(input, viewport);\n\n\t\t//Camera stub size\n\t\tif (input.key().isActivated(Key::LeftControl) && input.mouseScroll() != 0.0) {\n\t\t\t_userCameraScaling = std::max(0.001f, _userCameraScaling + (float)input.mouseScroll() * 0.1f);\n\t\t}\n\t\tif (input.key().isActivated(Key::LeftControl) && input.key().isReleased(Key::P)) {\n\t\t\tMeshData & guizmo = getMeshData(\"guizmo\");\n\t\t\tguizmo.active = !guizmo.active;\n\t\t}\n\n\t\tMeshData & proxy = getMeshData(\"proxy\");\n\t\tif( proxy.meshPtr->triangles().size() == 0 )\n\t\t\t// SfM Points only\n\t\t\tproxy.renderMode = Mesh::RenderMode::PointRenderMode;\n\n\t\tif (input.key().isActivated(Key::LeftControl) && input.key().isReleased(Key::Z)) {\n\t\t\t//MeshData & proxy = getMeshData(\"proxy\");\n\t\t\tif (proxy.renderMode == Mesh::RenderMode::FillRenderMode) {\n\t\t\t\tproxy.renderMode = Mesh::RenderMode::LineRenderMode;\n\t\t\t} else {\n\t\t\t\tproxy.renderMode = Mesh::RenderMode::FillRenderMode;\n\t\t\t}\n\t\t}\n\n\t\tif (input.key().isReleased(Key::T)) {\n\t\t\tsave();\n\t\t}\n\n\t\t//user camera transform update\n\t\tsibr::Transform3f scaled = _userCurrentCam->getCamera().transform();\n\t\tscaled.scale(_userCameraScaling);\n\t\tgetMeshData(\"scene cam\").setTransformation(scaled.matrix());\n\n\t\t//update current chunk (in which user camera is)\n\t\t_currentChunk = \"none\";\n\t\tremoveMesh(\"current chunk\");\n\t\tfor (Chunk chunk : chunks)\n\t\t{\n\t\t\tif (chunk.contains(_userCurrentCam->getCamera().position()))\n\t\t\t{\n\t\t\t\t_currentChunk = chunk.name;\n\t\t\t\tif (_highlight_current_chunk)\n\t\t\t\t{\n\t\t\t\t\tcurrent_chunk_mesh.reset();\n\t\t\t\t\tcurrent_chunk_mesh = chunk.generateMesh();\n\t\t\t\t\t\n\n\t\t\t\t\t//current_chunk_mesh = std::make_shared<Mesh>();\n\t\t\t\t\taddMeshAsLines(\"current chunk\", current_chunk_mesh).setColor({ 1,0,0 });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// update input camera (path) scales\n\t\tif (_pathScaling != _lastPathScaling) {\n\t\t\tremoveMesh(\"used cams\");\n\t\t\t_used_cams.reset();\n\t\t\t_used_cams = std::make_shared<Mesh>();\n\t\t\tfor (const auto& camInfos : _cameras) {\n\t\t\t\tif (!camInfos.cam.isActive()) { continue; }\n\t\t\t\t(camInfos.highlight ? _used_cams : _non_used_cams)->merge(*generateCamFrustum(camInfos.cam, 0.0f, _pathScaling));\n\t\t\t}\n\t\t\t_lastPathScaling = _pathScaling;\n\t\t\taddMeshAsLines(\"used cams\", _used_cams).setColor({ 0,1,0 }).setDepthTest(true);\n\n\t\t}\n\t}\n\n\tvoid SceneDebugView::onUpdate(Input & input, const Viewport & viewport)\n\t{\n\t\tonUpdate(input, 1.0f / 60.0f, viewport);\n\t}\n\n\tvoid SceneDebugView::onUpdate(Input & input)\n\t{\n\t\t// Update camera with a fixed timestep.\n\t\tonUpdate(input, 1.0f / 60.0f);\n\t}\n\n\tvoid SceneDebugView::onRender(Window & win)\n\t{\n\t\t// We need no information about the window, we render wherever we are.\n\t\tonRender(win.viewport());\n\t}\n\n\tvoid SceneDebugView::onRender(const Viewport & viewport)\n\t{\n\t\tglPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, \"Scene debug view\");\n\n\t\tviewport.clear(backgroundColor);\n\t\tviewport.bind();\n\n\t\trenderMeshes();\n\t\t\n\t\tif (_displayImg) {\n\t\t\tglEnable(GL_BLEND);\n\t\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\t\t//renderFromTex(camera_handler.getCamera(), _cameras[_cameraIdInfoGUI].cam, _imgTex->handle());\n\t\t\trenderImage(camera_handler.getCamera(), _cameras[_cameraIdInfoGUI].cam, _imgRt);\n\t\t\tglDisable(GL_BLEND);\n\t\t}\n\n\n\t\tif (_showLabels) {\n\t\t\trenderLabels(camera_handler.getCamera(), viewport, _cameras);\n\t\t}\n\n\t\tcamera_handler.onRender(viewport);\n\t\tglPopDebugGroup();\n\t}\n\n\tvoid SceneDebugView::onGUI()\n\t{\n\t\tif (ImGui::Begin(\"Top view settings\")) {\n\t\t\tgui_options();\n\t\t\tlist_mesh_onGUI();\n\t\t\tgui_cameras();\n\t\t\tif(chunks.size() != 0)\n\t\t\t\tgui_chunks();\n\t\t}\n\t\tImGui::End();\n\t\t\n\t}\n\n\tvoid SceneDebugView::save()\n\t{\n\t\t\n\t\tstd::ofstream outfile(_topViewPath, std::ios::out | std::ios::trunc);\n\t\tstd::cerr << \"Saving topview camera to \" << _topViewPath << std::endl;\n\t\t// save camera view proj matrix\n\t\tcamera_handler.getCamera().writeToFile(outfile);\n\t}\n\n\tvoid SceneDebugView::setScene(const IIBRScene::Ptr & scene, bool preserveCamera)\n\t{\n\t\t_scene = scene;\n\t\tconst InputCamera cameraBack = camera_handler.getCamera();\n\t\tsetup();\n\t\tcamera_handler.setup(_scene->cameras()->inputCameras(), camera_handler.getViewport(), camera_handler.getRaycaster());\n\t\tcamera_handler.setupInterpolationPath(_scene->cameras()->inputCameras());\n\t\t// Optionally restore the camera pose.\n\t\tif (preserveCamera) {\n\t\t\tcamera_handler.fromCamera(cameraBack, false);\n\t\t}\n\t}\n\n\tvoid SceneDebugView::updateActiveCams(const std::vector<uint>& cams_id)\n\t{\n\t\tfor (auto & cam : _cameras) {\n\t\t\tcam.highlight = false;\n\t\t}\n\t\tfor (const uint id : cams_id) {\n\t\t\tif (id < _cameras.size()) {\n\t\t\t\t_cameras[id].highlight = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid SceneDebugView::loadChunksData(const char* file)\n\t{\n\t\tchunks.clear();\n\t\tstd::ifstream infile(file);\n\n\t\tif (!infile.is_open())\n\t\t\tthrow std::runtime_error(\"File not found!\");\n\n\t\tstd::string line;\n\n\t\twhile (std::getline(infile, line)) {\n\t\t\tstd::istringstream iss(line);\n\t\t\tstd::string value;\n\t\t\tChunk chunk;\n\n\t\t\tiss >> chunk.name;\n\t\t\tiss >> chunk.center.x() >> chunk.center.y() >> chunk.center.z();\n\t\t\tiss >> chunk.extent.x() >> chunk.extent.y() >> chunk.extent.z();\n\n\t\t\tchunks.push_back(chunk);\n\t\t}\n\t}\n\n\tvoid SceneDebugView::createChunksMesh(bool use_z)\n\t{\n\t\tremoveMesh(\"chunks\");\n\t\tif (chunks.size() != 0) {\n\t\t\tfor (Chunk chunk : chunks) {\n\t\t\t\tchunks_mesh->merge(*chunk.generateMesh(use_z));\n\t\t\t}\n\n\t\t\taddMeshAsLines(\"chunks\", chunks_mesh).setColor({ 0, 0.5f, 0.3f });\n\t\t}\n\t}\n\n\tvoid SceneDebugView::recomputeSelectedChunksMesh()\n\t{\n\t\tselected_chunks_mesh.reset();\n\t\tselected_chunks_mesh = std::make_shared<Mesh>();\n\t\tfor (Chunk chunk : chunks) {\n\t\t\tif(chunk.selected)\n\t\t\t\tselected_chunks_mesh->merge(*chunk.generateMesh());\n\t\t}\n\n\t\taddMeshAsLines(\"selected_chunks\", selected_chunks_mesh).setColor({ 0.9f, 0.9f, 0.f });\n\t}\n\n\tvoid SceneDebugView::gui_options()\n\t{\n\n\t\tif (ImGui::CollapsingHeader(\"OptionsSceneDebugView##\")) {\n\t\t\tif (ImGui::Button(\"Save topview\")) {\n\t\t\t\tsave();\n\t\t\t}\n\n\t\t\tImGui::PushScaledItemWidth(120);\n\t\t\tImGui::InputFloat(\"Input cameras scale\", &_pathScaling, 0.1f, 10.0f);\n\t\t\tImGui::InputFloat(\"User camera scale\", &_userCameraScaling, 0.1f, 10.0f);\n\t\t\t_pathScaling = std::max(0.001f, _pathScaling);\n\t\t\t_userCameraScaling = std::max(0.001f, _userCameraScaling);\n\n\t\t\tImGui::Checkbox(\"Draw labels \", &_showLabels);\n\t\t\tif (_showLabels) {\n\t\t\t\tImGui::SameLine();\n\t\t\t\tImGui::InputFloat(\"Label scale\", &_labelScale, 0.2f, 10.0f);\n\t\t\t}\n\n\t\t\tImGui::Separator();\n\t\t\tImGui::Checkbox(\"Draw Input Images \", &_showImages);\n\t\t\tif (_showImages) {\n\t\t\t\tImGui::SameLine();\n\t\t\t\tImGui::SliderFloat(\"Alpha\", &_alphaImage, 0, 1.0);\n\t\t\t}\n\t\t\t\n\t\t\tcamera_handler.onGUI(\"Top view settings\");\n\t\t\tImGui::PopItemWidth();\n\t\t\tImGui::Separator();\n\t\t}\n\t}\n\n\tvoid SceneDebugView::gui_cameras()\n\t{\n\t\tif (ImGui::CollapsingHeader(\"Cameras##SceneDebugView\")) {\n\t\t\t\n\t\t\tImGui::SliderInt(\"Camera ID info\", &_cameraIdInfoGUI, 0, static_cast<int>(_cameras.size()) - 1);\n\n\t\t\t//Snap topView cam to closest input camera in mainView\n\t\t\tif (ImGui::Button(std::string(\"Snap to closest\").c_str())) {\n\t\t\t\t_cameraIdInfoGUI = _userCurrentCam->findNearestCamera(_scene->cameras()->inputCameras());\n\t\t\t\tconst auto& input_cam = _scene->cameras()->inputCameras()[0];\n\n\t\t\t\tauto size = camera_handler.getViewport().finalSize();\n\t\t\t\tfloat ratio_dst = size[0] / size[1];\n\t\t\t\tfloat ratio_src = input_cam->w() / (float)input_cam->h();\n\t\t\t\tInputCamera cam = InputCamera(_cameras[_cameraIdInfoGUI].cam, (int)size[0], (int)size[1]);\n\n\t\t\t\tif (_displayImg)\n\t\t\t\t\t_displayImg = false;\n\n\t\t\t\tif (ratio_src < ratio_dst) {\n\t\t\t\t\tfloat fov_h = 2 * atan(tan(input_cam->fovy() / 2) * ratio_src / ratio_dst);\n\t\t\t\t\tcam.fovy(fov_h);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcam.fovy(input_cam->fovy());\n\t\t\t\t}\n\n\t\t\t\t//cam.znear(0.0001f);\n\t\t\t\tcamera_handler.fromCamera(cam, true, true);\n\t\t\t}\n\n\t\t\tImGui::Columns(4); // 0 name | snapto | active| size \n\n\t\t\tImGui::Separator();\n\t\t\tImGui::Text(\"Camera\"); ImGui::NextColumn();\n\t\t\tImGui::Text(\"SnapTo\"); ImGui::NextColumn();\n\t\t\tImGui::Text(\"alpha\"); ImGui::NextColumn();\n\n\t\t\tstatic std::vector<std::string> cam_info_option_str = { \"size\", \"focal\", \"fov_y\",\"aspect\" };\n\t\t\tif (ImGui::BeginCombo(\"Info\", cam_info_option_str[_camInfoOption].c_str())) {\n\t\t\t\tfor (int i = 0; i < (int)cam_info_option_str.size(); ++i) {\n\t\t\t\t\tif (ImGui::Selectable(cam_info_option_str[i].c_str(), _camInfoOption == i)) {\n\t\t\t\t\t\t_camInfoOption = (CameraInfoDisplay)i;\n\t\t\t\t\t}\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\tImGui::EndCombo();\n\t\t\t}\n\t\t\tImGui::NextColumn();\n\t\t\tImGui::Separator();\n\t\n\t\t\t//for (uint i = 0; i < _cameras.size(); ++i) \n\t\t\t{\n\t\t\t\tstd::string name = \"cam_\" + intToString<4>(_cameraIdInfoGUI);\n\t\t\t\tImGui::Text(name.c_str());\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tif (ImGui::Button((\"SnapTo##\" + name).c_str())) {\n\t\t\t\t\tconst auto & input_cam = _scene->cameras()->inputCameras()[0];\n\n\t\t\t\t\tauto size = camera_handler.getViewport().finalSize();\n\t\t\t\t\tfloat ratio_dst = size[0] / size[1];\n\t\t\t\t\tfloat ratio_src = input_cam->w() / (float)input_cam->h();\n\t\t\t\t\tInputCamera cam = InputCamera(_cameras[_cameraIdInfoGUI].cam, (int)size[0], (int)size[1]);\n\n\t\t\t\t\tif (ratio_src < ratio_dst) {\n\t\t\t\t\t\tfloat fov_h = 2 * atan(tan(input_cam->fovy() / 2) * ratio_src / ratio_dst);\n\t\t\t\t\t\tcam.fovy(fov_h);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcam.fovy(input_cam->fovy());\n\t\t\t\t\t}\n\n\t\t\t\t\tif (_displayImg)\n\t\t\t\t\t\t_displayImg = false;\n\n\t\t\t\t\t//cam.znear(0.0001f);\n\t\t\t\t\tcamera_handler.fromCamera(cam, true, true);\n\t\t\t\t}\n\n\t\t\t\tif (_images_path != \"\") {\n\n\t\t\t\t\tImGui::SameLine();\n\t\t\t\t\tif (ImGui::Button(\"DisplayImg##\")) {\n\n\t\t\t\t\t\tif (_imgToFetch != _cameras[_cameraIdInfoGUI].cam.name()) {\n\t\t\t\t\t\t\t_imgToFetch = _cameras[_cameraIdInfoGUI].cam.name();\n\n\t\t\t\t\t\t\tstd::string fullPath = _images_path + \"/\" + _imgToFetch;\n\t\t\t\t\t\t\tsibr::ImageRGBA img;\n\n\t\t\t\t\t\t\tif (!fileExists(fullPath)) {\n\t\t\t\t\t\t\t\tfullPath = fullPath.substr(0, fullPath.length() - 3) + \"JPG\";\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (img.load(fullPath)) {\n\t\t\t\t\t\t\t\tupdateImgRt(_cameras[_cameraIdInfoGUI].cam, img);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_displayImg = !_displayImg;\n\t\t\t\t\t\t//(sibr::Image)img.load(fullPath);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tImGui::SliderFloat(\"Alpha##\", &_alphaImage, 0, 1.0);\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tconst InputCamera & cam = _cameras[_cameraIdInfoGUI].cam;\n\t\t\t\tstd::stringstream tmp;\n\t\t\t\tswitch (_camInfoOption)\n\t\t\t\t{\n\t\t\t\t\tcase SIZE: tmp << cam.w() << \" x \" << cam.h(); break;\n\t\t\t\t\tcase FOCAL: tmp << cam.focal(); break;\n\t\t\t\t\tcase FOV_Y: tmp << cam.fovy(); break;\n\t\t\t\t\tcase ASPECT: tmp << cam.aspect(); break;\n\t\t\t\t\tdefault: break;\n\t\t\t\t}\n\t\t\t\tImGui::Text(tmp.str().c_str());\n\t\t\t\tImGui::NextColumn();\n\t\t\t\tImGui::Columns(1);\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\n\tvoid SceneDebugView::gui_chunks()\n\t{\n\t\tIterator swap_it_src, swap_it_dst;\n\t\tbool do_swap = false;\n\t\tstatic int num_swap = 1;\n\n\t\tif (ImGui::CollapsingHeader(\"Chunks##SceneDebugView\")) {\n\t\t\tImGui::Text(\"User camera in chunk %s\", _currentChunk);\n\t\t\tImGui::Checkbox(\"Highlight current chunk \", &_highlight_current_chunk);\n\n\t\t\t// 0 name | 1 center | 2 extents\n\t\t\tImGui::Columns(3, \"chunk info\");\n\n\t\t\t//ImGui::SetColumnWidth(4, 50);\n\t\t\tif (ImGui::Button(\"Chunks\")) {\n\t\t\t\tfor (Chunk& c : chunks)\n\t\t\t\t\tc.selected = !c.selected;\n\n\t\t\t\trecomputeSelectedChunksMesh();\n\t\t\t}\n\n\t\t\tImGui::SameLine();\n\n\t\t\tif (ImGui::Button(\"All\")) {\n\t\t\t\tfor (Chunk& c : chunks)\n\t\t\t\t\tc.selected = true;\n\n\t\t\t\trecomputeSelectedChunksMesh();\n\t\t\t}\n\n\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"Center\");\n\n\t\t\t//ImGui::Separator();\n\t\t\tImGui::NextColumn();\n\n\t\t\tImGui::Text(\"Extents\");\n\t\t\tImGui::Separator();\n\n\t\t\tImGui::NextColumn();\n\n\n\t\t\tfor (Chunk& chunk : chunks) {\n\t\t\t\t\n\t\t\t\tif (ImGui::Checkbox((\"##\" + chunk.name).c_str(), &chunk.selected)) {\n\t\t\t\t\trecomputeSelectedChunksMesh();\n\t\t\t\t}\n\t\t\t\tImGui::SameLine();\n\t\t\t\tImGui::Selectable(chunk.name.c_str());\n\t\t\t\t\n\n\t\t\t\tImGui::NextColumn();\n\n\t\t\t\tstd::string center_str = std::to_string(chunk.center.x()) + \" \" + std::to_string(chunk.center.y()) + \" \" + std::to_string(chunk.center.z());\n\t\t\t\tImGui::Text(center_str.c_str());\n\n\t\t\t\tImGui::NextColumn();\n\t\t\t\tstd::string extent_str = std::to_string(chunk.extent.x()) + \" \" + std::to_string(chunk.extent.y()) + \" \" + std::to_string(chunk.extent.z());\n\t\t\t\tImGui::Text(extent_str.c_str());\n\n\t\t\t\tImGui::Separator();\n\t\t\t\tImGui::NextColumn();\n\t\t\t\t//ImGui::Separator();\n\t\t\t}\n\n\t\t\tImGui::Columns(1);\n\t\t}\n\t\tif (do_swap) {\n\t\t\tstd::swap(*swap_it_src, *swap_it_dst);\n\t\t\t++num_swap;\n\t\t}\n\t\tif (ImGui::IsMouseReleased(0)) {\n\t\t\tnum_swap = 1;\n\t\t}\n\t\t\n\t}\n\n\tvoid SceneDebugView::setup()\n\t{\n\t\tif (_scene) {\n\t\t\tsetupLabelsManagerMeshes(_scene->cameras()->inputCameras());\n\t\t\tsetupMeshes();\n\t\t\t_user_cam = generateCamFrustum(_userCurrentCam->getCamera(), 0.0f, _userCameraScaling, false);\n\t\t\t_cameras.clear();\n\n\t\t\tint id = 0;\n\t\t\tfor (const auto& inputCam : _scene->cameras()->inputCameras()) {\n\t\t\t\tconst bool isUsed = _scene->cameras()->isCameraUsedForRendering(inputCam->id());\n\t\t\t\t_cameras.push_back(CameraInfos(*inputCam, inputCam->id(), isUsed));\n\n\t\t\t\tif (inputCam->isActive())\n\t\t\t\t\t(isUsed ? _used_cams : _non_used_cams)->merge(*generateCamFrustum(*inputCam, 0.0f, _pathScaling));\n\t\t\t}\n\t\t\taddMeshAsLines(\"scene cam\", _user_cam).setColor({ 1,0,0 }).setDepthTest(true);\n\t\t\taddMeshAsLines(\"used cams\", _used_cams).setColor({ 0,1,0 }).setDepthTest(true);\n\t\t\taddMeshAsLines(\"non used cams\", _non_used_cams).setColor({ 0,0,1 }).setDepthTest(true);\n\t\t}\n\n\t\t_snapToImage = 0;\n\t\t_showLabels = false;\n\n\t\t// check if topview.txt exists\n\t\tstd::ifstream topViewFile(_topViewPath);\n\t\tif (topViewFile.good())\n\t\t{\n\t\t\tSIBR_LOG << \"Loaded saved topview (\" << _topViewPath << \").\" << std::endl;\n\t\t\t// Intialize a temp camera (used to load the saved top view pose) with\n\t\t\t// the current top view camera to get the resolution/fov right.\n\t\t\tInputCamera cam(camera_handler.getCamera());\n\t\t\tcam.readFromFile(topViewFile);\n\t\t\t// Apply it to the top view FPS camera.\n\t\t\t//camera_handler.fromCamera(cam, false);\n\t\t\tcamera_handler.fromTransform(cam.transform(), false, true);\n\t\t}\n\n\t}\n\n\tvoid SceneDebugView::setupMeshes()\n\t{\n\t\t// no colors and no texture ? try to find capreal\n\t\tbool success = false;\n\t\tMesh sdv_mesh;\n\t\tMesh::Ptr mp;\n\t\tif (!_scene->proxies()->proxyPtr()->hasColors() && !_scene->proxies()->proxyPtr()->hasTexCoords()) {\n\t\t\tstd::string fn;\n\t\t\tif (fileExists(fn = _scene->data()->basePathName() + \"/capreal/mesh.ply\")) {\n\t\t\t\tif (sdv_mesh.load(fn, _scene->data()->basePathName()))\n\t\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t\t// in sibr subdir\n\t\t\telse if (fileExists(fn = _scene->data()->basePathName() + \"/../capreal/mesh.ply\")) {\n\t\t\t\tif (sdv_mesh.load(fn, _scene->data()->basePathName()))\n\t\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t\tif (success) {\n\t\t\t\tMesh::Ptr mp;\n\t\t\t\tmp.reset(new Mesh);\n\t\t\t\tmp->merge(sdv_mesh);\n\t\t\t\taddMesh(\"proxy\", mp);\n\t\t\t}\n\t\t\telse\n\t\t\t\taddMesh(\"proxy\", _scene->proxies()->proxyPtr()).setRadiusPoint(2).setDepthTest(false);\n\t\t}\n\t\telse\n\t\t\taddMesh(\"proxy\", _scene->proxies()->proxyPtr()).setRadiusPoint(2).setDepthTest(false);\n\n\t\t// Add a gizmo.\n\t\taddMeshAsLines(\"guizmo\", RenderUtility::createAxisGizmo())\n\t\t\t.setDepthTest(false).setColorMode(MeshData::ColorMode::VERTEX);\n\n\t\t// generate and add chunks meshes (wireframes)\n\t\tif (chunks.size() != 0) {\n\t\t\tfor (Chunk chunk : chunks) {\n\t\t\t\tchunks_mesh->merge(*chunk.generateMesh());\n\t\t\t}\n\n\t\t\taddMeshAsLines(\"chunks\", chunks_mesh).setColor({ 0, 0.5f, 0.3f });\n\t\t}\n\t}\n\n} // namespace\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/SceneDebugView.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#pragma once\n\n# include \"core/assets/InputCamera.hpp\"\n# include \"core/assets/CameraRecorder.hpp\"\n# include \"core/graphics/Texture.hpp\"\n# include \"core/graphics/Camera.hpp\"\n# include \"core/graphics/Window.hpp\"\n# include \"core/graphics/Shader.hpp\"\n# include \"core/graphics/Mesh.hpp\"\n# include \"core/view/InteractiveCameraHandler.hpp\"\n# include \"core/view/ViewBase.hpp\"\n# include \"core/scene/BasicIBRScene.hpp\"\n# include \"core/system/CommandLineArgs.hpp\"\n\n#include <core/view/MultiMeshManager.hpp>\n\n\nstruct Chunk\n{\n\tstd::string name;\n\tsibr::Vector3f center;\n\tsibr::Vector3f extent;\n\tbool selected = false;\n\tbool display = true;\n\n\tbool contains(const sibr::Vector3f& pos)\n\t{\n\t\tsibr::Vector3f minn = center - (0.5 * extent);\n\t\tsibr::Vector3f maxx = center + (0.5 * extent);\n\n\t\tfor (int i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (pos[i] > maxx[i] || pos[i] < minn[i])\n\t\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tsibr::Mesh::Ptr generateMesh(bool use_z = false)\n\t{\n\t\tconst sibr::Vector3f d_extent = 0.5f * extent;\n\t\tsibr::Vector3f origin = center - d_extent;\n\t\t\n\t\tsibr::Mesh::Vertices vertices;\n\t\tvertices.resize(8);\n\n\t\tfor (int i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (int j = 0; j < 2; j++)\n\t\t\t{\n\t\t\t\tsibr::Vector3f p1(\n\t\t\t\t\torigin.x() + (j * extent.x()),\n\t\t\t\t\torigin.y() + (i * extent.y()),\n\t\t\t\t\tuse_z ? origin.z() : -50.f\n\t\t\t\t);\n\n\t\t\t\tsibr::Vector3f p2(\n\t\t\t\t\torigin.x() + (j * extent.x()),\n\t\t\t\t\torigin.y() + (i * extent.y()),\n\t\t\t\t\tuse_z ? origin.z() + extent.z() : 50.f\n\t\t\t\t);\n\n\t\t\t\tvertices[i * 2 + j] = p1;\n\t\t\t\tvertices[4 + (i * 2 + j)] = p2;\n\t\t\t}\n\t\t}\n\n\t\tconst sibr::Mesh::Triangles tris = {\n\t\t\t{0,0,1},{1,1,3},{3,3,2},{2,2,0},\n\t\t\t{4,4,5},{5,5,7},{7,7,6},{6,6,4},\n\t\t\t{0,0,4},{1,1,5},{2,2,6},{3,3,7},\n\t\t};\n\n\n\t\t//const sibr::Mesh::Triangles tris = {\n\t\t//{0, 0, 1}, { 1,1,2 }, { 2,2,3 }, { 3,3,0 },\n\t\t//{ 4,4,5 }, { 5,5,6 }, { 6,6,7 }, { 7,7,4 },\n\t\t//{ 0,0,4 }, { 1,1,5 }, { 2,2,6 }, { 3,3,7 },\n\t\t//};\n\n\t\tauto out = std::make_shared<sibr::Mesh>();\n\t\tout->vertices(vertices);\n\t\tout->triangles(tris);\n\n\t\treturn out;\n\t}\n};\n\nnamespace sibr\n{\n\n\t/** Generate an accurate camera frustum\n\t\t\\param cam camera to visualize as a stub\n\t\t\\param znear near value to use for the frustum (if < 0, cam.near() will be used)\n\t\t\\param zfar far value to use for the frustum (if < 0, cam.far() will be used)\n\t\t \\ingroup sibr_view\n\t\t*/\n\tMesh::Ptr SIBR_VIEW_EXPORT generateCamFrustum(const InputCamera & cam, float znear = -1, float zfar = -1, bool useCam = true);\n\n\t/** Generate an accurate camera frustum with a custom color.\n\t\t\\param cam camera to visualize as a stub\n\t\t\\param col the mesh line color\n\t\t\\param znear near value to use for the frustum (if < 0, cam.near() will be used)\n\t\t\\param zfar far value to use for the frustum (if < 0, cam.far() will be used)\n\t\t \\ingroup sibr_view\n\t\t*/\n\tMesh::Ptr SIBR_VIEW_EXPORT generateCamFrustumColored(const InputCamera & cam, const Vector3f & col, float znear = -1, float zfar = -1);\n\n\t/** Generate a quad representing a camera image plane.\n\t *\\param cam the camera\n\t *\\param dist the distance in world space from the camera position to the image plane\n\t  \\ingroup sibr_view\n\t **/\n\tMesh::Ptr SIBR_VIEW_EXPORT generateCamQuadWithUvs(const InputCamera & cam, float dist);\n\n\t/** Helper used to display camera labels on screen.\n\t * Internally use ImGui to generate labels data.\n\t  \\ingroup sibr_view\n\t * */\n\tstruct SIBR_VIEW_EXPORT LabelsManager {\n\n\tprotected:\n\n\t\t/** Displayed cameras info. */\n\t\tstruct CameraInfos {\n\t\t\t/** Constructor.\n\t\t\t *\\param cam the camera\n\t\t\t *\\param id the corresponding vector ID\n\t\t\t *\\param highlight should the camera be highlighted. \n\t\t\t */\n\t\t\tCameraInfos(const InputCamera& cam, uint id, bool highlight);\n\n\t\t\tconst InputCamera & cam; ///< Camera.\n\t\t\tuint id = 0; ///< Array ID.\n\t\t\tbool highlight = false; ///< Highlight status.\n\t\t};\n\n\t\t/** Initialize the shaders. */\n\t\tvoid setupLabelsManagerShader();\n\n\t\t/** Generate labels data based on input camera informations.\n\t\t *\\param cams the cameras \n\t\t */\n\t\tvoid setupLabelsManagerMeshes(const std::vector<InputCamera::Ptr> & cams);\n\n\t\t/** Render the camera labels.\n\t\t *\\param eye the current viewpoint\n\t\t *\\param vp the view viewport\n\t\t *\\param cams_info the current state of the cameras. \n\t\t * \\todo Get rid of the viewport if possible.\n\t\t **/\n\t\tvoid renderLabels(const Camera & eye, const Viewport & vp, const std::vector<CameraInfos> & cams_info);\n\t\n\n\t\t/** Label geometry info. The mesh is split in two parts, \n\t\t * one containing the background label shape, \n\t\t * and one containing the quads that support the text. */\n\t\tstruct LabelMesh {\n\t\t\tMesh::Ptr mesh; ///< The generated mesh.\n\t\t\tunsigned int splitIndex = 0; ///< The boundary between foreground and background mesh.\n\t\t};\n\n\t\tstd::map<unsigned int, LabelMesh> \t_labelMeshes; ///< Generated geometry for each label.\n\t\tGLShader\t\t\t\t\t\t\t_labelShader; ///< Shader.\n\t\tGLuniform<Vector3f>\t\t\t\t\t_labelShaderPosition; ///< Uniform for the label position.\n\t\tGLuniform<float>\t\t\t\t\t_labelShaderScale = 1.0f; ///< Uniform for the label scale (used twice per label, with different values derived from _labelScale).\n\t\tGLuniform<Vector2f>\t\t\t\t\t_labelShaderViewport; ///< The viewport of the view, for ratio adjustment.\n\t\tfloat\t\t\t\t\t\t\t\t_labelScale = 1.0f; ///< The label scale ons creen.\n\n\t};\n\n\t/** Helper used to render image planes in front of the camera, \n\t * for both scenes storing 2D separate images or a texture array.\n\t  \\ingroup sibr_view\n\t */\n\tstruct SIBR_VIEW_EXPORT ImageCamViewer {\n\n\tprotected:\n\n\t\t/** Initialize the shaders. */\n\t\tvoid initImageCamShaders();\n\n\t\t/** Render one specific input image on a camera image plane.\n\t\t *\\param eye the current viewpoint\n\t\t *\\param cam the camera to show the image plane of\n\t\t *\\param rts input 2D textures list\n\t\t *\\param cam_id the list index associated to the camera\n\t\t */\n\t\tvoid renderImage(const Camera & eye, const InputCamera & cam, const std::vector<RenderTargetRGBA32F::Ptr> & rts, int cam_id);\n\n\t\t/** Render one specific input image on a camera image plane.\n\t\t *\\param eye the current viewpoint\n\t\t *\\param cam the camera to show the image plane of\n\t\t *\\param tex2Darray_handle input images texture array\n\t\t *\\param cam_id the array slice associated to the camera\n\t\t */\n\t\tvoid renderImage(const Camera & eye, const InputCamera & cam, uint tex2Darray_handle, int cam_id);\n\n\t\tvoid renderImage(const Camera& eye, const InputCamera& cam, const RenderTargetRGBA32F::Ptr& rt);\n\n\t\tvoid updateImgRt(const InputCamera& cam, const sibr::ImageRGBA& img);\n\n\t\tGLShader _shader2D;\t\t///< Shader for the 2D separate case.\n\t\tGLShader _shaderArray;  ///< Shader for the texture array case.\n\t\tGLuniform<sibr::Matrix4f>\t_mvp2D, _mvpArray; ///< MVP matrix.\n\t\tGLuniform<float>\t\t\t_alpha2D = 1.0f; ///< Opacity.\n\t\tGLuniform<float>\t\t\t_alphaArray = 1.0f; ///< Opacity.\n\t\tGLuniform<int>\t\t\t\t_sliceArray = 1; ///< Slice location (for the texture array case).\n\t\tfloat\t\t\t\t\t\t_alphaImage = 0.5f; ///< Opacity shared value.\n\n\t\tRenderTargetRGBA32F::Ptr\t\t\t_imgRt;\n\t\tfloat\t\t\t\t\t\t\t\t_userCameraScaling = 3.f; ///< User camera scaling.\n\t\tfloat\t\t\t\t\t\t\t\t_pathScaling = 0.3f; ///< Input cameras scaling.\n\t\tfloat\t\t\t\t\t\t\t\t_lastPathScaling = 0.2f;\n\t\tstd::string\t\t\t\t\t\t\t_imgToFetch = \"\";\n\t\tuint\t\t\t\t\t\t\t\t_imgTexHandle;\n\t\tbool\t\t\t\t\t\t\t\t_displayImg = false;\n\t\tsibr::Texture2D<unsigned char, 4>* _imgTex = nullptr;\n\t};\n\n\t/** Scene viewer for IBR scenes with a proxy, cameras and input images. \n\t * It adds camera visualization options (labels, frusta, image planes) on top of the MeshManager.\n\t  \\ingroup sibr_view\n\t */\n\tclass SIBR_VIEW_EXPORT SceneDebugView : public MultiMeshManager, public ImageCamViewer, public LabelsManager\n\t{\n\t\tSIBR_CLASS_PTR(SceneDebugView);\n\n\tpublic:\n\n\t\t/** Which camera info should be displayed in the GUI. */\n\t\tenum CameraInfoDisplay { SIZE, FOCAL, FOV_Y, ASPECT };\n\n\t\t/** Constructor.\n\t\t * \\param scene the scene to display\n\t\t * \\param camHandler a camera handler to display as a \"user camera\"\n\t\t * \\param myArgs dataset arguments (needed to load/save the camera location)\n\t\t */\n\t\tSceneDebugView(const IIBRScene::Ptr& scene, const InteractiveCameraHandler::Ptr & camHandler, const BasicDatasetArgs& myArgs, const std::string& imagesPath = \"\");\n\n\t\t/** Constructor.\n\t\t * \\param scene the scene to display\n\t\t * \\param viewport the view viewport\n\t\t * \\param camHandler a camera handler to display as a \"user camera\"\n\t\t * \\param myArgs dataset arguments (needed to load/save the camera location)\n\t\t * \\warning Deprecated, use the version without the viewport.\n\t\t */\n\t\tSceneDebugView(const IIBRScene::Ptr& scene, const Viewport& viewport, const InteractiveCameraHandler::Ptr& camHandler, const BasicDatasetArgs& myArgs);\n\n\t\t/** Update state based on user input.\n\t\t * \\param input user input\n\t\t * \\param deltaTime the time elapsed since last update\n\t\t * \\param viewport input viewport\n\t\t * \\note Used when the view is in a multi-view system.\n\t\t */\n\t\tvirtual void onUpdate(Input & input, const float deltaTime, const Viewport & viewport = Viewport(0.0f, 0.0f, 0.0f, 0.0f));\n\n\t\t/** Update state based on user input.\n\t\t * \\param input user input\n\t\t * \\param viewport input viewport\n\t\t * \\note Used when the view is in a multi-view system.\n\t\t */\n\t\tvirtual void onUpdate(Input & input, const Viewport & viewport) override;\n\n\t\t/* Update state based on user input.\n\t\t * \\param input user input\n\t\t */\n\t\tvirtual void onUpdate(Input& input) override;\n\t\t\n\t\t/** Render content in a window.\n\t\t *\\param win destination window\n\t\t */\n\t\tvirtual void onRender(Window& win) override;\n\n\t\t/** Render content in the currently bound RT, using a specific viewport.\n\t\t * \\param viewport destination viewport\n\t\t * \\note Used when the view is in a multi-view system.\n\t\t */\n\t\tvirtual void onRender(const Viewport & viewport) override;\n\n\t\tusing MultiMeshManager::onRender;\n\n\t\t/** Update and display GUI panels. */\n\t\tvirtual void onGUI() override;\n\n\t\t/** Save the top view camera to scene/cameras/topview.txt. */\n\t\tvoid save();\n\n\t\t/** \\return the camera handler for the view. */\n\t\tconst InteractiveCameraHandler & getCamera() const { return camera_handler; }\n\n\t\t/** \\return the camera handler for the view. */\n\t\tInteractiveCameraHandler & getCamera() { return camera_handler; }\n\n\t\t/** Replace the scene.\n\t\t *\\param scene the new scene\n\t\t *\\param preserveCamera should the current camera position be preserved\n\t\t **/\n\t\tvoid setScene(const IIBRScene::Ptr & scene, bool preserveCamera = false);\n\n\t\t/** Update the active status of all cameras\n\t\t *\\param cams_id the active camera IDs. \n\t\t */\n\t\tvoid updateActiveCams(const std::vector<uint> & cams_id);\n\n\t\t/*\n\t\t* Read from chunks.txt files their center, names and extents (if file exists btw)\n\t\t*/\n\t\tvoid  loadChunksData(const char* file);\n\n\t\tvoid createChunksMesh(bool use_z = false);\n\n\t\tvoid recomputeSelectedChunksMesh();\n\n\tprotected:\n\n\t\t/** Generate the GUI for the display options. */\n\t\tvoid gui_options();\n\n\t\t/** generate the GUI with the camera infos. */\n\t\tvoid gui_cameras();\n\n\t\t/** generate the GUI with the chunks infos. */\n\t\tvoid gui_chunks();\n\n\t\t/** Setup the view. */\n\t\tvoid setup();\n\n\t\t/** Setup the geometry. */\n\t\tvoid setupMeshes();\n\n\t\tInteractiveCameraHandler::Ptr\t_userCurrentCam; ///< The \"main view\" camera handler (will be displayed as an extra camera).\n\t\tIIBRScene::Ptr\t\t\t\t\t_scene; ///< Current displayed scene.\n\t\tstd::vector<CameraInfos>\t\t_cameras; ///< Additional scene cameras info.\n\t\tCameraInfoDisplay\t\t\t\t_camInfoOption = SIZE; ///< Camera info to display in the GUI.\n\t\tstd::string\t\t\t\t\t\t_topViewPath; ///< Path to the topview saved file.\n\t\tint\t\t\t\t\t\t\t\t_snapToImage = 0; ///< ID of the camera to snap to.\n\t\tint\t\t\t\t\t\t\t\t_cameraIdInfoGUI = 0; ///< ID of the camera to display info about.\n\t\tbool\t\t\t\t\t\t\t_showImages = true; ///< Show the image planes.\n\t\tbool\t\t\t\t\t\t\t_showLabels = false; ///< Show camera labels.\n\n\t\tstd::string\t\t\t\t\t\t_images_path;\n\t\tint\t\t\t\t\t\t\t\t_renderingCam; ///< ID of the camera used for rendering\n\t\tstd::string\t\t\t\t\t\t_currentChunk = \"\";\n\t\tbool\t\t\t\t\t\t\t_highlight_current_chunk;\n\t\tbool\t\t\t\t\t\t\t_dirty_selected_chunks_mesh = false;\n\t\tstd::vector<Chunk> chunks;\n\n\t\tMesh::Ptr current_chunk_mesh = std::make_shared<Mesh>();\n\t\tMesh::Ptr close_cams = std::make_shared<Mesh>();\n\t\tMesh::Ptr _used_cams = std::make_shared<Mesh>();\n\t\tMesh::Ptr _non_used_cams = std::make_shared<Mesh>();\n\t\tMesh::Ptr _user_cam = std::make_shared<Mesh>();\n\t\tMesh::Ptr chunks_mesh = std::make_shared<Mesh>();\n\t\tMesh::Ptr selected_chunks_mesh = std::make_shared<Mesh>();\n\t};\n\n} // namespace\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/Skybox.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n# include \"core/assets/Resources.hpp\"\r\n# include \"core/view/Skybox.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\tbool\tSkybox::load(const std::string& skyFolder)\r\n\t{\r\n\t\tif (!sibr::directoryExists(skyFolder))\r\n\t\t\treturn false;\r\n\r\n\t\t_shader.init(\"Skybox\",\r\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"skybox.vp\")),\r\n\t\t\tsibr::loadFile(sibr::Resources::Instance()->getResourceFilePathName(\"skybox.fp\")));\r\n\t\t_paramView.init(_shader, \"in_View\");\r\n\t\t_paramAspect.init(_shader, \"in_Aspect\");\r\n\r\n\t\tstd::array<const char*, 6> filenames = {\r\n\t\t\t\"right.jpg\"\t\t,\r\n\t\t\t\"left.jpg\"\t\t,\r\n\t\t\t\"top.jpg\"\t\t,\r\n\t\t\t\"bottom.jpg\"\t,\r\n\t\t\t\"forward.jpg\"\t,\r\n\t\t\t\"back.jpg\"\t\t\r\n\t\t};\r\n\r\n\t\tstd::array<ImageRGB, filenames.size()>\timages;\r\n\r\n\t\tfor (uint i = 0; i < filenames.size(); ++i)\r\n\t\t{\r\n\t\t\tstd::string file = (skyFolder + \"/\") + filenames[i];\r\n\t\t\tif (images[i].load(file) == false)\r\n\t\t\t{\r\n\t\t\t\tSIBR_ERR << \"cannot open \" << file << \" (loading the skybox)\" << std::endl;\r\n\t\t\t}\r\n\t\t}\r\n\t\t_cubemap.reset(new TextureCubeMapRGB(images[0], images[1], images[2], images[3], images[4], images[5]));\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\r\n\tvoid\tSkybox::render(const Camera& eye, const sibr::Vector2u& imgSize)\r\n\t{\r\n\t\tif (_cubemap == nullptr)\r\n\t\t\treturn;\r\n\r\n\r\n\t\tglDisable(GL_DEPTH_TEST);\r\n\r\n\t\tCHECK_GL_ERROR;\r\n\t\t_shader.begin();\r\n\t\tCHECK_GL_ERROR;\r\n\t\t_paramAspect.set(Vector2f(float(imgSize.x())/float(imgSize.y()), float(imgSize.y())/float(imgSize.x())));\r\n\t\tCHECK_GL_ERROR;\r\n\t\t_paramView.set(Matrix4f(eye.view().inverse()));\r\n\t\tCHECK_GL_ERROR;\r\n\t\t// cube map texture should already be bound\r\n\t\tglActiveTexture(GL_TEXTURE0);\r\n\t\tglBindTexture(GL_TEXTURE_CUBE_MAP, _cubemap->handle());\r\n\t\tCHECK_GL_ERROR;\r\n\r\n\t\tRenderUtility::useDefaultVAO();\r\n\t\tconst unsigned char indices[] = { 0, 1, 2, 3 };\r\n\t\tglDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);\r\n\t\tCHECK_GL_ERROR;\r\n\r\n\t\t_shader.end();\r\n\t}\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/Skybox.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"core/view/Config.hpp\"\n# include \"core/graphics/Shader.hpp\"\n# include \"core/graphics/Texture.hpp\"\n# include \"core/graphics/Camera.hpp\"\n\nnamespace sibr\n{\n\t/** A skybox object for rendering a cubemap texture.\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT Skybox\n\t{\n\t\tSIBR_CLASS_PTR(Skybox);\n\n\tpublic:\n\n\t\t/** Load skybox faces from a directory. The files should be named: {right, left, top, bottom, forward, back}.jpg\n\t\t\\param skyFolder directory path\n\t\t\\return a success boolean \n\t\t*/\n\t\tbool\tload(const std::string& skyFolder);\n\n\t\t/** Render in the current RT.\n\t\t\\param eye current viewpoint\n\t\t\\param imgSize the destination RT size\n\t\t*/\n\t\tvoid\trender(const Camera& eye, const sibr::Vector2u& imgSize);\n\n\tprivate:\n\n\t\tGLShader\t\t_shader; ///< Skybox shader.\n\t\tGLParameter\t\t_paramView; ///< VP parameter.\n\t\tGLParameter\t\t_paramAspect; ///< Aspect ratio parameter.\n\n\t\tTextureCubeMapRGB::Ptr\t_cubemap = nullptr; ///< Cubemap texture.\n\n\t};\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/TrackBall.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include \"TrackBall.h\"\r\n#include <boost/filesystem.hpp>\r\n#include \"core/graphics/Input.hpp\"\r\n#include \"core/graphics/Viewport.hpp\"\r\n#include \"core/raycaster/CameraRaycaster.hpp\" \r\n#include \"core/graphics/Window.hpp\"\r\n#include \"core/graphics/Mesh.hpp\"\r\n\r\nnamespace sibr {\r\n\r\n\tfloat TrackBall::ratioTrackBall2D = 0.75f;\r\n\r\n\tTrackBall::TrackBall(bool _verbose) : hasBeenInitialized(false), shadersCompiled(false), state(TrackBallState::IDLE), verbose(_verbose),\r\n\t\tfixedCamera(InputCamera()), tempCamera(InputCamera())\r\n\t{\r\n\t\tdrawThis = true;\r\n\t}\r\n\r\n\tvoid TrackBall::update(const sibr::Input& input, const float deltaTime, const Viewport& viewport) {\r\n\t\tupdate(input, viewport, std::shared_ptr<Raycaster>());\r\n\t}\r\n\r\n\tconst InputCamera & TrackBall::getCamera(void) const\r\n\t{\r\n\t\tif (!hasBeenInitialized) {\r\n\t\t\tSIBR_ERR << \" TrackBall : camera not initialized before use\" << std::endl\r\n\t\t\t\t<< \"\\t you should use either fromMesh(), fromCamera() or load() \" << std::endl;\r\n\t\t}\r\n\t\tif (state == TrackBallState::IDLE) {\r\n\t\t\treturn fixedCamera;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn tempCamera;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::onRender(const sibr::Viewport& viewport) {\r\n\t\tif (!drawThis) { return; }\r\n\r\n\t\tif (!shadersCompiled) {\r\n\t\t\tinitTrackBallShader();\r\n\t\t}\r\n\r\n\t\tif (state == TrackBallState::IDLE) { return; }\r\n\r\n\t\t// Save current blending state and function.\r\n\t\tGLboolean blendState;\r\n\t\tglGetBooleanv(GL_BLEND, &blendState);\r\n\t\tGLint blendSrc, blendDst;\r\n\t\tglGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrc);\r\n\t\tglGetIntegerv(GL_BLEND_DST_ALPHA, &blendDst);\r\n\r\n\t\t// Enable basic blending.\r\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r\n\t\tglEnable(GL_BLEND);\r\n\r\n\t\t// Render.\r\n\t\tviewport.bind();\r\n\t\ttrackBallShader.begin();\r\n\t\tratioTrackBall2Dgpu.set(ratioTrackBall2D);\r\n\t\ttrackBallStateGPU.set((int)state);\r\n\t\tquadMesh->render(false, false, Mesh::RenderMode::FillRenderMode);\r\n\t\ttrackBallShader.end();\r\n\r\n\t\t// Restore blend state.\r\n\t\tif (!blendState) {\r\n\t\t\tglDisable(GL_BLEND);\r\n\t\t}\r\n\t\tglBlendFunc(blendSrc, blendDst);\r\n\t}\r\n\r\n\tvoid TrackBall::saveVectorInFile(std::ofstream & s, const Vector3f & v) const {\r\n\t\ts << v.x() << \" \" << v.y() << \" \" << v.z() << std::endl;\r\n\t}\r\n\r\n\tvoid TrackBall::setCameraAttributes(const Viewport & viewport)\r\n\t{\r\n\t\tfixedCamera.size((int)viewport.finalWidth(), (int)viewport.finalHeight());\r\n\t\tfixedCamera.aspect(viewport.finalWidth() / viewport.finalHeight());\r\n\t}\r\n\r\n\tvoid TrackBall::updateTrackBallCameraSize(const Viewport & viewport)\r\n\t{\r\n\t\tsibr::Vector2i viewPortSize = viewport.finalSize().cast<int>();\r\n\t\tfixedCamera.size(viewPortSize[0], viewPortSize[1]);\r\n\t}\r\n\r\n\tbool TrackBall::load(std::string & filePath, const Viewport & viewport)\r\n\t{\r\n\t\tstd::ifstream file(filePath.c_str());\r\n\t\tif (file.is_open()) {\r\n\t\t\tfloat a, b, c, fov, zNear, zFar;\r\n\t\t\tfile >> a >> b >> c;\r\n\t\t\tVector3f tbCenter(a, b, c);\r\n\t\t\tfile >> a >> b >> c;\r\n\t\t\tVector3f eye(a, b, c);\r\n\t\t\tfile >> a >> b >> c;\r\n\t\t\tVector3f up(a, b, c);\r\n\t\t\tfile >> fov >> zNear >> zFar;\r\n\r\n\t\t\ttempCenter = fixedCenter = tbCenter;\r\n\r\n\t\t\tfixedCamera.setLookAt(eye, fixedCenter, up);\r\n\t\t\tfixedCamera.fovy(fov);\r\n\t\t\tfixedCamera.znear(zNear);\r\n\t\t\tfixedCamera.zfar(zFar);\r\n\t\t\tsetCameraAttributes(viewport);\r\n\t\t\ttempCamera = fixedCamera;\r\n\r\n\t\t\thasBeenInitialized = true;\r\n\t\t\tprintMessage(\" n trackBall loaded \" + filePath);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse {\r\n\t\t\tprintMessage(\" could not open trackBall\" + filePath);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::save(std::string & filePath) const\r\n\t{\r\n\t\tif (boost::filesystem::exists(filePath)) {\r\n\t\t\tchar c;\r\n\t\t\tSIBR_LOG << \" a track ball already exists, override ? y/n ... \" << std::flush;\r\n\t\t\tstd::cin >> c;\r\n\t\t\tif (c != 'y') {\r\n\t\t\t\tstd::cout << \" not saved ! \" << std::endl;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\t\tstd::ofstream file(filePath.c_str());\r\n\t\tif (file.is_open()) {\r\n\t\t\tsaveVectorInFile(file, fixedCenter);\r\n\t\t\tsaveVectorInFile(file, fixedCamera.position());\r\n\t\t\tsaveVectorInFile(file, fixedCamera.up());\r\n\t\t\tfile << fixedCamera.fovy() << \" \" << fixedCamera.znear() << \" \" << fixedCamera.zfar() << std::endl;\r\n\t\t\tSIBR_LOG << \" TrackBall saved at \" << filePath << std::endl;\r\n\t\t}\r\n\t\telse {\r\n\t\t\tSIBR_LOG << \" Could not save trackBall\" << std::endl;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::fromCamera(const InputCamera & cam, const Viewport & viewport, const float & radius)\r\n\t{\r\n\t\tfixedCamera = cam;\r\n\r\n\t\tif (fixedCamera.zfar() == 0 || fixedCamera.znear() == 0) {\r\n\t\t\tInputCamera defaultCam = InputCamera();\r\n\t\t\tfixedCamera.znear(defaultCam.znear());\r\n\t\t\tfixedCamera.zfar(defaultCam.zfar());\r\n\t\t}\r\n\r\n\t\tsetCameraAttributes(viewport);\r\n\t\ttempCamera = fixedCamera;\r\n\t\ttempCenter = fixedCenter = cam.position() + cam.dir().normalized() * radius;\r\n\r\n\t\thasBeenInitialized = true;\r\n\t}\r\n\r\n\tbool TrackBall::fromMesh(const Mesh & mesh, const Viewport & viewport)\r\n\t{\r\n\t\treturn fromBoundingBox(mesh.getBoundingBox(), viewport);\r\n\t}\r\n\r\n\tbool TrackBall::fromBoundingBox(const Eigen::AlignedBox<float, 3> & box, const Viewport & viewport)\r\n\t{\r\n\r\n\t\tif (box.isEmpty() || (box.diagonal().array() == 0.0f).any()) {\r\n\t\t\tSIBR_LOG << \" [WARNING] TrackBall::fromMesh : cannot create camera from flat mesh \" << std::endl;\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\telse {\r\n\t\t\ttempCenter = fixedCenter = box.center();\r\n\t\t\tVector3f eye = fixedCenter + box.diagonal();\r\n\t\t\tVector3f up(0, 1, 0);\r\n\r\n\t\t\tfixedCamera.setLookAt(eye, fixedCenter, up);\r\n\r\n\t\t\tfixedCamera.zfar(2.0f*box.diagonal().norm());\r\n\t\t\tsetCameraAttributes(viewport);\r\n\t\t\ttempCamera = fixedCamera;\r\n\t\t\thasBeenInitialized = true;\r\n\t\t\tprintMessage(\" TrackBall::fromMesh : camera created \");\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::update(const Input & input, const Viewport & viewport, std::shared_ptr<Raycaster> raycaster)\r\n\t{\r\n\t\tif( !hasBeenInitialized || input.empty()) { return; }\r\n\r\n\t\tupdateTrackBallCameraSize(viewport);\r\n\r\n\t\tupdateTrackBallStatus(input, viewport);\r\n\r\n\t\tupdateTrackBallCamera(input, viewport, raycaster);\r\n\r\n\t\tupdateFromKeyboard(input);\r\n\t}\r\n\r\n\tvoid TrackBall::updateAspectWithViewport(const Viewport & viewport)\r\n\t{\r\n\t\tfixedCamera.size(static_cast<uint>(viewport.finalWidth()), static_cast<uint>(viewport.finalHeight()));\r\n\t\tfixedCamera.aspect(viewport.finalHeight() / viewport.finalWidth());\r\n\t}\r\n\r\n\tvoid TrackBall::updateTrackBallStatus(const Input & input, const Viewport & viewport)\r\n\t{\r\n\t\tcurrentPoint2D = input.mousePosition();\r\n\r\n\t\tif (input.key().isActivatedOnly(Key::T) && input.key().isActivatedOnly(Key::V)) {\r\n\t\t\tverbose = !verbose;\r\n\t\t\tif (verbose) {\r\n\t\t\t\tprintMessage(\"trackBall is now verbose \");\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tSIBR_LOG << \" TrackBall not verbose anymore \" << std::endl;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (input.key().isActivated(Key::LeftControl)) {\r\n\t\t\tstate = TrackBallState::IDLE;\r\n\t\t}\r\n\t\telse if (input.mouseButton().isPressed(Mouse::Right)) {\r\n\t\t\tlastPoint2D = currentPoint2D;\r\n\t\t\ttempCamera = fixedCamera;\r\n\t\t\ttempCenter = fixedCenter;\r\n\t\t\tif (isInTrackBall2dRegion(lastPoint2D, viewport)) {\r\n\t\t\t\tstate = TrackBallState::TRANSLATION_PLANE;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tstate = TrackBallState::TRANSLATION_Z;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (input.mouseButton().isPressed(Mouse::Left)) {\r\n\t\t\tlastPoint2D = currentPoint2D;\r\n\t\t\ttempCamera = fixedCamera;\r\n\t\t\tif (isInTrackBall2dRegion(lastPoint2D, viewport)) {\r\n\t\t\t\tstate = TrackBallState::ROTATION_SPHERE;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tstate = TrackBallState::ROTATION_ROLL;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (input.mouseButton().isReleased(Mouse::Right) || input.mouseButton().isReleased(Mouse::Left)) {\r\n\t\t\tif (state != TrackBallState::IDLE) {\r\n\t\t\t\tstate = TrackBallState::IDLE;\r\n\t\t\t\tfixedCamera = tempCamera;\r\n\t\t\t\tfixedCenter = tempCenter;\r\n\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::updateTrackBallCamera(const Input & input, const Viewport & viewport, std::shared_ptr<Raycaster> raycaster)\r\n\t{\r\n\t\tif (state == TrackBallState::ROTATION_SPHERE) {\r\n\t\t\tupdateRotationSphere(input, viewport);\r\n\t\t}\r\n\t\telse if (state == TrackBallState::ROTATION_ROLL) {\r\n\t\t\tupdateRotationRoll(input, viewport);\r\n\t\t}\r\n\t\telse if (state == TrackBallState::TRANSLATION_PLANE) {\r\n\t\t\tupdateTranslationPlane(input, viewport, raycaster);\r\n\t\t}\r\n\t\telse if (state == TrackBallState::TRANSLATION_Z) {\r\n\t\t\tupdateTranslationZ(input, viewport);\r\n\t\t}\r\n\t\telse if (state == TrackBallState::IDLE) {\r\n\t\t\tif (input.key().isActivated(Key::LeftControl)) {\r\n\t\t\t\tupdateBallCenter(input, raycaster);\r\n\t\t\t}\r\n\t\t\telse if (input.mouseScroll() != 0) {\r\n\t\t\t\tupdateZnearZFar(input);\r\n\t\t\t\tupdateRadius(input);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::updateBallCenter(const Input & input, std::shared_ptr<Raycaster> raycaster)\r\n\t{\r\n\r\n\t\tif (raycaster.get() == nullptr || !input.mouseButton().isPressed(Mouse::Left)) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tsibr::Vector3f worldPos, dir;\r\n\t\tif(fixedCamera.ortho())\r\n\t\t{\r\n\t\t\tsibr::Vector2i clickPos = input.mousePosition();\r\n\t\t\tworldPos = fixedCamera.position() +\r\n\t\t\t\t\t\t\t\t\t(2.0f*clickPos.x() / (float)fixedCamera.w() - 1.0f)*fixedCamera.orthoRight()*fixedCamera.right()\r\n\t\t\t\t\t\t\t\t\t+ (2.0f*((float)fixedCamera.h() - 1 - clickPos.y()) / (float)fixedCamera.h() - 1.0f)*fixedCamera.orthoTop()*fixedCamera.up();\r\n\t\t\tdir = fixedCamera.dir();\r\n\r\n\t\t}\r\n\t\telse {\r\n\t\t\tdir = CameraRaycaster::computeRayDir(fixedCamera, input.mousePosition().cast<float>()).normalized();\r\n\t\t\tworldPos = fixedCamera.position();\r\n\t\t}\r\n\t\tRayHit hit = raycaster->intersect(Ray(worldPos, dir));\r\n\r\n\t\tif (hit.hitSomething()) {\r\n\t\t\tprintMessage(\" TrackBall::updateBallCenter : updating center from mesh \");\r\n\t\t\tVector3f intersection(worldPos + hit.dist()*dir.normalized());\r\n\t\t\tfixedCenter = tempCenter = intersection;\r\n\t\t\tfixedCamera.setLookAt(worldPos, fixedCenter, fixedCamera.up());\r\n\t\t}\r\n\t\telse {\r\n\t\t\tprintMessage(\" TrackBall::updateBallCenter : could not intersect mesh \");\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tvoid TrackBall::updateRotationSphere(const Input & input, const Viewport & viewport)\r\n\t{\r\n\t\tif (!isInTrackBall2dRegion(input.mousePosition(), viewport) || input.mousePosition() == lastPoint2D) { return; }\r\n\t\tVector3f lastPointSphere(mapToSphere(lastPoint2D, viewport));\r\n\t\tVector3f newPointSphere(mapToSphere(input.mousePosition(), viewport));\r\n\t\tVector3f rotationAxisScreenSpace((lastPointSphere.cross(newPointSphere)).normalized());\r\n\t\tVector4f axis;\r\n\t\taxis << rotationAxisScreenSpace, 0.0f;\r\n\t\tVector3f rotationAxisWorldSpace((fixedCamera.view().inverse()* axis).xyz());\r\n\r\n\t\tfloat angleCos = newPointSphere.dot(lastPointSphere);\r\n\t\tif (std::abs(angleCos) < 1.0f) {\r\n\t\t\tfloat rotationAngle = -2.0f * acos(angleCos);\r\n\t\t\tEigen::Quaternionf rot(Eigen::AngleAxisf(rotationAngle, rotationAxisWorldSpace));\r\n\r\n\t\t\tfloat radius = (fixedCamera.position() - fixedCenter).norm();\r\n\t\t\tVector3f oldEye = -fixedCamera.dir().normalized();\r\n\t\t\tVector3f newEye = fixedCenter + radius * (rot*oldEye);\r\n\t\t\ttempCamera.setLookAt(newEye, fixedCenter, fixedCamera.up());\r\n\t\t\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::updateRotationRoll(const Input & input, const Viewport & viewport)\r\n\t{\r\n\t\tif (isInTrackBall2dRegion(input.mousePosition(), viewport)) { return; }\r\n\r\n\t\tVector2f viewportCenter(0.5f*(viewport.finalLeft() + viewport.finalRight()), 0.5f*(viewport.finalTop() + viewport.finalBottom()));\r\n\t\tfloat clockwise = (areClockWise(viewportCenter, lastPoint2D.cast<float>(), input.mousePosition().cast<float>()) ? -1.0f : 1.0f);\r\n\t\tfloat diagonal = std::sqrt((float)(viewport.finalWidth()*viewport.finalWidth() + viewport.finalHeight()*viewport.finalHeight()));\r\n\t\tfloat rollAngle = clockwise * (float)M_PI * (float)(lastPoint2D - input.mousePosition()).norm() / diagonal;\r\n\r\n\t\tEigen::Quaternionf rot(Eigen::AngleAxisf(rollAngle, -fixedCamera.dir().normalized()));\r\n\t\tVector3f newUp = rot * fixedCamera.up().normalized();\r\n\r\n\t\ttempCamera.setLookAt(fixedCamera.position(), fixedCenter, newUp);\r\n\t}\r\n\r\n\tvoid TrackBall::updateTranslationPlane(const Input & input, const Viewport & viewport, std::shared_ptr<Raycaster> raycaster)\r\n\t{\r\n\t\tif (!isInTrackBall2dRegion(input.mousePosition(), viewport)) { return; }\r\n\r\n\t\tif (input.mouseButton().isPressed(Mouse::Right)) {\r\n\r\n\t\t\tsibr::Vector3f worldPos, dir;\r\n\t\t\tif(fixedCamera.ortho())\r\n\t\t\t{\r\n\t\t\t\tsibr::Vector2i clickPos = input.mousePosition();\r\n\t\t\t\tworldPos = fixedCamera.position() +\r\n\t\t\t\t\t\t\t\t\t\t(2.0f*clickPos.x() / (float)fixedCamera.w() - 1.0f)*fixedCamera.orthoRight()*fixedCamera.right()\r\n\t\t\t\t\t\t\t\t\t\t+ (2.0f*((float)fixedCamera.h() - 1 - clickPos.y()) / (float)fixedCamera.h() - 1.0f)*fixedCamera.orthoTop()*fixedCamera.up();\r\n\t\t\t\tdir = fixedCamera.dir();\r\n\t\t\t\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tdir = CameraRaycaster::computeRayDir(fixedCamera, input.mousePosition().cast<float>()).normalized();\r\n\t\t\t\tworldPos = fixedCamera.position();\r\n\t\t\t}\r\n\r\n\t\t\tVector3f pointOnPlane = fixedCenter;\r\n\t\t\tif (raycaster.get() != nullptr) {\r\n\t\t\t\tRayHit hit = raycaster->intersect(Ray(worldPos, dir));\r\n\t\t\t\tif (hit.hitSomething()) {\r\n\t\t\t\t\tpointOnPlane = worldPos + hit.dist()*dir;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ttrackballPlane = Eigen::Hyperplane<float, 3>(fixedCamera.dir().normalized(), pointOnPlane);\r\n\t\t}\r\n\r\n\t\tVector3f clicked3DPosition(mapTo3Dplane(lastPoint2D));\r\n\t\tVector3f current3DPosition(mapTo3Dplane(input.mousePosition()));\r\n\t\tVector3f shift3D = clicked3DPosition - current3DPosition;\r\n\r\n\t\ttempCenter = fixedCenter + shift3D / zoom;\r\n\t\ttempCamera.setLookAt(fixedCamera.position() + shift3D, tempCenter, fixedCamera.up());\r\n\t}\r\n\r\n\tvoid TrackBall::updateTranslationZ(const Input & input, const Viewport & viewport)\r\n\t{\r\n\t\tif (isInTrackBall2dRegion(input.mousePosition(), viewport)) { return; }\r\n\t\tVector3f zAxis = -fixedCamera.dir().normalized();\r\n\r\n\t\tVector2i shift2D(input.mousePosition() - lastPoint2D);\r\n\t\tVector2f shift2Df(shift2D.cast<float>().array() / Vector2f(viewport.finalWidth(), viewport.finalHeight()).array());\r\n\r\n\t\tint whichDir = (std::abs(shift2D.x()) > std::abs(shift2D.y()) ? 0 : 1);\r\n\r\n\t\tfloat shift = 4.0f*(fixedCenter - fixedCamera.position()).norm()*(whichDir == 0 ? -1.0f : 1.0f)*shift2Df[whichDir];\r\n\t\tVector3f shift3D = shift * zAxis;\r\n\t\ttempCenter = fixedCenter + shift3D / zoom;\r\n\t\ttempCamera.setLookAt(fixedCamera.position() + shift3D, tempCenter, fixedCamera.up());\r\n\t}\r\n\r\n\tvoid TrackBall::updateFromKeyboard(const Input & input)\r\n\t{\r\n\t\tfloat angle = 0.005f;\r\n\t\tfloat angleChange = 0.0f;\r\n\t\tenum Change { NONE, X, Y, Z };\r\n\t\tChange change = NONE;\r\n\r\n\t\tif (input.key().isActivated(sibr::Key::KPNum6)) {\r\n\t\t\tangleChange = +angle;\r\n\t\t\tchange = Y;\r\n\t\t}\r\n\t\tif (input.key().isActivated(sibr::Key::KPNum4)) {\r\n\t\t\tangleChange = -angle;\r\n\t\t\tchange = Y;\r\n\t\t}\r\n\t\tif (input.key().isActivated(sibr::Key::KPNum8)) {\r\n\t\t\tangleChange = -angle;\r\n\t\t\tchange = X;\r\n\t\t}\r\n\t\tif (input.key().isActivated(sibr::Key::KPNum2)) {\r\n\t\t\tangleChange = +angle;\r\n\t\t\tchange = X;\r\n\t\t}\r\n\t\tif (input.key().isActivated(sibr::Key::KPNum7)) {\r\n\t\t\tangleChange = -angle;\r\n\t\t\tchange = Z;\r\n\t\t}\r\n\t\tif (input.key().isActivated(sibr::Key::KPNum9)) {\r\n\t\t\tangleChange = +angle;\r\n\t\t\tchange = Z;\r\n\t\t}\r\n\t\tif (change != NONE) {\r\n\t\t\tVector3f zAxis = -fixedCamera.dir().normalized();\r\n\t\t\tVector3f yAxis = fixedCamera.up().normalized();\r\n\t\t\tVector3f xAxis = fixedCamera.right().normalized();\r\n\r\n\t\t\tVector3f rotAxis = (change == Z ? zAxis : change == Y ? yAxis : xAxis);\r\n\r\n\t\t\tEigen::Quaternionf rot(Eigen::AngleAxisf(angleChange, rotAxis));\r\n\t\t\tsibr::Vector3f newEye = fixedCamera.position();\r\n\t\t\tsibr::Vector3f newUp = yAxis;\r\n\t\t\tif (change == Z) {\r\n\t\t\t\tnewUp = rot * newUp;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tnewEye = rot * (newEye - fixedCenter) + fixedCenter;\r\n\t\t\t}\r\n\r\n\t\t\tfixedCamera.setLookAt(newEye, fixedCenter, newUp);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tvoid TrackBall::updateRadius(const Input & input)\r\n\t{\r\n\t\tif(input.key().getNumActivated() != 0){ return; }\r\n\t\tif (!fixedCamera.ortho()) {\r\n\t\t\tfloat zoomIn = (input.mouseScroll() > 0 ? -1.0f : 1.0f);\r\n\t\t\tfloat radius = (fixedCamera.position() - fixedCenter).norm();\r\n\t\t\tVector3f oldEye = -fixedCamera.dir().normalized();\r\n\t\t\tradius = radius * pow(1.25f, zoomIn);\r\n\t\t\tVector3f newEye = fixedCenter + radius * oldEye;\r\n\t\t\tfixedCamera.setLookAt(newEye, fixedCenter, fixedCamera.up());\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfloat zoomIn = (input.mouseScroll() > 0.0f ? -1.0f : 1.0f);\r\n\t\t\tfixedCamera.orthoRight(fixedCamera.orthoRight() * pow(1.25f, zoomIn));\r\n\t\t\tfixedCamera.orthoTop(fixedCamera.orthoTop() * pow(1.25f, zoomIn));\r\n\t\t\tzoom /= pow(1.25f, zoomIn);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid TrackBall::updateZnearZFar(const Input & input)\r\n\t{\r\n\t\tfloat direction = (input.mouseScroll() > 0 ? 1.0f : -1.0f);\r\n\r\n\t\tif (input.key().isActivatedOnly(Key::Z)) {\r\n\t\t\tfixedCamera.zfar(fixedCamera.zfar()* pow(1.25f, direction));\r\n\t\t\tprintMessage(\" zFar : \" + std::to_string(fixedCamera.zfar()));\r\n\t\t}\r\n\t\telse if (input.key().isActivatedOnly(sibr::Key::Z) && input.key().isActivatedOnly(Key::LeftShift)) {\r\n\t\t\tfixedCamera.znear(fixedCamera.znear()* pow(1.25f, direction));\r\n\t\t\tprintMessage(\" zNear : \" + std::to_string(fixedCamera.znear()));\r\n\t\t}\r\n\t\ttempCamera = fixedCamera;\r\n\t}\r\n\r\n\tbool TrackBall::isInTrackBall2dRegion(const Vector2i & pos2D, const Viewport & viewport) const\r\n\t{\r\n\t\tfloat pos_x = (lastPoint2D.x()) / viewport.finalWidth();\r\n\t\tfloat pos_y = (lastPoint2D.y()) / viewport.finalHeight();\r\n\t\tfloat min_ratio = 0.5f * (1.0f - TrackBall::ratioTrackBall2D);\r\n\t\tfloat max_ratio = 0.5f * (1.0f + TrackBall::ratioTrackBall2D);\r\n\t\treturn pos_x >= min_ratio && pos_x <= max_ratio && pos_y >= min_ratio && pos_y <= max_ratio;\r\n\t}\r\n\r\n\tVector3f TrackBall::mapToSphere(const Vector2i & pos2D, const Viewport & viewport) const\r\n\t{\r\n\t\t\r\n\t\tint xMin = (int)0;\r\n\t\tint xMax = (int)(viewport.finalRight() - viewport.finalLeft());\r\n\t\tint yMin = (int)0;\r\n\t\tint yMax = (int)(viewport.finalBottom() - viewport.finalTop());\r\n\r\n\t\tVector2i clampPos = pos2D.cwiseMin(Vector2i(xMax, yMax)).cwiseMax(Vector2i(xMin, yMin));\r\n\r\n\t\tdouble x = clampPos.x() / (double)viewport.finalWidth() - 0.5;\r\n\t\tdouble y = 0.5 - clampPos.y() / (double)viewport.finalHeight();\r\n\r\n\t\tdouble sinx = sin(M_PI * x * 0.5);\r\n\t\tdouble siny = sin(M_PI * y * 0.5);\r\n\t\tdouble sinx2siny2 = sinx * sinx + siny * siny;\r\n\r\n\t\treturn Vector3d(sinx, siny, sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0).cast<float>();\r\n\t}\r\n\r\n\tVector3f TrackBall::mapTo3Dplane(const Vector2i & pos2D) const\r\n\t{\r\n\t\tsibr::Vector3f worldPos, dir;\r\n\t\tif(fixedCamera.ortho())\r\n\t\t{\r\n\t\t\tworldPos = fixedCamera.position() +\r\n\t\t\t\t\t\t\t\t\t(2.0f*pos2D.x() / (float)fixedCamera.w() - 1.0f)*fixedCamera.orthoRight()*fixedCamera.right()\r\n\t\t\t\t\t\t\t\t\t+ (2.0f*((float)fixedCamera.h() - 1 - pos2D.y()) / (float)fixedCamera.h() - 1.0f)*fixedCamera.orthoTop()*fixedCamera.up();\r\n\t\t\tdir = fixedCamera.dir();\r\n\t\t\t\r\n\t\t}\r\n\t\telse {\r\n\t\t\tdir = CameraRaycaster::computeRayDir(fixedCamera, pos2D.cast<float>()).normalized();\r\n\t\t\tworldPos = fixedCamera.position();\r\n\t\t}\r\n\r\n\t\tEigen::ParametrizedLine<float, 3> line(worldPos, dir);\r\n\t\treturn line.intersectionPoint(trackballPlane);\r\n\t}\r\n\r\n\tbool TrackBall::areClockWise(const Vector2f & a, const Vector2f & b, const Vector2f & c) const\r\n\t{\r\n\t\tVector2f u((b - a).normalized());\r\n\t\tVector2f v((c - b).normalized());\r\n\t\tVector2f uOrtho(u.y(), -u.x());\r\n\t\treturn v.dot(uOrtho) >= 0;\r\n\t}\r\n\r\n\r\n\tvoid TrackBall::initTrackBallShader(void)\r\n\t{\r\n\t\tquadMesh = std::shared_ptr<Mesh>(new Mesh(true));\r\n\r\n\t\tint corners[4][2] = { {-1,-1}, {-1,1}, {1,-1}, {1,1} };\r\n\r\n\t\tstd::vector<float> vertexBuffer;\r\n\t\tfor (int i = 0; i < 4; i++) {\r\n\t\t\tVector3f corner((float)corners[i][0], (float)corners[i][1], 0.0f);\r\n\t\t\tfor (int c = 0; c < 3; c++) {\r\n\t\t\t\tvertexBuffer.push_back(corner[c]);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tint indices[6] = { 0, 1, 3, 0, 2, 3 };\r\n\t\tstd::vector<uint> indicesBuffer(&indices[0], &indices[0] + 6);\r\n\r\n\t\tquadMesh->vertices(vertexBuffer);\r\n\t\tquadMesh->triangles(indicesBuffer);\r\n\r\n\t\tstd::string trackBallVertexShader =\r\n\t\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\r\n\t\t\t\"out vec2 uv_coord;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tuv_coord = in_vertex.xy;\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tgl_Position = vec4(in_vertex.xy,0.0, 1.0);\t\t\\n\"\r\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\t\tstd::string trackBallFragmentShader =\r\n\t\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform float ratio;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"uniform int mState;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"in vec2 uv_coord;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tfloat minB = -ratio;\t \t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tfloat maxB = +ratio;\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tfloat x = uv_coord.x;\t\t \t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tfloat y = uv_coord.y;\t\t \t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\tbool fragOutside = ( x<minB || x>maxB || y<minB || y>maxB );\t\\n\"\r\n\t\t\t\"\tif( mState == 1 ){\t\t\t\t\t\t\t\t\t\t\t\t\\n\" //plane transl\t\r\n\t\t\t\"\t\tvec2 d = abs(uv_coord ) - vec2(ratio,ratio);\t\t\t\t\\n\"\r\n\t\t\t\"\t\tfloat v = min(max(d.x,d.y),0.0) + length(max(d,0.0));\t\t\\n\"\r\n\t\t\t\"\t\tfloat a =  0.2 * exp( - 5000.0 *v*v );\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\tout_color = vec4(1.0,0.0,0.0,a);\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t} else if (  mState == 2 && fragOutside  ){\t\t\t\t\t\t\\n\" //zoom transl\r\n\t\t\t\"\t\tout_color = vec4(0.0,1.0,0.0,0.1);\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t} else if (  mState == 3 ){\t\t\t\t\t\t\t\t\t\t\\n\" //sphere rot\t\r\n\t\t\t\"\t\tfloat d =  x*x + y*y - ratio*ratio;\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\tfloat a =  0.2 * exp( - 5000.0 *d*d );\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\tout_color = vec4(1.0,0.0,0.0,a);\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t} else if (  mState == 4  ){\t\t\t\t\t\t\t\t\t\\n\" //roll rot\t\r\n\t\t\t\"\t\tfloat d =  x*x + y*y - 0.5*(ratio+1.0)*ratio*ratio;\t\t\t\\n\"\r\n\t\t\t\"\t\tfloat a =  0.2 * exp( - 5000.0 *d*d );\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\tout_color = vec4(0.0,1.0,0.0,a);\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t} else {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t\tout_color = vec4(0.0,0.0,0.0,0.0);\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\t\ttrackBallShader.init(\"trackBallShader\", trackBallVertexShader, trackBallFragmentShader);\r\n\r\n\t\tratioTrackBall2Dgpu.init(trackBallShader, \"ratio\");\r\n\t\ttrackBallStateGPU.init(trackBallShader, \"mState\");\r\n\r\n\t\tshadersCompiled = true;\r\n\t}\r\n\r\n\tvoid TrackBall::printMessage(const std::string & msg) const\r\n\t{\r\n\t\tif (verbose) {\r\n\t\t\tstd::cout << msg << std::endl;\r\n\t\t}\r\n\t}\r\n\r\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/TrackBall.h",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n#include <memory>\r\n#include <fstream>\r\n\r\n#include \"Config.hpp\"\r\n#include \"core/graphics/Shader.hpp\"\r\n#include \"core/assets/InputCamera.hpp\"\r\n#include \"ICameraHandler.hpp\"\r\n\r\nnamespace sibr {\r\n\r\n\tclass Viewport;\r\n\tclass Mesh;\r\n\tclass Input;\r\n\tclass Raycaster;\r\n\r\n\t\r\n\t/** Provide a handler to interact using a trackball (based on mouse motions).\r\n\t* \\ingroup sibr_view\r\n\t*/\r\n\tclass SIBR_VIEW_EXPORT TrackBall : public ICameraHandler\r\n\t{\r\n\tpublic:\r\n\t\t/** Constructor\r\n\t\t\\param verbose log updates and infos. \r\n\t\t*/\r\n\t\tTrackBall( bool verbose = false );\r\n\r\n\t\t/** Load a trackball settings from a file on disk.\r\n\t\t\\param filePath path to the file\r\n\t\t\\param viewport current viewport\r\n\t\t\\return a success boolean\r\n\t\t\\note The viewport is needed to fill-in missing info.\r\n\t\t*/\r\n\t\tbool load( std::string & filePath ,  const Viewport & viewport);\r\n\r\n\t\t/** Save trackball settings to a file on disk.\r\n\t\t\\param filePath file path\r\n\t\t*/\r\n\t\tvoid save( std::string & filePath ) const ;\r\n\r\n\t\t/** Update the trackball pose from a reference camera.\r\n\t\t \\param cam the reference camera\r\n\t\t \\param viewport the viewport to use\r\n\t\t \\param radius the default trackball radius to use\r\n\t\t**/\r\n\t\tvoid fromCamera( const InputCamera & cam , const Viewport & viewport , const float & radius = 100.0f );\r\n\r\n\t\t/** Setup the trackball so that a mesh if visible and centered.\r\n\t\t\\param mesh the mesh to show\r\n\t\t\\param viewport the view viewport\r\n\t\t*/\r\n\t\tbool fromMesh( const Mesh & mesh, const Viewport & viewport );\r\n\r\n\t\t/** Setup the trackball so that a region of space if visible and centered.\r\n\t\t\\param box the region of space to cover\r\n\t\t\\param viewport the view viewport\r\n\t\t*/\r\n\t\tbool fromBoundingBox(const Eigen::AlignedBox<float, 3>& box, const Viewport & viewport);\r\n\r\n\t\t/** Update the trackball handler state. the raycaster is used when the user is clicking to center the trackball or panning.\r\n\t\t\t\\param input user input\r\n\t\t\t\\param viewport view viewport\r\n\t\t\t\\param raycaster an optional raycaster\r\n\t\t*/\r\n\t\tvoid update( const Input & input , const Viewport & viewport, std::shared_ptr<Raycaster> raycaster = std::shared_ptr<Raycaster>());\r\n\r\n\t\t/** update the internal aspect ratio.\r\n\t\t\\param viewport the new viewport\r\n\t\t*/\r\n\t\tvoid updateAspectWithViewport(const Viewport & viewport);\r\n\r\n\t\tbool drawThis; ///< Should the trackball overlay be displayed.\r\n\r\n\t\t/** \\return true if the trackball has been initialized */\r\n\t\tbool initialized() const { return hasBeenInitialized; }\r\n\r\n\t\t/// ICameraHandler interface\r\n\t\t/** Update the camera handler state.\r\n\t\t\\param input user input\r\n\t\t\\param deltaTime time elapsed since last udpate\r\n\t\t\\param viewport view viewport\r\n\t\t*/\r\n\t\tvirtual void update(const sibr::Input & input, const float deltaTime, const Viewport & viewport) override;\r\n\r\n\t\t/** \\return the current camera. */\r\n\t\tvirtual const InputCamera & getCamera(void) const override;\r\n\r\n\t\t/** Render on top of the associated view(s).\r\n\t\t\\param viewport the rendering region\r\n\t\t*/\r\n\t\tvirtual void onRender(const sibr::Viewport & viewport) override;\r\n\r\n\tprivate:\r\n\r\n\t\t/** Trackball interaction status. */\r\n\t\tenum class TrackBallState { IDLE, TRANSLATION_PLANE, TRANSLATION_Z, ROTATION_SPHERE, ROTATION_ROLL };\r\n\r\n\t\t/** Map a pixel to a point on the sphere.\r\n\t\t\\param pos2D pixel position\r\n\t\t\\param viewport view viewport\r\n\t\t\\return a 3D point on the sphere\r\n\t\t*/\r\n\t\tVector3f mapToSphere( const Vector2i & pos2D, const Viewport & viewport ) const;\r\n\r\n\r\n\t\t/** Map a pixel to a point on the plane.\r\n\t\t\\param pos2D pixel position\r\n\t\t\\return a 3D point on the plane\r\n\t\t*/\r\n\t\tVector3f mapTo3Dplane( const Vector2i & pos2D ) const;\r\n\r\n\t\t/** update near and far planes.\r\n\t\t\\param input user input\r\n\t\t*/\r\n\t\tvoid updateZnearZFar( const Input & input );\r\n\r\n\t\t/** update the trackball pivot center.\r\n\t\t\\param input user input\r\n\t\t\\param raycaster the scene raycaster\r\n\t\t*/\r\n\t\tvoid updateBallCenter( const Input & input,  std::shared_ptr<Raycaster> raycaster );\r\n\r\n\t\t/** Update the trackball radius.\r\n\t\t\\param input user input\r\n\t\t*/\r\n\t\tvoid updateRadius( const Input & input );\r\n\r\n\t\t/** Update the rotation parameters.\r\n\t\t\\param input user input\r\n\t\t\\param viewport view viewport\r\n\t\t*/\r\n\t\tvoid updateRotationSphere( const Input & input , const Viewport & viewport );\r\n\r\n\t\t/** Update the rotation parameters.\r\n\t\t\\param input user input\r\n\t\t\\param viewport view viewport\r\n\t\t*/\r\n\t\tvoid updateRotationRoll( const Input & input , const Viewport & viewport );\r\n\r\n\t\t/** Update the translation parameters.\r\n\t\t\\param input user input\r\n\t\t\\param viewport view viewport\r\n\t\t\\param raycaster optional scene raycaster\r\n\t\t*/\r\n\t\tvoid updateTranslationPlane( const Input & input , const Viewport & viewport, std::shared_ptr<Raycaster> raycaster = std::shared_ptr<Raycaster>() );\r\n\r\n\t\t/** Update the translation parameters.\r\n\t\t\\param input user input\r\n\t\t\\param viewport view viewport\r\n\t\t*/\r\n\t\tvoid updateTranslationZ( const Input & input , const Viewport & viewport );\r\n\r\n\t\t/** Update based on keys input\r\n\t\t\\param input user input\r\n\t\t*/\r\n\t\tvoid updateFromKeyboard(const Input & input);\r\n\r\n\t\t/** Check if a point is in the central trackball region.\r\n\t\t\\param pos2D position in the view\r\n\t\t\\param viewport view viewport\r\n\t\t\\return true if it falls inside\r\n\t\t*/\r\n\t\tbool isInTrackBall2dRegion( const Vector2i & pos2D, const Viewport & viewport ) const;\r\n\r\n\t\t/**\r\n\t\t * Check if three points are in clockwise order.\r\n\t\t * \\param a first point\r\n\t\t * \\param b second point\r\n\t\t * \\param c third point\r\n\t\t * \\return true if their order is clockwise.\r\n\t\t */\r\n\t\tbool areClockWise( const Vector2f & a, const Vector2f & b, const Vector2f & c ) const;\r\n\r\n\t\t/** Log a message.\r\n\t\t\\param msg essage string\r\n\t\t*/\r\n\t\tvoid printMessage( const std::string & msg ) const;\r\n\r\n\t\t/** Save vector to output stream.\r\n\t\t\\param s stream\r\n\t\t\\param v vector\r\n\t\t*/\r\n\t\tvoid saveVectorInFile( std::ofstream & s , const Vector3f & v ) const ;\r\n\r\n\t\t/** Setup trackball shader. */\r\n\t\tvoid initTrackBallShader( void );\r\n\r\n\t\t/** Set camera attributes\r\n\t\t\\param viewport the viewport to use\r\n\t\t*/\r\n\t\tvoid setCameraAttributes( const Viewport & viewport );\r\n\r\n\t\t/** Update camera size.\r\n\t\t\\param viewport the viewport to use\r\n\t\t*/\r\n\t\tvoid updateTrackBallCameraSize(const Viewport & viewport);\r\n\r\n\t\t/** Update trackball interaction status.\r\n\t\t  \\param input user input\r\n\t\t  \\param viewport the viewport to use\r\n\t\t*/\r\n\t\tvoid updateTrackBallStatus( const Input & input, const Viewport & viewport );\r\n\r\n\t\t/** Main update function.\r\n\t\t  \\param input user input\r\n\t\t  \\param viewport the viewport to use\r\n\t\t  \\param raycaster optional scene raycaster\r\n\t\t*/\r\n\t\tvoid updateTrackBallCamera( const Input & input, const Viewport & viewport ,  std::shared_ptr<Raycaster> raycaster = std::shared_ptr<Raycaster>() );\r\n\r\n\r\n\t\tInputCamera\t\t\t\t\tfixedCamera;\t///< Reference camera.\r\n\t\tInputCamera\t\t\t\t\ttempCamera;\t///< Temp camera.\r\n\r\n\t\tVector3f\t\t\t\t\tfixedCenter; ///< Current center.\r\n\t\tVector3f\t\t\t\t\ttempCenter; ///< Temp center.\r\n\r\n\t\tVector2i\t\t\t\t\tlastPoint2D; ///< Last clicked 2D point.\r\n\t\tVector2i\t\t\t\t\tcurrentPoint2D; ///< Current clicked point.\r\n\r\n\t\tEigen::Hyperplane<float,3>\ttrackballPlane; ///< Trackball translation plane.\r\n\r\n\t\tTrackBallState\t\t\t\tstate; ///< Current status.\r\n\r\n\t\tbool\t\t\t\t\t\thasBeenInitialized; ///< Initialized or not.\r\n\t\tbool\t\t\t\t\t\tverbose; ///< verbose or not.\r\n\r\n\t\tfloat\t\t\t\t\t\tzoom=1.0f;//zoom factor used for ortho cams\r\n\t\t//members used for interaction drawing\r\n\t\tstd::shared_ptr<Mesh>\t\tquadMesh; ///< Supporting mesh for the overlay.\r\n\t\tGLShader\t\t\t\t\ttrackBallShader; ///< Overlay shader.\r\n\t\tGLParameter\t\t\t\t\tratioTrackBall2Dgpu; ///< Aspect ratio.\r\n\t\tGLParameter\t\t\t\t\ttrackBallStateGPU; ///< Trackball state uniform.\r\n\t\tbool\t\t\t\t\t\tshadersCompiled; ///< Are the sahders ready.\r\n\t\tstatic float\t\t\t\tratioTrackBall2D; ///< 2D ratio parameter.\r\n\r\n\t};\r\n\r\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/UIShortcuts.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include <algorithm>\r\n#include <string> \r\n#include <sstream> \r\n#include <iomanip>\r\n#include \"core/view/UIShortcuts.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\t/*static*/ UIShortcuts& UIShortcuts::global( void )\r\n\t{\r\n\t\tstatic UIShortcuts instance;\r\n\t\treturn instance;\r\n\t}\r\n\r\n\tvoid\tUIShortcuts::list( void )\r\n\t{\r\n\t\t// Sort elements in alphabetical order.\r\n\t\tstd::vector<std::pair<std::string, const char *>> elems(_shortcuts.begin(), _shortcuts.end());\r\n\t\tstd::sort(elems.begin(), elems.end(), [](std::pair<std::string, const char *> a, std::pair<std::string, const char *> b) { return b.first < a.first; });\r\n\t\t\r\n\t\tstd::ostringstream oss;\r\n\t\tfor (auto& pair: elems)\r\n\t\t\toss << \"  \" << std::setw(24)<< std::left << pair.first << \" : \" << pair.second << std::endl;\r\n\t\tSIBR_LOG << \"List of Shortcuts:\\n\" << oss.str() << std::endl;\r\n\t}\r\n\r\n\tvoid\tUIShortcuts::add( const std::string& shortcut, const char* desc )\r\n\t{\r\n\t\tstd::string lshortcut = shortcut;\r\n\t\tstd::transform(lshortcut.begin(), lshortcut.end(), lshortcut.begin(), ::tolower);\r\n\r\n\t\tif (_shortcuts.find(lshortcut) == _shortcuts.end())\r\n\t\t\t_shortcuts[lshortcut] = desc;\r\n\t\telse\r\n\t\t{\r\n\t\t\tconst char* current = _shortcuts[lshortcut];\r\n\t\t\tif (current != desc)\r\n\t\t\t{\r\n\t\t\t\tSIBR_ERR << \"conflict with shortcuts.\\n\"\r\n\t\t\t\t\t\"Trying to register:\\n\"\r\n\t\t\t\t\t\"[\" << shortcut << \"] : \" << desc\r\n\t\t\t\t\t<< \"\\nBut already exists as:\\n\"\r\n\t\t\t\t\t\"[\" << shortcut << \"] : \" << current\r\n\t\t\t\t\t<< std::endl;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/UIShortcuts.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include <unordered_map>\r\n# include \"core/view/Config.hpp\"\r\n\r\nnamespace sibr\r\n{\r\n\t/** Register and display keyboard shortcuts.\r\n\t* \\todo The system should be more robust for collision detection.\r\n\t* \\ingroup sibr_view\r\n\t*/\r\n\tclass SIBR_VIEW_EXPORT UIShortcuts\r\n\t{\r\n\tpublic:\r\n\r\n\t\t/** Singleton. */\r\n\t\tstatic UIShortcuts& global( void );\r\n\r\n\t\t/** Print all registered shortcuts. */\r\n\t\tvoid\tlist( void );\r\n\r\n\t\t/** Register a shortcut.\r\n\t\t *\\param shortcut the shortcut keys\r\n\t\t *\\param desc the description\r\n\t\t */\r\n\t\tvoid\tadd( const std::string& shortcut, const char* desc );\r\n\r\n\r\n\tprivate:\r\n\t\tstd::unordered_map<std::string, const char*>\t_shortcuts; ///< List of shortcuts.\r\n\r\n\t};\r\n\r\n\r\n} // namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ViewBase.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n# include \"core/view/ViewBase.hpp\"\n\nnamespace sibr\n{\n\tViewBase::ViewBase(const unsigned int w, const unsigned int h)\n\t{\n\t\t_resolution = Vector2i(w, h);\n\t\t_whichRT = 6; // poisson filling\n\t}\n\n\tvoid \tViewBase::onUpdate(Input& input, const Viewport & vp) {\n\t\tonUpdate(input);\n\t}\n\n\tvoid\t\t\t\tViewBase::setResolution(const Vector2i& size)\n\t{\n\t\t_resolution = size;\n\t}\n\n\tconst Vector2i&\t\t\tViewBase::getResolution( void ) const\n\t{\n\t\treturn _resolution;\n\t}\n\n\tvoid\t\t\t\tViewBase::setFocus(bool focus)\n\t{\n\t\t_focus = focus;\n\t}\n\n\tbool\t\t\t\tViewBase::isFocused(void) const\n\t{\n\t\treturn _focus;\n\t}\n\n\tvoid ViewBase::setName(const std::string& name) {\n\t\t_name = name;\n\t}\n\n\tconst std::string & ViewBase::name() const {\n\t\treturn _name;\n\t}\n\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/ViewBase.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <type_traits>\n\n# include \"core/graphics/Texture.hpp\"\n# include \"core/graphics/Camera.hpp\"\n# include \"core/view/Config.hpp\"\n//# include \"core/view/IBRScene.hpp\"\n#include \"core/graphics/Input.hpp\"\n#include \"core/graphics/Window.hpp\"\n\nnamespace sibr\n{\n\n\t/** Basic view representation. All views should inherit from it. \n\t* Can be added as a subview in a multi-window system.\n\t* \\sa MultiViewBase\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT ViewBase\n\t{\n\tpublic:\n\n\t\ttypedef std::shared_ptr<ViewBase> Ptr;\n\n\t\t/** Constructor.\n\t\t *\\param w view width\n\t\t *\\param h view height\n\t\t */\n\t\tViewBase( const unsigned int w=720, const unsigned int h=480);\n\n\t\t/** Destructor. */\n\t\tvirtual ~ViewBase() = default;\n\n\t\t/* Update state based on user input.\n\t\t * \\param input user input\n\t\t */\n\t\tvirtual void\tonUpdate(Input& input) { }\n\n\t\t/** Render content in a window.\n\t\t *\\param win destination window\n\t\t */\n\t\tvirtual void\tonRender( Window& win)\t\t{ }\n\n\t\t/** Render content in a given rendertarget.\n\t\t *\\param dst destination RT\n\t\t *\\param eye current viewpoint\n\t\t *\\sa IRenderingMode\n\t\t */\n\t\tvirtual void\tonRenderIBR(IRenderTarget& dst, const Camera& eye) {};\n\n\t\t/** Display GUI. */\n\t\tvirtual void\tonGUI() { }\n\n\t\t/** Render content in the currently bound RT, using a specific viewport.\n\t\t * \\param vpRender destination viewport\n\t\t * \\note Used when the view is in a multi-view system.\n\t\t */\n\t\tvirtual void\tonRender(const Viewport & vpRender) {  }\n\n\t\t/** Update state based on user input.\n\t\t * \\param input user input\n\t\t * \\param vp input viewport\n\t\t * \\note Used when the view is in a multi-view system.\n\t\t */\n\t\tvirtual void\tonUpdate(Input& input, const Viewport & vp);\n\n\t\t/** Legacy: Used to mix with previous pass.\n\t\t * \\param prev the previous step RT\n\t\t */\n\t\tvirtual void\tpreRender(RenderTargetRGB& prev) {} ;\n\n\t\t/** Legacy: Set the internal RT to use.\n\t\t *\\param i RT index */\n\t\tvirtual void\t\twhichRT(uint i)\t\t\t{ _whichRT=i; }\n\t\t/** Legacy: \\return the current selected RT ID. */\n\t\tvirtual uint\t\twhichRT(void)\t\t\t{ return _whichRT; }\n\n\t\t/** Set the view resolution.\n\t\t\\param size the new resolution, in pixels.\n\t\t*/\n\t\tvoid\t\t\t\tsetResolution(const Vector2i& size);\n\t\t/**\\return the current resolution. */\n\t\tconst Vector2i&\t\tgetResolution( void ) const;\n\n\t\t/** Toggle view status.\n\t\t\\param act if true, the view is active.\n\t\t*/\n\t\tvoid\t\t\t\tactive(bool act) { _active = act; }\n\t\t/** \\return true if the view is currently active. */\n\t\tbool\t\t\t\tactive() { return _active; }\n\n\t\t/** Toggle view focus (ie the user is interacting with it).\n\t\t\\param focus if true, the view is currently focused \n\t\t*/\n\t\tvoid\t\t\t\tsetFocus(bool focus);\n\t\t/** \\return true if the view is currently focused (ie the user is interacting with it). */\n\t\tbool\t\t\t\tisFocused(void) const;\n\n\t\t\n\t\t/** Define the name of the view (used for disambiguation of GUI, etc.).\n\t\t * \\param name the new name\n\t\t */\n\t\tvoid\t\t\t\tsetName(const std::string & name);\n\n\t\t/** \\return the name of the view. */\n\t\tconst std::string & name() const;\n\t\t\n\tprotected:\n\t\tuint\t\t\t_whichRT; ///< Selected RT id.\n\t\tstd::vector<RenderTargetLum::Ptr>\t_masks; ///< Rendering masks that can beused by some views/renderers.\n\n\t\tbool\t\t\t_active = true; ///< Is the view active.\n\t\tVector2i\t\t_resolution; ///< View resolution.\n\t\tbool\t\t\t_focus = false; ///< Is the view focused.\n\t\tstd::string\t\t_name = \"\"; ///< View name.\n\n\t};\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/httplib.h",
    "content": "//\n//  httplib.h\n//\n//  Copyright (c) 2024 Yuji Hirose. All rights reserved.\n//  MIT License\n//\n\n#ifndef CPPHTTPLIB_HTTPLIB_H\n#define CPPHTTPLIB_HTTPLIB_H\n\n#define CPPHTTPLIB_VERSION \"0.18.1\"\n\n/*\n * Configuration\n */\n\n#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND\n#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND\n#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000\n#endif\n\n#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT\n#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100\n#endif\n\n#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND\n#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300\n#endif\n\n#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND\n#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND\n#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND\n#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND\n#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND\n#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND\n#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND\n#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND\n#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND\n#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND\n#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND\n#ifdef _WIN32\n#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000\n#else\n#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0\n#endif\n#endif\n\n#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH\n#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH\n#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT\n#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20\n#endif\n\n#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT\n#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024\n#endif\n\n#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH\n#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())\n#endif\n\n#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH\n#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_RANGE_MAX_COUNT\n#define CPPHTTPLIB_RANGE_MAX_COUNT 1024\n#endif\n\n#ifndef CPPHTTPLIB_TCP_NODELAY\n#define CPPHTTPLIB_TCP_NODELAY false\n#endif\n\n#ifndef CPPHTTPLIB_IPV6_V6ONLY\n#define CPPHTTPLIB_IPV6_V6ONLY false\n#endif\n\n#ifndef CPPHTTPLIB_RECV_BUFSIZ\n#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)\n#endif\n\n#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ\n#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)\n#endif\n\n#ifndef CPPHTTPLIB_THREAD_POOL_COUNT\n#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \\\n  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \\\n                      ? std::thread::hardware_concurrency() - 1                \\\n                      : 0))\n#endif\n\n#ifndef CPPHTTPLIB_RECV_FLAGS\n#define CPPHTTPLIB_RECV_FLAGS 0\n#endif\n\n#ifndef CPPHTTPLIB_SEND_FLAGS\n#define CPPHTTPLIB_SEND_FLAGS 0\n#endif\n\n#ifndef CPPHTTPLIB_LISTEN_BACKLOG\n#define CPPHTTPLIB_LISTEN_BACKLOG 5\n#endif\n\n/*\n * Headers\n */\n\n#ifdef _WIN32\n#ifndef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif //_CRT_SECURE_NO_WARNINGS\n\n#ifndef _CRT_NONSTDC_NO_DEPRECATE\n#define _CRT_NONSTDC_NO_DEPRECATE\n#endif //_CRT_NONSTDC_NO_DEPRECATE\n\n#if defined(_MSC_VER)\n#if _MSC_VER < 1900\n#error Sorry, Visual Studio versions prior to 2015 are not supported\n#endif\n\n#pragma comment(lib, \"ws2_32.lib\")\n\n#ifdef _WIN64\nusing ssize_t = __int64;\n#else\nusing ssize_t = long;\n#endif\n#endif // _MSC_VER\n\n#ifndef S_ISREG\n#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)\n#endif // S_ISREG\n\n#ifndef S_ISDIR\n#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)\n#endif // S_ISDIR\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif // NOMINMAX\n\n#include <io.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n\n#ifndef WSA_FLAG_NO_HANDLE_INHERIT\n#define WSA_FLAG_NO_HANDLE_INHERIT 0x80\n#endif\n\nusing socket_t = SOCKET;\n#ifdef CPPHTTPLIB_USE_POLL\n#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)\n#endif\n\n#else // not _WIN32\n\n#include <arpa/inet.h>\n#if !defined(_AIX) && !defined(__MVS__)\n#include <ifaddrs.h>\n#endif\n#ifdef __MVS__\n#include <strings.h>\n#ifndef NI_MAXHOST\n#define NI_MAXHOST 1025\n#endif\n#endif\n#include <net/if.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#ifdef __linux__\n#include <resolv.h>\n#endif\n#include <netinet/tcp.h>\n#ifdef CPPHTTPLIB_USE_POLL\n#include <poll.h>\n#endif\n#include <csignal>\n#include <pthread.h>\n#include <sys/mman.h>\n#include <sys/select.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n\nusing socket_t = int;\n#ifndef INVALID_SOCKET\n#define INVALID_SOCKET (-1)\n#endif\n#endif //_WIN32\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <cctype>\n#include <climits>\n#include <condition_variable>\n#include <cstring>\n#include <errno.h>\n#include <exception>\n#include <fcntl.h>\n#include <fstream>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <list>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <random>\n#include <regex>\n#include <set>\n#include <sstream>\n#include <string>\n#include <sys/stat.h>\n#include <thread>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n#ifdef _WIN32\n#include <wincrypt.h>\n\n// these are defined in wincrypt.h and it breaks compilation if BoringSSL is\n// used\n#undef X509_NAME\n#undef X509_CERT_PAIR\n#undef X509_EXTENSIONS\n#undef PKCS7_SIGNER_INFO\n\n#ifdef _MSC_VER\n#pragma comment(lib, \"crypt32.lib\")\n#endif\n#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)\n#include <TargetConditionals.h>\n#if TARGET_OS_OSX\n#include <CoreFoundation/CoreFoundation.h>\n#include <Security/Security.h>\n#endif // TARGET_OS_OSX\n#endif // _WIN32\n\n#include <openssl/err.h>\n#include <openssl/evp.h>\n#include <openssl/ssl.h>\n#include <openssl/x509v3.h>\n\n#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)\n#include <openssl/applink.c>\n#endif\n\n#include <iostream>\n#include <sstream>\n\n#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)\n#if OPENSSL_VERSION_NUMBER < 0x1010107f\n#error Please use OpenSSL or a current version of BoringSSL\n#endif\n#define SSL_get1_peer_certificate SSL_get_peer_certificate\n#elif OPENSSL_VERSION_NUMBER < 0x30000000L\n#error Sorry, OpenSSL versions prior to 3.0.0 are not supported\n#endif\n\n#endif\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n#include <zlib.h>\n#endif\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n#include <brotli/decode.h>\n#include <brotli/encode.h>\n#endif\n\n/*\n * Declaration\n */\nnamespace httplib {\n\nnamespace detail {\n\n/*\n * Backport std::make_unique from C++14.\n *\n * NOTE: This code came up with the following stackoverflow post:\n * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique\n *\n */\n\ntemplate <class T, class... Args>\ntypename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type\nmake_unique(Args &&...args) {\n  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));\n}\n\ntemplate <class T>\ntypename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type\nmake_unique(std::size_t n) {\n  typedef typename std::remove_extent<T>::type RT;\n  return std::unique_ptr<T>(new RT[n]);\n}\n\nnamespace case_ignore {\n\ninline unsigned char to_lower(int c) {\n  const static unsigned char table[256] = {\n      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,\n      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,\n      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,\n      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,\n      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,\n      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,\n      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,\n      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,\n      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,\n      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,\n      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,\n      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,\n      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,\n      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,\n      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,\n      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,\n      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,\n      255,\n  };\n  return table[(unsigned char)(char)c];\n}\n\ninline bool equal(const std::string &a, const std::string &b) {\n  return a.size() == b.size() &&\n         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {\n           return to_lower(ca) == to_lower(cb);\n         });\n}\n\nstruct equal_to {\n  bool operator()(const std::string &a, const std::string &b) const {\n    return equal(a, b);\n  }\n};\n\nstruct hash {\n  size_t operator()(const std::string &key) const {\n    return hash_core(key.data(), key.size(), 0);\n  }\n\n  size_t hash_core(const char *s, size_t l, size_t h) const {\n    return (l == 0) ? h\n                    : hash_core(s + 1, l - 1,\n                                // Unsets the 6 high bits of h, therefore no\n                                // overflow happens\n                                (((std::numeric_limits<size_t>::max)() >> 6) &\n                                 h * 33) ^\n                                    static_cast<unsigned char>(to_lower(*s)));\n  }\n};\n\n} // namespace case_ignore\n\n// This is based on\n// \"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189\".\n\nstruct scope_exit {\n  explicit scope_exit(std::function<void(void)> &&f)\n      : exit_function(std::move(f)), execute_on_destruction{true} {}\n\n  scope_exit(scope_exit &&rhs) noexcept\n      : exit_function(std::move(rhs.exit_function)),\n        execute_on_destruction{rhs.execute_on_destruction} {\n    rhs.release();\n  }\n\n  ~scope_exit() {\n    if (execute_on_destruction) { this->exit_function(); }\n  }\n\n  void release() { this->execute_on_destruction = false; }\n\nprivate:\n  scope_exit(const scope_exit &) = delete;\n  void operator=(const scope_exit &) = delete;\n  scope_exit &operator=(scope_exit &&) = delete;\n\n  std::function<void(void)> exit_function;\n  bool execute_on_destruction;\n};\n\n} // namespace detail\n\nenum StatusCode {\n  // Information responses\n  Continue_100 = 100,\n  SwitchingProtocol_101 = 101,\n  Processing_102 = 102,\n  EarlyHints_103 = 103,\n\n  // Successful responses\n  OK_200 = 200,\n  Created_201 = 201,\n  Accepted_202 = 202,\n  NonAuthoritativeInformation_203 = 203,\n  NoContent_204 = 204,\n  ResetContent_205 = 205,\n  PartialContent_206 = 206,\n  MultiStatus_207 = 207,\n  AlreadyReported_208 = 208,\n  IMUsed_226 = 226,\n\n  // Redirection messages\n  MultipleChoices_300 = 300,\n  MovedPermanently_301 = 301,\n  Found_302 = 302,\n  SeeOther_303 = 303,\n  NotModified_304 = 304,\n  UseProxy_305 = 305,\n  unused_306 = 306,\n  TemporaryRedirect_307 = 307,\n  PermanentRedirect_308 = 308,\n\n  // Client error responses\n  BadRequest_400 = 400,\n  Unauthorized_401 = 401,\n  PaymentRequired_402 = 402,\n  Forbidden_403 = 403,\n  NotFound_404 = 404,\n  MethodNotAllowed_405 = 405,\n  NotAcceptable_406 = 406,\n  ProxyAuthenticationRequired_407 = 407,\n  RequestTimeout_408 = 408,\n  Conflict_409 = 409,\n  Gone_410 = 410,\n  LengthRequired_411 = 411,\n  PreconditionFailed_412 = 412,\n  PayloadTooLarge_413 = 413,\n  UriTooLong_414 = 414,\n  UnsupportedMediaType_415 = 415,\n  RangeNotSatisfiable_416 = 416,\n  ExpectationFailed_417 = 417,\n  ImATeapot_418 = 418,\n  MisdirectedRequest_421 = 421,\n  UnprocessableContent_422 = 422,\n  Locked_423 = 423,\n  FailedDependency_424 = 424,\n  TooEarly_425 = 425,\n  UpgradeRequired_426 = 426,\n  PreconditionRequired_428 = 428,\n  TooManyRequests_429 = 429,\n  RequestHeaderFieldsTooLarge_431 = 431,\n  UnavailableForLegalReasons_451 = 451,\n\n  // Server error responses\n  InternalServerError_500 = 500,\n  NotImplemented_501 = 501,\n  BadGateway_502 = 502,\n  ServiceUnavailable_503 = 503,\n  GatewayTimeout_504 = 504,\n  HttpVersionNotSupported_505 = 505,\n  VariantAlsoNegotiates_506 = 506,\n  InsufficientStorage_507 = 507,\n  LoopDetected_508 = 508,\n  NotExtended_510 = 510,\n  NetworkAuthenticationRequired_511 = 511,\n};\n\nusing Headers =\n    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,\n                            detail::case_ignore::equal_to>;\n\nusing Params = std::multimap<std::string, std::string>;\nusing Match = std::smatch;\n\nusing Progress = std::function<bool(uint64_t current, uint64_t total)>;\n\nstruct Response;\nusing ResponseHandler = std::function<bool(const Response &response)>;\n\nstruct MultipartFormData {\n  std::string name;\n  std::string content;\n  std::string filename;\n  std::string content_type;\n};\nusing MultipartFormDataItems = std::vector<MultipartFormData>;\nusing MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;\n\nclass DataSink {\npublic:\n  DataSink() : os(&sb_), sb_(*this) {}\n\n  DataSink(const DataSink &) = delete;\n  DataSink &operator=(const DataSink &) = delete;\n  DataSink(DataSink &&) = delete;\n  DataSink &operator=(DataSink &&) = delete;\n\n  std::function<bool(const char *data, size_t data_len)> write;\n  std::function<bool()> is_writable;\n  std::function<void()> done;\n  std::function<void(const Headers &trailer)> done_with_trailer;\n  std::ostream os;\n\nprivate:\n  class data_sink_streambuf final : public std::streambuf {\n  public:\n    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}\n\n  protected:\n    std::streamsize xsputn(const char *s, std::streamsize n) override {\n      sink_.write(s, static_cast<size_t>(n));\n      return n;\n    }\n\n  private:\n    DataSink &sink_;\n  };\n\n  data_sink_streambuf sb_;\n};\n\nusing ContentProvider =\n    std::function<bool(size_t offset, size_t length, DataSink &sink)>;\n\nusing ContentProviderWithoutLength =\n    std::function<bool(size_t offset, DataSink &sink)>;\n\nusing ContentProviderResourceReleaser = std::function<void(bool success)>;\n\nstruct MultipartFormDataProvider {\n  std::string name;\n  ContentProviderWithoutLength provider;\n  std::string filename;\n  std::string content_type;\n};\nusing MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;\n\nusing ContentReceiverWithProgress =\n    std::function<bool(const char *data, size_t data_length, uint64_t offset,\n                       uint64_t total_length)>;\n\nusing ContentReceiver =\n    std::function<bool(const char *data, size_t data_length)>;\n\nusing MultipartContentHeader =\n    std::function<bool(const MultipartFormData &file)>;\n\nclass ContentReader {\npublic:\n  using Reader = std::function<bool(ContentReceiver receiver)>;\n  using MultipartReader = std::function<bool(MultipartContentHeader header,\n                                             ContentReceiver receiver)>;\n\n  ContentReader(Reader reader, MultipartReader multipart_reader)\n      : reader_(std::move(reader)),\n        multipart_reader_(std::move(multipart_reader)) {}\n\n  bool operator()(MultipartContentHeader header,\n                  ContentReceiver receiver) const {\n    return multipart_reader_(std::move(header), std::move(receiver));\n  }\n\n  bool operator()(ContentReceiver receiver) const {\n    return reader_(std::move(receiver));\n  }\n\n  Reader reader_;\n  MultipartReader multipart_reader_;\n};\n\nusing Range = std::pair<ssize_t, ssize_t>;\nusing Ranges = std::vector<Range>;\n\nstruct Request {\n  std::string method;\n  std::string path;\n  Headers headers;\n  std::string body;\n\n  std::string remote_addr;\n  int remote_port = -1;\n  std::string local_addr;\n  int local_port = -1;\n\n  // for server\n  std::string version;\n  std::string target;\n  Params params;\n  MultipartFormDataMap files;\n  Ranges ranges;\n  Match matches;\n  std::unordered_map<std::string, std::string> path_params;\n\n  // for client\n  ResponseHandler response_handler;\n  ContentReceiverWithProgress content_receiver;\n  Progress progress;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  const SSL *ssl = nullptr;\n#endif\n\n  bool has_header(const std::string &key) const;\n  std::string get_header_value(const std::string &key, const char *def = \"\",\n                               size_t id = 0) const;\n  uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,\n                                size_t id = 0) const;\n  size_t get_header_value_count(const std::string &key) const;\n  void set_header(const std::string &key, const std::string &val);\n\n  bool has_param(const std::string &key) const;\n  std::string get_param_value(const std::string &key, size_t id = 0) const;\n  size_t get_param_value_count(const std::string &key) const;\n\n  bool is_multipart_form_data() const;\n\n  bool has_file(const std::string &key) const;\n  MultipartFormData get_file_value(const std::string &key) const;\n  std::vector<MultipartFormData> get_file_values(const std::string &key) const;\n\n  // private members...\n  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;\n  size_t content_length_ = 0;\n  ContentProvider content_provider_;\n  bool is_chunked_content_provider_ = false;\n  size_t authorization_count_ = 0;\n};\n\nstruct Response {\n  std::string version;\n  int status = -1;\n  std::string reason;\n  Headers headers;\n  std::string body;\n  std::string location; // Redirect location\n\n  bool has_header(const std::string &key) const;\n  std::string get_header_value(const std::string &key, const char *def = \"\",\n                               size_t id = 0) const;\n  uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,\n                                size_t id = 0) const;\n  size_t get_header_value_count(const std::string &key) const;\n  void set_header(const std::string &key, const std::string &val);\n\n  void set_redirect(const std::string &url, int status = StatusCode::Found_302);\n  void set_content(const char *s, size_t n, const std::string &content_type);\n  void set_content(const std::string &s, const std::string &content_type);\n  void set_content(std::string &&s, const std::string &content_type);\n\n  void set_content_provider(\n      size_t length, const std::string &content_type, ContentProvider provider,\n      ContentProviderResourceReleaser resource_releaser = nullptr);\n\n  void set_content_provider(\n      const std::string &content_type, ContentProviderWithoutLength provider,\n      ContentProviderResourceReleaser resource_releaser = nullptr);\n\n  void set_chunked_content_provider(\n      const std::string &content_type, ContentProviderWithoutLength provider,\n      ContentProviderResourceReleaser resource_releaser = nullptr);\n\n  void set_file_content(const std::string &path,\n                        const std::string &content_type);\n  void set_file_content(const std::string &path);\n\n  Response() = default;\n  Response(const Response &) = default;\n  Response &operator=(const Response &) = default;\n  Response(Response &&) = default;\n  Response &operator=(Response &&) = default;\n  ~Response() {\n    if (content_provider_resource_releaser_) {\n      content_provider_resource_releaser_(content_provider_success_);\n    }\n  }\n\n  // private members...\n  size_t content_length_ = 0;\n  ContentProvider content_provider_;\n  ContentProviderResourceReleaser content_provider_resource_releaser_;\n  bool is_chunked_content_provider_ = false;\n  bool content_provider_success_ = false;\n  std::string file_content_path_;\n  std::string file_content_content_type_;\n};\n\nclass Stream {\npublic:\n  virtual ~Stream() = default;\n\n  virtual bool is_readable() const = 0;\n  virtual bool is_writable() const = 0;\n\n  virtual ssize_t read(char *ptr, size_t size) = 0;\n  virtual ssize_t write(const char *ptr, size_t size) = 0;\n  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;\n  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;\n  virtual socket_t socket() const = 0;\n\n  ssize_t write(const char *ptr);\n  ssize_t write(const std::string &s);\n};\n\nclass TaskQueue {\npublic:\n  TaskQueue() = default;\n  virtual ~TaskQueue() = default;\n\n  virtual bool enqueue(std::function<void()> fn) = 0;\n  virtual void shutdown() = 0;\n\n  virtual void on_idle() {}\n};\n\nclass ThreadPool final : public TaskQueue {\npublic:\n  explicit ThreadPool(size_t n, size_t mqr = 0)\n      : shutdown_(false), max_queued_requests_(mqr) {\n    while (n) {\n      threads_.emplace_back(worker(*this));\n      n--;\n    }\n  }\n\n  ThreadPool(const ThreadPool &) = delete;\n  ~ThreadPool() override = default;\n\n  bool enqueue(std::function<void()> fn) override {\n    {\n      std::unique_lock<std::mutex> lock(mutex_);\n      if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {\n        return false;\n      }\n      jobs_.push_back(std::move(fn));\n    }\n\n    cond_.notify_one();\n    return true;\n  }\n\n  void shutdown() override {\n    // Stop all worker threads...\n    {\n      std::unique_lock<std::mutex> lock(mutex_);\n      shutdown_ = true;\n    }\n\n    cond_.notify_all();\n\n    // Join...\n    for (auto &t : threads_) {\n      t.join();\n    }\n  }\n\nprivate:\n  struct worker {\n    explicit worker(ThreadPool &pool) : pool_(pool) {}\n\n    void operator()() {\n      for (;;) {\n        std::function<void()> fn;\n        {\n          std::unique_lock<std::mutex> lock(pool_.mutex_);\n\n          pool_.cond_.wait(\n              lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });\n\n          if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }\n\n          fn = pool_.jobs_.front();\n          pool_.jobs_.pop_front();\n        }\n\n        assert(true == static_cast<bool>(fn));\n        fn();\n      }\n\n#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \\\n    !defined(LIBRESSL_VERSION_NUMBER)\n      OPENSSL_thread_stop();\n#endif\n    }\n\n    ThreadPool &pool_;\n  };\n  friend struct worker;\n\n  std::vector<std::thread> threads_;\n  std::list<std::function<void()>> jobs_;\n\n  bool shutdown_;\n  size_t max_queued_requests_ = 0;\n\n  std::condition_variable cond_;\n  std::mutex mutex_;\n};\n\nusing Logger = std::function<void(const Request &, const Response &)>;\n\nusing SocketOptions = std::function<void(socket_t sock)>;\n\nvoid default_socket_options(socket_t sock);\n\nconst char *status_message(int status);\n\nstd::string get_bearer_token_auth(const Request &req);\n\nnamespace detail {\n\nclass MatcherBase {\npublic:\n  virtual ~MatcherBase() = default;\n\n  // Match request path and populate its matches and\n  virtual bool match(Request &request) const = 0;\n};\n\n/**\n * Captures parameters in request path and stores them in Request::path_params\n *\n * Capture name is a substring of a pattern from : to /.\n * The rest of the pattern is matched agains the request path directly\n * Parameters are captured starting from the next character after\n * the end of the last matched static pattern fragment until the next /.\n *\n * Example pattern:\n * \"/path/fragments/:capture/more/fragments/:second_capture\"\n * Static fragments:\n * \"/path/fragments/\", \"more/fragments/\"\n *\n * Given the following request path:\n * \"/path/fragments/:1/more/fragments/:2\"\n * the resulting capture will be\n * {{\"capture\", \"1\"}, {\"second_capture\", \"2\"}}\n */\nclass PathParamsMatcher final : public MatcherBase {\npublic:\n  PathParamsMatcher(const std::string &pattern);\n\n  bool match(Request &request) const override;\n\nprivate:\n  // Treat segment separators as the end of path parameter capture\n  // Does not need to handle query parameters as they are parsed before path\n  // matching\n  static constexpr char separator = '/';\n\n  // Contains static path fragments to match against, excluding the '/' after\n  // path params\n  // Fragments are separated by path params\n  std::vector<std::string> static_fragments_;\n  // Stores the names of the path parameters to be used as keys in the\n  // Request::path_params map\n  std::vector<std::string> param_names_;\n};\n\n/**\n * Performs std::regex_match on request path\n * and stores the result in Request::matches\n *\n * Note that regex match is performed directly on the whole request.\n * This means that wildcard patterns may match multiple path segments with /:\n * \"/begin/(.*)/end\" will match both \"/begin/middle/end\" and \"/begin/1/2/end\".\n */\nclass RegexMatcher final : public MatcherBase {\npublic:\n  RegexMatcher(const std::string &pattern) : regex_(pattern) {}\n\n  bool match(Request &request) const override;\n\nprivate:\n  std::regex regex_;\n};\n\nssize_t write_headers(Stream &strm, const Headers &headers);\n\n} // namespace detail\n\nclass Server {\npublic:\n  using Handler = std::function<void(const Request &, Response &)>;\n\n  using ExceptionHandler =\n      std::function<void(const Request &, Response &, std::exception_ptr ep)>;\n\n  enum class HandlerResponse {\n    Handled,\n    Unhandled,\n  };\n  using HandlerWithResponse =\n      std::function<HandlerResponse(const Request &, Response &)>;\n\n  using HandlerWithContentReader = std::function<void(\n      const Request &, Response &, const ContentReader &content_reader)>;\n\n  using Expect100ContinueHandler =\n      std::function<int(const Request &, Response &)>;\n\n  Server();\n\n  virtual ~Server();\n\n  virtual bool is_valid() const;\n\n  Server &Get(const std::string &pattern, Handler handler);\n  Server &Post(const std::string &pattern, Handler handler);\n  Server &Post(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Put(const std::string &pattern, Handler handler);\n  Server &Put(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Patch(const std::string &pattern, Handler handler);\n  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Delete(const std::string &pattern, Handler handler);\n  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Options(const std::string &pattern, Handler handler);\n\n  bool set_base_dir(const std::string &dir,\n                    const std::string &mount_point = std::string());\n  bool set_mount_point(const std::string &mount_point, const std::string &dir,\n                       Headers headers = Headers());\n  bool remove_mount_point(const std::string &mount_point);\n  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,\n                                                  const std::string &mime);\n  Server &set_default_file_mimetype(const std::string &mime);\n  Server &set_file_request_handler(Handler handler);\n\n  template <class ErrorHandlerFunc>\n  Server &set_error_handler(ErrorHandlerFunc &&handler) {\n    return set_error_handler_core(\n        std::forward<ErrorHandlerFunc>(handler),\n        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});\n  }\n\n  Server &set_exception_handler(ExceptionHandler handler);\n  Server &set_pre_routing_handler(HandlerWithResponse handler);\n  Server &set_post_routing_handler(Handler handler);\n\n  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);\n  Server &set_logger(Logger logger);\n\n  Server &set_address_family(int family);\n  Server &set_tcp_nodelay(bool on);\n  Server &set_ipv6_v6only(bool on);\n  Server &set_socket_options(SocketOptions socket_options);\n\n  Server &set_default_headers(Headers headers);\n  Server &\n  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);\n\n  Server &set_keep_alive_max_count(size_t count);\n  Server &set_keep_alive_timeout(time_t sec);\n\n  Server &set_read_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  Server &set_write_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  Server &set_idle_interval(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);\n\n  Server &set_payload_max_length(size_t length);\n\n  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);\n  int bind_to_any_port(const std::string &host, int socket_flags = 0);\n  bool listen_after_bind();\n\n  bool listen(const std::string &host, int port, int socket_flags = 0);\n\n  bool is_running() const;\n  void wait_until_ready() const;\n  void stop();\n  void decommission();\n\n  std::function<TaskQueue *(void)> new_task_queue;\n\nprotected:\n  bool process_request(Stream &strm, const std::string &remote_addr,\n                       int remote_port, const std::string &local_addr,\n                       int local_port, bool close_connection,\n                       bool &connection_closed,\n                       const std::function<void(Request &)> &setup_request);\n\n  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};\n  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;\n  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;\n  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;\n  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;\n  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;\n  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;\n  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;\n  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;\n  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;\n\nprivate:\n  using Handlers =\n      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;\n  using HandlersForContentReader =\n      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,\n                            HandlerWithContentReader>>;\n\n  static std::unique_ptr<detail::MatcherBase>\n  make_matcher(const std::string &pattern);\n\n  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);\n  Server &set_error_handler_core(Handler handler, std::false_type);\n\n  socket_t create_server_socket(const std::string &host, int port,\n                                int socket_flags,\n                                SocketOptions socket_options) const;\n  int bind_internal(const std::string &host, int port, int socket_flags);\n  bool listen_internal();\n\n  bool routing(Request &req, Response &res, Stream &strm);\n  bool handle_file_request(const Request &req, Response &res,\n                           bool head = false);\n  bool dispatch_request(Request &req, Response &res,\n                        const Handlers &handlers) const;\n  bool dispatch_request_for_content_reader(\n      Request &req, Response &res, ContentReader content_reader,\n      const HandlersForContentReader &handlers) const;\n\n  bool parse_request_line(const char *s, Request &req) const;\n  void apply_ranges(const Request &req, Response &res,\n                    std::string &content_type, std::string &boundary) const;\n  bool write_response(Stream &strm, bool close_connection, Request &req,\n                      Response &res);\n  bool write_response_with_content(Stream &strm, bool close_connection,\n                                   const Request &req, Response &res);\n  bool write_response_core(Stream &strm, bool close_connection,\n                           const Request &req, Response &res,\n                           bool need_apply_ranges);\n  bool write_content_with_provider(Stream &strm, const Request &req,\n                                   Response &res, const std::string &boundary,\n                                   const std::string &content_type);\n  bool read_content(Stream &strm, Request &req, Response &res);\n  bool\n  read_content_with_content_receiver(Stream &strm, Request &req, Response &res,\n                                     ContentReceiver receiver,\n                                     MultipartContentHeader multipart_header,\n                                     ContentReceiver multipart_receiver);\n  bool read_content_core(Stream &strm, Request &req, Response &res,\n                         ContentReceiver receiver,\n                         MultipartContentHeader multipart_header,\n                         ContentReceiver multipart_receiver) const;\n\n  virtual bool process_and_close_socket(socket_t sock);\n\n  std::atomic<bool> is_running_{false};\n  std::atomic<bool> is_decommisioned{false};\n\n  struct MountPointEntry {\n    std::string mount_point;\n    std::string base_dir;\n    Headers headers;\n  };\n  std::vector<MountPointEntry> base_dirs_;\n  std::map<std::string, std::string> file_extension_and_mimetype_map_;\n  std::string default_file_mimetype_ = \"application/octet-stream\";\n  Handler file_request_handler_;\n\n  Handlers get_handlers_;\n  Handlers post_handlers_;\n  HandlersForContentReader post_handlers_for_content_reader_;\n  Handlers put_handlers_;\n  HandlersForContentReader put_handlers_for_content_reader_;\n  Handlers patch_handlers_;\n  HandlersForContentReader patch_handlers_for_content_reader_;\n  Handlers delete_handlers_;\n  HandlersForContentReader delete_handlers_for_content_reader_;\n  Handlers options_handlers_;\n\n  HandlerWithResponse error_handler_;\n  ExceptionHandler exception_handler_;\n  HandlerWithResponse pre_routing_handler_;\n  Handler post_routing_handler_;\n  Expect100ContinueHandler expect_100_continue_handler_;\n\n  Logger logger_;\n\n  int address_family_ = AF_UNSPEC;\n  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;\n  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;\n  SocketOptions socket_options_ = default_socket_options;\n\n  Headers default_headers_;\n  std::function<ssize_t(Stream &, Headers &)> header_writer_ =\n      detail::write_headers;\n};\n\nenum class Error {\n  Success = 0,\n  Unknown,\n  Connection,\n  BindIPAddress,\n  Read,\n  Write,\n  ExceedRedirectCount,\n  Canceled,\n  SSLConnection,\n  SSLLoadingCerts,\n  SSLServerVerification,\n  SSLServerHostnameVerification,\n  UnsupportedMultipartBoundaryChars,\n  Compression,\n  ConnectionTimeout,\n  ProxyConnection,\n\n  // For internal use only\n  SSLPeerCouldBeClosed_,\n};\n\nstd::string to_string(Error error);\n\nstd::ostream &operator<<(std::ostream &os, const Error &obj);\n\nclass Result {\npublic:\n  Result() = default;\n  Result(std::unique_ptr<Response> &&res, Error err,\n         Headers &&request_headers = Headers{})\n      : res_(std::move(res)), err_(err),\n        request_headers_(std::move(request_headers)) {}\n  // Response\n  operator bool() const { return res_ != nullptr; }\n  bool operator==(std::nullptr_t) const { return res_ == nullptr; }\n  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }\n  const Response &value() const { return *res_; }\n  Response &value() { return *res_; }\n  const Response &operator*() const { return *res_; }\n  Response &operator*() { return *res_; }\n  const Response *operator->() const { return res_.get(); }\n  Response *operator->() { return res_.get(); }\n\n  // Error\n  Error error() const { return err_; }\n\n  // Request Headers\n  bool has_request_header(const std::string &key) const;\n  std::string get_request_header_value(const std::string &key,\n                                       const char *def = \"\",\n                                       size_t id = 0) const;\n  uint64_t get_request_header_value_u64(const std::string &key,\n                                        uint64_t def = 0, size_t id = 0) const;\n  size_t get_request_header_value_count(const std::string &key) const;\n\nprivate:\n  std::unique_ptr<Response> res_;\n  Error err_ = Error::Unknown;\n  Headers request_headers_;\n};\n\nclass ClientImpl {\npublic:\n  explicit ClientImpl(const std::string &host);\n\n  explicit ClientImpl(const std::string &host, int port);\n\n  explicit ClientImpl(const std::string &host, int port,\n                      const std::string &client_cert_path,\n                      const std::string &client_key_path);\n\n  virtual ~ClientImpl();\n\n  virtual bool is_valid() const;\n\n  Result Get(const std::string &path);\n  Result Get(const std::string &path, const Headers &headers);\n  Result Get(const std::string &path, Progress progress);\n  Result Get(const std::string &path, const Headers &headers,\n             Progress progress);\n  Result Get(const std::string &path, ContentReceiver content_receiver);\n  Result Get(const std::string &path, const Headers &headers,\n             ContentReceiver content_receiver);\n  Result Get(const std::string &path, ContentReceiver content_receiver,\n             Progress progress);\n  Result Get(const std::string &path, const Headers &headers,\n             ContentReceiver content_receiver, Progress progress);\n  Result Get(const std::string &path, ResponseHandler response_handler,\n             ContentReceiver content_receiver);\n  Result Get(const std::string &path, const Headers &headers,\n             ResponseHandler response_handler,\n             ContentReceiver content_receiver);\n  Result Get(const std::string &path, ResponseHandler response_handler,\n             ContentReceiver content_receiver, Progress progress);\n  Result Get(const std::string &path, const Headers &headers,\n             ResponseHandler response_handler, ContentReceiver content_receiver,\n             Progress progress);\n\n  Result Get(const std::string &path, const Params &params,\n             const Headers &headers, Progress progress = nullptr);\n  Result Get(const std::string &path, const Params &params,\n             const Headers &headers, ContentReceiver content_receiver,\n             Progress progress = nullptr);\n  Result Get(const std::string &path, const Params &params,\n             const Headers &headers, ResponseHandler response_handler,\n             ContentReceiver content_receiver, Progress progress = nullptr);\n\n  Result Head(const std::string &path);\n  Result Head(const std::string &path, const Headers &headers);\n\n  Result Post(const std::string &path);\n  Result Post(const std::string &path, const Headers &headers);\n  Result Post(const std::string &path, const char *body, size_t content_length,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers, const char *body,\n              size_t content_length, const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers, const char *body,\n              size_t content_length, const std::string &content_type,\n              Progress progress);\n  Result Post(const std::string &path, const std::string &body,\n              const std::string &content_type);\n  Result Post(const std::string &path, const std::string &body,\n              const std::string &content_type, Progress progress);\n  Result Post(const std::string &path, const Headers &headers,\n              const std::string &body, const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers,\n              const std::string &body, const std::string &content_type,\n              Progress progress);\n  Result Post(const std::string &path, size_t content_length,\n              ContentProvider content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path,\n              ContentProviderWithoutLength content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers,\n              size_t content_length, ContentProvider content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers,\n              ContentProviderWithoutLength content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Params &params);\n  Result Post(const std::string &path, const Headers &headers,\n              const Params &params);\n  Result Post(const std::string &path, const Headers &headers,\n              const Params &params, Progress progress);\n  Result Post(const std::string &path, const MultipartFormDataItems &items);\n  Result Post(const std::string &path, const Headers &headers,\n              const MultipartFormDataItems &items);\n  Result Post(const std::string &path, const Headers &headers,\n              const MultipartFormDataItems &items, const std::string &boundary);\n  Result Post(const std::string &path, const Headers &headers,\n              const MultipartFormDataItems &items,\n              const MultipartFormDataProviderItems &provider_items);\n\n  Result Put(const std::string &path);\n  Result Put(const std::string &path, const char *body, size_t content_length,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers, const char *body,\n             size_t content_length, const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers, const char *body,\n             size_t content_length, const std::string &content_type,\n             Progress progress);\n  Result Put(const std::string &path, const std::string &body,\n             const std::string &content_type);\n  Result Put(const std::string &path, const std::string &body,\n             const std::string &content_type, Progress progress);\n  Result Put(const std::string &path, const Headers &headers,\n             const std::string &body, const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers,\n             const std::string &body, const std::string &content_type,\n             Progress progress);\n  Result Put(const std::string &path, size_t content_length,\n             ContentProvider content_provider, const std::string &content_type);\n  Result Put(const std::string &path,\n             ContentProviderWithoutLength content_provider,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers,\n             size_t content_length, ContentProvider content_provider,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers,\n             ContentProviderWithoutLength content_provider,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Params &params);\n  Result Put(const std::string &path, const Headers &headers,\n             const Params &params);\n  Result Put(const std::string &path, const Headers &headers,\n             const Params &params, Progress progress);\n  Result Put(const std::string &path, const MultipartFormDataItems &items);\n  Result Put(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items);\n  Result Put(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items, const std::string &boundary);\n  Result Put(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items,\n             const MultipartFormDataProviderItems &provider_items);\n\n  Result Patch(const std::string &path);\n  Result Patch(const std::string &path, const char *body, size_t content_length,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const char *body, size_t content_length,\n               const std::string &content_type, Progress progress);\n  Result Patch(const std::string &path, const Headers &headers,\n               const char *body, size_t content_length,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               const char *body, size_t content_length,\n               const std::string &content_type, Progress progress);\n  Result Patch(const std::string &path, const std::string &body,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const std::string &body,\n               const std::string &content_type, Progress progress);\n  Result Patch(const std::string &path, const Headers &headers,\n               const std::string &body, const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               const std::string &body, const std::string &content_type,\n               Progress progress);\n  Result Patch(const std::string &path, size_t content_length,\n               ContentProvider content_provider,\n               const std::string &content_type);\n  Result Patch(const std::string &path,\n               ContentProviderWithoutLength content_provider,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               size_t content_length, ContentProvider content_provider,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               ContentProviderWithoutLength content_provider,\n               const std::string &content_type);\n\n  Result Delete(const std::string &path);\n  Result Delete(const std::string &path, const Headers &headers);\n  Result Delete(const std::string &path, const char *body,\n                size_t content_length, const std::string &content_type);\n  Result Delete(const std::string &path, const char *body,\n                size_t content_length, const std::string &content_type,\n                Progress progress);\n  Result Delete(const std::string &path, const Headers &headers,\n                const char *body, size_t content_length,\n                const std::string &content_type);\n  Result Delete(const std::string &path, const Headers &headers,\n                const char *body, size_t content_length,\n                const std::string &content_type, Progress progress);\n  Result Delete(const std::string &path, const std::string &body,\n                const std::string &content_type);\n  Result Delete(const std::string &path, const std::string &body,\n                const std::string &content_type, Progress progress);\n  Result Delete(const std::string &path, const Headers &headers,\n                const std::string &body, const std::string &content_type);\n  Result Delete(const std::string &path, const Headers &headers,\n                const std::string &body, const std::string &content_type,\n                Progress progress);\n\n  Result Options(const std::string &path);\n  Result Options(const std::string &path, const Headers &headers);\n\n  bool send(Request &req, Response &res, Error &error);\n  Result send(const Request &req);\n\n  void stop();\n\n  std::string host() const;\n  int port() const;\n\n  size_t is_socket_open() const;\n  socket_t socket() const;\n\n  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);\n\n  void set_default_headers(Headers headers);\n\n  void\n  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);\n\n  void set_address_family(int family);\n  void set_tcp_nodelay(bool on);\n  void set_ipv6_v6only(bool on);\n  void set_socket_options(SocketOptions socket_options);\n\n  void set_connection_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void\n  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_read_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_write_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_basic_auth(const std::string &username, const std::string &password);\n  void set_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_digest_auth(const std::string &username,\n                       const std::string &password);\n#endif\n\n  void set_keep_alive(bool on);\n  void set_follow_location(bool on);\n\n  void set_url_encode(bool on);\n\n  void set_compress(bool on);\n\n  void set_decompress(bool on);\n\n  void set_interface(const std::string &intf);\n\n  void set_proxy(const std::string &host, int port);\n  void set_proxy_basic_auth(const std::string &username,\n                            const std::string &password);\n  void set_proxy_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_proxy_digest_auth(const std::string &username,\n                             const std::string &password);\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_ca_cert_path(const std::string &ca_cert_file_path,\n                        const std::string &ca_cert_dir_path = std::string());\n  void set_ca_cert_store(X509_STORE *ca_cert_store);\n  X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void enable_server_certificate_verification(bool enabled);\n  void enable_server_hostname_verification(bool enabled);\n  void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);\n#endif\n\n  void set_logger(Logger logger);\n\nprotected:\n  struct Socket {\n    socket_t sock = INVALID_SOCKET;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    SSL *ssl = nullptr;\n#endif\n\n    bool is_open() const { return sock != INVALID_SOCKET; }\n  };\n\n  virtual bool create_and_connect_socket(Socket &socket, Error &error);\n\n  // All of:\n  //   shutdown_ssl\n  //   shutdown_socket\n  //   close_socket\n  // should ONLY be called when socket_mutex_ is locked.\n  // Also, shutdown_ssl and close_socket should also NOT be called concurrently\n  // with a DIFFERENT thread sending requests using that socket.\n  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);\n  void shutdown_socket(Socket &socket) const;\n  void close_socket(Socket &socket);\n\n  bool process_request(Stream &strm, Request &req, Response &res,\n                       bool close_connection, Error &error);\n\n  bool write_content_with_provider(Stream &strm, const Request &req,\n                                   Error &error) const;\n\n  void copy_settings(const ClientImpl &rhs);\n\n  // Socket endpoint information\n  const std::string host_;\n  const int port_;\n  const std::string host_and_port_;\n\n  // Current open socket\n  Socket socket_;\n  mutable std::mutex socket_mutex_;\n  std::recursive_mutex request_mutex_;\n\n  // These are all protected under socket_mutex\n  size_t socket_requests_in_flight_ = 0;\n  std::thread::id socket_requests_are_from_thread_ = std::thread::id();\n  bool socket_should_be_closed_when_request_is_done_ = false;\n\n  // Hostname-IP map\n  std::map<std::string, std::string> addr_map_;\n\n  // Default headers\n  Headers default_headers_;\n\n  // Header writer\n  std::function<ssize_t(Stream &, Headers &)> header_writer_ =\n      detail::write_headers;\n\n  // Settings\n  std::string client_cert_path_;\n  std::string client_key_path_;\n\n  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;\n  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;\n  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;\n  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;\n  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;\n  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;\n\n  std::string basic_auth_username_;\n  std::string basic_auth_password_;\n  std::string bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  std::string digest_auth_username_;\n  std::string digest_auth_password_;\n#endif\n\n  bool keep_alive_ = false;\n  bool follow_location_ = false;\n\n  bool url_encode_ = true;\n\n  int address_family_ = AF_UNSPEC;\n  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;\n  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;\n  SocketOptions socket_options_ = nullptr;\n\n  bool compress_ = false;\n  bool decompress_ = true;\n\n  std::string interface_;\n\n  std::string proxy_host_;\n  int proxy_port_ = -1;\n\n  std::string proxy_basic_auth_username_;\n  std::string proxy_basic_auth_password_;\n  std::string proxy_bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  std::string proxy_digest_auth_username_;\n  std::string proxy_digest_auth_password_;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  std::string ca_cert_file_path_;\n  std::string ca_cert_dir_path_;\n\n  X509_STORE *ca_cert_store_ = nullptr;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  bool server_certificate_verification_ = true;\n  bool server_hostname_verification_ = true;\n  std::function<bool(SSL *ssl)> server_certificate_verifier_;\n#endif\n\n  Logger logger_;\n\nprivate:\n  bool send_(Request &req, Response &res, Error &error);\n  Result send_(Request &&req);\n\n  socket_t create_client_socket(Error &error) const;\n  bool read_response_line(Stream &strm, const Request &req,\n                          Response &res) const;\n  bool write_request(Stream &strm, Request &req, bool close_connection,\n                     Error &error);\n  bool redirect(Request &req, Response &res, Error &error);\n  bool handle_request(Stream &strm, Request &req, Response &res,\n                      bool close_connection, Error &error);\n  std::unique_ptr<Response> send_with_content_provider(\n      Request &req, const char *body, size_t content_length,\n      ContentProvider content_provider,\n      ContentProviderWithoutLength content_provider_without_length,\n      const std::string &content_type, Error &error);\n  Result send_with_content_provider(\n      const std::string &method, const std::string &path,\n      const Headers &headers, const char *body, size_t content_length,\n      ContentProvider content_provider,\n      ContentProviderWithoutLength content_provider_without_length,\n      const std::string &content_type, Progress progress);\n  ContentProviderWithoutLength get_multipart_content_provider(\n      const std::string &boundary, const MultipartFormDataItems &items,\n      const MultipartFormDataProviderItems &provider_items) const;\n\n  std::string adjust_host_string(const std::string &host) const;\n\n  virtual bool process_socket(const Socket &socket,\n                              std::function<bool(Stream &strm)> callback);\n  virtual bool is_ssl() const;\n};\n\nclass Client {\npublic:\n  // Universal interface\n  explicit Client(const std::string &scheme_host_port);\n\n  explicit Client(const std::string &scheme_host_port,\n                  const std::string &client_cert_path,\n                  const std::string &client_key_path);\n\n  // HTTP only interface\n  explicit Client(const std::string &host, int port);\n\n  explicit Client(const std::string &host, int port,\n                  const std::string &client_cert_path,\n                  const std::string &client_key_path);\n\n  Client(Client &&) = default;\n  Client &operator=(Client &&) = default;\n\n  ~Client();\n\n  bool is_valid() const;\n\n  Result Get(const std::string &path);\n  Result Get(const std::string &path, const Headers &headers);\n  Result Get(const std::string &path, Progress progress);\n  Result Get(const std::string &path, const Headers &headers,\n             Progress progress);\n  Result Get(const std::string &path, ContentReceiver content_receiver);\n  Result Get(const std::string &path, const Headers &headers,\n             ContentReceiver content_receiver);\n  Result Get(const std::string &path, ContentReceiver content_receiver,\n             Progress progress);\n  Result Get(const std::string &path, const Headers &headers,\n             ContentReceiver content_receiver, Progress progress);\n  Result Get(const std::string &path, ResponseHandler response_handler,\n             ContentReceiver content_receiver);\n  Result Get(const std::string &path, const Headers &headers,\n             ResponseHandler response_handler,\n             ContentReceiver content_receiver);\n  Result Get(const std::string &path, const Headers &headers,\n             ResponseHandler response_handler, ContentReceiver content_receiver,\n             Progress progress);\n  Result Get(const std::string &path, ResponseHandler response_handler,\n             ContentReceiver content_receiver, Progress progress);\n\n  Result Get(const std::string &path, const Params &params,\n             const Headers &headers, Progress progress = nullptr);\n  Result Get(const std::string &path, const Params &params,\n             const Headers &headers, ContentReceiver content_receiver,\n             Progress progress = nullptr);\n  Result Get(const std::string &path, const Params &params,\n             const Headers &headers, ResponseHandler response_handler,\n             ContentReceiver content_receiver, Progress progress = nullptr);\n\n  Result Head(const std::string &path);\n  Result Head(const std::string &path, const Headers &headers);\n\n  Result Post(const std::string &path);\n  Result Post(const std::string &path, const Headers &headers);\n  Result Post(const std::string &path, const char *body, size_t content_length,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers, const char *body,\n              size_t content_length, const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers, const char *body,\n              size_t content_length, const std::string &content_type,\n              Progress progress);\n  Result Post(const std::string &path, const std::string &body,\n              const std::string &content_type);\n  Result Post(const std::string &path, const std::string &body,\n              const std::string &content_type, Progress progress);\n  Result Post(const std::string &path, const Headers &headers,\n              const std::string &body, const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers,\n              const std::string &body, const std::string &content_type,\n              Progress progress);\n  Result Post(const std::string &path, size_t content_length,\n              ContentProvider content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path,\n              ContentProviderWithoutLength content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers,\n              size_t content_length, ContentProvider content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Headers &headers,\n              ContentProviderWithoutLength content_provider,\n              const std::string &content_type);\n  Result Post(const std::string &path, const Params &params);\n  Result Post(const std::string &path, const Headers &headers,\n              const Params &params);\n  Result Post(const std::string &path, const Headers &headers,\n              const Params &params, Progress progress);\n  Result Post(const std::string &path, const MultipartFormDataItems &items);\n  Result Post(const std::string &path, const Headers &headers,\n              const MultipartFormDataItems &items);\n  Result Post(const std::string &path, const Headers &headers,\n              const MultipartFormDataItems &items, const std::string &boundary);\n  Result Post(const std::string &path, const Headers &headers,\n              const MultipartFormDataItems &items,\n              const MultipartFormDataProviderItems &provider_items);\n\n  Result Put(const std::string &path);\n  Result Put(const std::string &path, const char *body, size_t content_length,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers, const char *body,\n             size_t content_length, const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers, const char *body,\n             size_t content_length, const std::string &content_type,\n             Progress progress);\n  Result Put(const std::string &path, const std::string &body,\n             const std::string &content_type);\n  Result Put(const std::string &path, const std::string &body,\n             const std::string &content_type, Progress progress);\n  Result Put(const std::string &path, const Headers &headers,\n             const std::string &body, const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers,\n             const std::string &body, const std::string &content_type,\n             Progress progress);\n  Result Put(const std::string &path, size_t content_length,\n             ContentProvider content_provider, const std::string &content_type);\n  Result Put(const std::string &path,\n             ContentProviderWithoutLength content_provider,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers,\n             size_t content_length, ContentProvider content_provider,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Headers &headers,\n             ContentProviderWithoutLength content_provider,\n             const std::string &content_type);\n  Result Put(const std::string &path, const Params &params);\n  Result Put(const std::string &path, const Headers &headers,\n             const Params &params);\n  Result Put(const std::string &path, const Headers &headers,\n             const Params &params, Progress progress);\n  Result Put(const std::string &path, const MultipartFormDataItems &items);\n  Result Put(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items);\n  Result Put(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items, const std::string &boundary);\n  Result Put(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items,\n             const MultipartFormDataProviderItems &provider_items);\n\n  Result Patch(const std::string &path);\n  Result Patch(const std::string &path, const char *body, size_t content_length,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const char *body, size_t content_length,\n               const std::string &content_type, Progress progress);\n  Result Patch(const std::string &path, const Headers &headers,\n               const char *body, size_t content_length,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               const char *body, size_t content_length,\n               const std::string &content_type, Progress progress);\n  Result Patch(const std::string &path, const std::string &body,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const std::string &body,\n               const std::string &content_type, Progress progress);\n  Result Patch(const std::string &path, const Headers &headers,\n               const std::string &body, const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               const std::string &body, const std::string &content_type,\n               Progress progress);\n  Result Patch(const std::string &path, size_t content_length,\n               ContentProvider content_provider,\n               const std::string &content_type);\n  Result Patch(const std::string &path,\n               ContentProviderWithoutLength content_provider,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               size_t content_length, ContentProvider content_provider,\n               const std::string &content_type);\n  Result Patch(const std::string &path, const Headers &headers,\n               ContentProviderWithoutLength content_provider,\n               const std::string &content_type);\n\n  Result Delete(const std::string &path);\n  Result Delete(const std::string &path, const Headers &headers);\n  Result Delete(const std::string &path, const char *body,\n                size_t content_length, const std::string &content_type);\n  Result Delete(const std::string &path, const char *body,\n                size_t content_length, const std::string &content_type,\n                Progress progress);\n  Result Delete(const std::string &path, const Headers &headers,\n                const char *body, size_t content_length,\n                const std::string &content_type);\n  Result Delete(const std::string &path, const Headers &headers,\n                const char *body, size_t content_length,\n                const std::string &content_type, Progress progress);\n  Result Delete(const std::string &path, const std::string &body,\n                const std::string &content_type);\n  Result Delete(const std::string &path, const std::string &body,\n                const std::string &content_type, Progress progress);\n  Result Delete(const std::string &path, const Headers &headers,\n                const std::string &body, const std::string &content_type);\n  Result Delete(const std::string &path, const Headers &headers,\n                const std::string &body, const std::string &content_type,\n                Progress progress);\n\n  Result Options(const std::string &path);\n  Result Options(const std::string &path, const Headers &headers);\n\n  bool send(Request &req, Response &res, Error &error);\n  Result send(const Request &req);\n\n  void stop();\n\n  std::string host() const;\n  int port() const;\n\n  size_t is_socket_open() const;\n  socket_t socket() const;\n\n  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);\n\n  void set_default_headers(Headers headers);\n\n  void\n  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);\n\n  void set_address_family(int family);\n  void set_tcp_nodelay(bool on);\n  void set_socket_options(SocketOptions socket_options);\n\n  void set_connection_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void\n  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_read_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_write_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_basic_auth(const std::string &username, const std::string &password);\n  void set_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_digest_auth(const std::string &username,\n                       const std::string &password);\n#endif\n\n  void set_keep_alive(bool on);\n  void set_follow_location(bool on);\n\n  void set_url_encode(bool on);\n\n  void set_compress(bool on);\n\n  void set_decompress(bool on);\n\n  void set_interface(const std::string &intf);\n\n  void set_proxy(const std::string &host, int port);\n  void set_proxy_basic_auth(const std::string &username,\n                            const std::string &password);\n  void set_proxy_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_proxy_digest_auth(const std::string &username,\n                             const std::string &password);\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void enable_server_certificate_verification(bool enabled);\n  void enable_server_hostname_verification(bool enabled);\n  void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);\n#endif\n\n  void set_logger(Logger logger);\n\n  // SSL\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_ca_cert_path(const std::string &ca_cert_file_path,\n                        const std::string &ca_cert_dir_path = std::string());\n\n  void set_ca_cert_store(X509_STORE *ca_cert_store);\n  void load_ca_cert_store(const char *ca_cert, std::size_t size);\n\n  long get_openssl_verify_result() const;\n\n  SSL_CTX *ssl_context() const;\n#endif\n\nprivate:\n  std::unique_ptr<ClientImpl> cli_;\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  bool is_ssl_ = false;\n#endif\n};\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nclass SSLServer : public Server {\npublic:\n  SSLServer(const char *cert_path, const char *private_key_path,\n            const char *client_ca_cert_file_path = nullptr,\n            const char *client_ca_cert_dir_path = nullptr,\n            const char *private_key_password = nullptr);\n\n  SSLServer(X509 *cert, EVP_PKEY *private_key,\n            X509_STORE *client_ca_cert_store = nullptr);\n\n  SSLServer(\n      const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);\n\n  ~SSLServer() override;\n\n  bool is_valid() const override;\n\n  SSL_CTX *ssl_context() const;\n\n  void update_certs(X509 *cert, EVP_PKEY *private_key,\n                    X509_STORE *client_ca_cert_store = nullptr);\n\nprivate:\n  bool process_and_close_socket(socket_t sock) override;\n\n  SSL_CTX *ctx_;\n  std::mutex ctx_mutex_;\n};\n\nclass SSLClient final : public ClientImpl {\npublic:\n  explicit SSLClient(const std::string &host);\n\n  explicit SSLClient(const std::string &host, int port);\n\n  explicit SSLClient(const std::string &host, int port,\n                     const std::string &client_cert_path,\n                     const std::string &client_key_path,\n                     const std::string &private_key_password = std::string());\n\n  explicit SSLClient(const std::string &host, int port, X509 *client_cert,\n                     EVP_PKEY *client_key,\n                     const std::string &private_key_password = std::string());\n\n  ~SSLClient() override;\n\n  bool is_valid() const override;\n\n  void set_ca_cert_store(X509_STORE *ca_cert_store);\n  void load_ca_cert_store(const char *ca_cert, std::size_t size);\n\n  long get_openssl_verify_result() const;\n\n  SSL_CTX *ssl_context() const;\n\nprivate:\n  bool create_and_connect_socket(Socket &socket, Error &error) override;\n  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;\n  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);\n\n  bool process_socket(const Socket &socket,\n                      std::function<bool(Stream &strm)> callback) override;\n  bool is_ssl() const override;\n\n  bool connect_with_proxy(Socket &sock, Response &res, bool &success,\n                          Error &error);\n  bool initialize_ssl(Socket &socket, Error &error);\n\n  bool load_certs();\n\n  bool verify_host(X509 *server_cert) const;\n  bool verify_host_with_subject_alt_name(X509 *server_cert) const;\n  bool verify_host_with_common_name(X509 *server_cert) const;\n  bool check_host_name(const char *pattern, size_t pattern_len) const;\n\n  SSL_CTX *ctx_;\n  std::mutex ctx_mutex_;\n  std::once_flag initialize_cert_;\n\n  std::vector<std::string> host_components_;\n\n  long verify_result_ = 0;\n\n  friend class ClientImpl;\n};\n#endif\n\n/*\n * Implementation of template methods.\n */\n\nnamespace detail {\n\ntemplate <typename T, typename U>\ninline void duration_to_sec_and_usec(const T &duration, U callback) {\n  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();\n  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(\n                  duration - std::chrono::seconds(sec))\n                  .count();\n  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));\n}\n\ninline uint64_t get_header_value_u64(const Headers &headers,\n                                     const std::string &key, uint64_t def,\n                                     size_t id) {\n  auto rng = headers.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) {\n    return std::strtoull(it->second.data(), nullptr, 10);\n  }\n  return def;\n}\n\n} // namespace detail\n\ninline uint64_t Request::get_header_value_u64(const std::string &key,\n                                              uint64_t def, size_t id) const {\n  return detail::get_header_value_u64(headers, key, def, id);\n}\n\ninline uint64_t Response::get_header_value_u64(const std::string &key,\n                                               uint64_t def, size_t id) const {\n  return detail::get_header_value_u64(headers, key, def, id);\n}\n\ninline void default_socket_options(socket_t sock) {\n  int opt = 1;\n#ifdef _WIN32\n  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,\n             reinterpret_cast<const char *>(&opt), sizeof(opt));\n  setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,\n             reinterpret_cast<const char *>(&opt), sizeof(opt));\n#else\n#ifdef SO_REUSEPORT\n  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,\n             reinterpret_cast<const void *>(&opt), sizeof(opt));\n#else\n  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,\n             reinterpret_cast<const void *>(&opt), sizeof(opt));\n#endif\n#endif\n}\n\ninline const char *status_message(int status) {\n  switch (status) {\n  case StatusCode::Continue_100: return \"Continue\";\n  case StatusCode::SwitchingProtocol_101: return \"Switching Protocol\";\n  case StatusCode::Processing_102: return \"Processing\";\n  case StatusCode::EarlyHints_103: return \"Early Hints\";\n  case StatusCode::OK_200: return \"OK\";\n  case StatusCode::Created_201: return \"Created\";\n  case StatusCode::Accepted_202: return \"Accepted\";\n  case StatusCode::NonAuthoritativeInformation_203:\n    return \"Non-Authoritative Information\";\n  case StatusCode::NoContent_204: return \"No Content\";\n  case StatusCode::ResetContent_205: return \"Reset Content\";\n  case StatusCode::PartialContent_206: return \"Partial Content\";\n  case StatusCode::MultiStatus_207: return \"Multi-Status\";\n  case StatusCode::AlreadyReported_208: return \"Already Reported\";\n  case StatusCode::IMUsed_226: return \"IM Used\";\n  case StatusCode::MultipleChoices_300: return \"Multiple Choices\";\n  case StatusCode::MovedPermanently_301: return \"Moved Permanently\";\n  case StatusCode::Found_302: return \"Found\";\n  case StatusCode::SeeOther_303: return \"See Other\";\n  case StatusCode::NotModified_304: return \"Not Modified\";\n  case StatusCode::UseProxy_305: return \"Use Proxy\";\n  case StatusCode::unused_306: return \"unused\";\n  case StatusCode::TemporaryRedirect_307: return \"Temporary Redirect\";\n  case StatusCode::PermanentRedirect_308: return \"Permanent Redirect\";\n  case StatusCode::BadRequest_400: return \"Bad Request\";\n  case StatusCode::Unauthorized_401: return \"Unauthorized\";\n  case StatusCode::PaymentRequired_402: return \"Payment Required\";\n  case StatusCode::Forbidden_403: return \"Forbidden\";\n  case StatusCode::NotFound_404: return \"Not Found\";\n  case StatusCode::MethodNotAllowed_405: return \"Method Not Allowed\";\n  case StatusCode::NotAcceptable_406: return \"Not Acceptable\";\n  case StatusCode::ProxyAuthenticationRequired_407:\n    return \"Proxy Authentication Required\";\n  case StatusCode::RequestTimeout_408: return \"Request Timeout\";\n  case StatusCode::Conflict_409: return \"Conflict\";\n  case StatusCode::Gone_410: return \"Gone\";\n  case StatusCode::LengthRequired_411: return \"Length Required\";\n  case StatusCode::PreconditionFailed_412: return \"Precondition Failed\";\n  case StatusCode::PayloadTooLarge_413: return \"Payload Too Large\";\n  case StatusCode::UriTooLong_414: return \"URI Too Long\";\n  case StatusCode::UnsupportedMediaType_415: return \"Unsupported Media Type\";\n  case StatusCode::RangeNotSatisfiable_416: return \"Range Not Satisfiable\";\n  case StatusCode::ExpectationFailed_417: return \"Expectation Failed\";\n  case StatusCode::ImATeapot_418: return \"I'm a teapot\";\n  case StatusCode::MisdirectedRequest_421: return \"Misdirected Request\";\n  case StatusCode::UnprocessableContent_422: return \"Unprocessable Content\";\n  case StatusCode::Locked_423: return \"Locked\";\n  case StatusCode::FailedDependency_424: return \"Failed Dependency\";\n  case StatusCode::TooEarly_425: return \"Too Early\";\n  case StatusCode::UpgradeRequired_426: return \"Upgrade Required\";\n  case StatusCode::PreconditionRequired_428: return \"Precondition Required\";\n  case StatusCode::TooManyRequests_429: return \"Too Many Requests\";\n  case StatusCode::RequestHeaderFieldsTooLarge_431:\n    return \"Request Header Fields Too Large\";\n  case StatusCode::UnavailableForLegalReasons_451:\n    return \"Unavailable For Legal Reasons\";\n  case StatusCode::NotImplemented_501: return \"Not Implemented\";\n  case StatusCode::BadGateway_502: return \"Bad Gateway\";\n  case StatusCode::ServiceUnavailable_503: return \"Service Unavailable\";\n  case StatusCode::GatewayTimeout_504: return \"Gateway Timeout\";\n  case StatusCode::HttpVersionNotSupported_505:\n    return \"HTTP Version Not Supported\";\n  case StatusCode::VariantAlsoNegotiates_506: return \"Variant Also Negotiates\";\n  case StatusCode::InsufficientStorage_507: return \"Insufficient Storage\";\n  case StatusCode::LoopDetected_508: return \"Loop Detected\";\n  case StatusCode::NotExtended_510: return \"Not Extended\";\n  case StatusCode::NetworkAuthenticationRequired_511:\n    return \"Network Authentication Required\";\n\n  default:\n  case StatusCode::InternalServerError_500: return \"Internal Server Error\";\n  }\n}\n\ninline std::string get_bearer_token_auth(const Request &req) {\n  if (req.has_header(\"Authorization\")) {\n    static std::string BearerHeaderPrefix = \"Bearer \";\n    return req.get_header_value(\"Authorization\")\n        .substr(BearerHeaderPrefix.length());\n  }\n  return \"\";\n}\n\ntemplate <class Rep, class Period>\ninline Server &\nServer::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });\n  return *this;\n}\n\ntemplate <class Rep, class Period>\ninline Server &\nServer::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });\n  return *this;\n}\n\ntemplate <class Rep, class Period>\ninline Server &\nServer::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });\n  return *this;\n}\n\ninline std::string to_string(const Error error) {\n  switch (error) {\n  case Error::Success: return \"Success (no error)\";\n  case Error::Connection: return \"Could not establish connection\";\n  case Error::BindIPAddress: return \"Failed to bind IP address\";\n  case Error::Read: return \"Failed to read connection\";\n  case Error::Write: return \"Failed to write connection\";\n  case Error::ExceedRedirectCount: return \"Maximum redirect count exceeded\";\n  case Error::Canceled: return \"Connection handling canceled\";\n  case Error::SSLConnection: return \"SSL connection failed\";\n  case Error::SSLLoadingCerts: return \"SSL certificate loading failed\";\n  case Error::SSLServerVerification: return \"SSL server verification failed\";\n  case Error::SSLServerHostnameVerification:\n    return \"SSL server hostname verification failed\";\n  case Error::UnsupportedMultipartBoundaryChars:\n    return \"Unsupported HTTP multipart boundary characters\";\n  case Error::Compression: return \"Compression failed\";\n  case Error::ConnectionTimeout: return \"Connection timed out\";\n  case Error::ProxyConnection: return \"Proxy connection failed\";\n  case Error::Unknown: return \"Unknown\";\n  default: break;\n  }\n\n  return \"Invalid\";\n}\n\ninline std::ostream &operator<<(std::ostream &os, const Error &obj) {\n  os << to_string(obj);\n  os << \" (\" << static_cast<std::underlying_type<Error>::type>(obj) << ')';\n  return os;\n}\n\ninline uint64_t Result::get_request_header_value_u64(const std::string &key,\n                                                     uint64_t def,\n                                                     size_t id) const {\n  return detail::get_header_value_u64(request_headers_, key, def, id);\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_connection_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {\n    set_connection_timeout(sec, usec);\n  });\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_read_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_write_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });\n}\n\ntemplate <class Rep, class Period>\ninline void Client::set_connection_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_connection_timeout(duration);\n}\n\ntemplate <class Rep, class Period>\ninline void\nClient::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_read_timeout(duration);\n}\n\ntemplate <class Rep, class Period>\ninline void\nClient::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_write_timeout(duration);\n}\n\n/*\n * Forward declarations and types that will be part of the .h file if split into\n * .h + .cc.\n */\n\nstd::string hosted_at(const std::string &hostname);\n\nvoid hosted_at(const std::string &hostname, std::vector<std::string> &addrs);\n\nstd::string append_query_params(const std::string &path, const Params &params);\n\nstd::pair<std::string, std::string> make_range_header(const Ranges &ranges);\n\nstd::pair<std::string, std::string>\nmake_basic_authentication_header(const std::string &username,\n                                 const std::string &password,\n                                 bool is_proxy = false);\n\nnamespace detail {\n\nstruct FileStat {\n  FileStat(const std::string &path);\n  bool is_file() const;\n  bool is_dir() const;\n\nprivate:\n  struct stat st_;\n  int ret_ = -1;\n};\n\nstd::string encode_query_param(const std::string &value);\n\nstd::string decode_url(const std::string &s, bool convert_plus_to_space);\n\nvoid read_file(const std::string &path, std::string &out);\n\nstd::string trim_copy(const std::string &s);\n\nvoid divide(\n    const char *data, std::size_t size, char d,\n    std::function<void(const char *, std::size_t, const char *, std::size_t)>\n        fn);\n\nvoid divide(\n    const std::string &str, char d,\n    std::function<void(const char *, std::size_t, const char *, std::size_t)>\n        fn);\n\nvoid split(const char *b, const char *e, char d,\n           std::function<void(const char *, const char *)> fn);\n\nvoid split(const char *b, const char *e, char d, size_t m,\n           std::function<void(const char *, const char *)> fn);\n\nbool process_client_socket(socket_t sock, time_t read_timeout_sec,\n                           time_t read_timeout_usec, time_t write_timeout_sec,\n                           time_t write_timeout_usec,\n                           std::function<bool(Stream &)> callback);\n\nsocket_t create_client_socket(const std::string &host, const std::string &ip,\n                              int port, int address_family, bool tcp_nodelay,\n                              bool ipv6_v6only, SocketOptions socket_options,\n                              time_t connection_timeout_sec,\n                              time_t connection_timeout_usec,\n                              time_t read_timeout_sec, time_t read_timeout_usec,\n                              time_t write_timeout_sec,\n                              time_t write_timeout_usec,\n                              const std::string &intf, Error &error);\n\nconst char *get_header_value(const Headers &headers, const std::string &key,\n                             const char *def, size_t id);\n\nstd::string params_to_query_str(const Params &params);\n\nvoid parse_query_text(const char *data, std::size_t size, Params &params);\n\nvoid parse_query_text(const std::string &s, Params &params);\n\nbool parse_multipart_boundary(const std::string &content_type,\n                              std::string &boundary);\n\nbool parse_range_header(const std::string &s, Ranges &ranges);\n\nint close_socket(socket_t sock);\n\nssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);\n\nssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);\n\nenum class EncodingType { None = 0, Gzip, Brotli };\n\nEncodingType encoding_type(const Request &req, const Response &res);\n\nclass BufferStream final : public Stream {\npublic:\n  BufferStream() = default;\n  ~BufferStream() override = default;\n\n  bool is_readable() const override;\n  bool is_writable() const override;\n  ssize_t read(char *ptr, size_t size) override;\n  ssize_t write(const char *ptr, size_t size) override;\n  void get_remote_ip_and_port(std::string &ip, int &port) const override;\n  void get_local_ip_and_port(std::string &ip, int &port) const override;\n  socket_t socket() const override;\n\n  const std::string &get_buffer() const;\n\nprivate:\n  std::string buffer;\n  size_t position = 0;\n};\n\nclass compressor {\npublic:\n  virtual ~compressor() = default;\n\n  typedef std::function<bool(const char *data, size_t data_len)> Callback;\n  virtual bool compress(const char *data, size_t data_length, bool last,\n                        Callback callback) = 0;\n};\n\nclass decompressor {\npublic:\n  virtual ~decompressor() = default;\n\n  virtual bool is_valid() const = 0;\n\n  typedef std::function<bool(const char *data, size_t data_len)> Callback;\n  virtual bool decompress(const char *data, size_t data_length,\n                          Callback callback) = 0;\n};\n\nclass nocompressor final : public compressor {\npublic:\n  ~nocompressor() override = default;\n\n  bool compress(const char *data, size_t data_length, bool /*last*/,\n                Callback callback) override;\n};\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\nclass gzip_compressor final : public compressor {\npublic:\n  gzip_compressor();\n  ~gzip_compressor() override;\n\n  bool compress(const char *data, size_t data_length, bool last,\n                Callback callback) override;\n\nprivate:\n  bool is_valid_ = false;\n  z_stream strm_;\n};\n\nclass gzip_decompressor final : public decompressor {\npublic:\n  gzip_decompressor();\n  ~gzip_decompressor() override;\n\n  bool is_valid() const override;\n\n  bool decompress(const char *data, size_t data_length,\n                  Callback callback) override;\n\nprivate:\n  bool is_valid_ = false;\n  z_stream strm_;\n};\n#endif\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\nclass brotli_compressor final : public compressor {\npublic:\n  brotli_compressor();\n  ~brotli_compressor();\n\n  bool compress(const char *data, size_t data_length, bool last,\n                Callback callback) override;\n\nprivate:\n  BrotliEncoderState *state_ = nullptr;\n};\n\nclass brotli_decompressor final : public decompressor {\npublic:\n  brotli_decompressor();\n  ~brotli_decompressor();\n\n  bool is_valid() const override;\n\n  bool decompress(const char *data, size_t data_length,\n                  Callback callback) override;\n\nprivate:\n  BrotliDecoderResult decoder_r;\n  BrotliDecoderState *decoder_s = nullptr;\n};\n#endif\n\n// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`\n// to store data. The call can set memory on stack for performance.\nclass stream_line_reader {\npublic:\n  stream_line_reader(Stream &strm, char *fixed_buffer,\n                     size_t fixed_buffer_size);\n  const char *ptr() const;\n  size_t size() const;\n  bool end_with_crlf() const;\n  bool getline();\n\nprivate:\n  void append(char c);\n\n  Stream &strm_;\n  char *fixed_buffer_;\n  const size_t fixed_buffer_size_;\n  size_t fixed_buffer_used_size_ = 0;\n  std::string glowable_buffer_;\n};\n\nclass mmap {\npublic:\n  mmap(const char *path);\n  ~mmap();\n\n  bool open(const char *path);\n  void close();\n\n  bool is_open() const;\n  size_t size() const;\n  const char *data() const;\n\nprivate:\n#if defined(_WIN32)\n  HANDLE hFile_ = NULL;\n  HANDLE hMapping_ = NULL;\n#else\n  int fd_ = -1;\n#endif\n  size_t size_ = 0;\n  void *addr_ = nullptr;\n  bool is_open_empty_file = false;\n};\n\n} // namespace detail\n\n// ----------------------------------------------------------------------------\n\n/*\n * Implementation that will be part of the .cc file if split into .h + .cc.\n */\n\nnamespace detail {\n\ninline bool is_hex(char c, int &v) {\n  if (0x20 <= c && isdigit(c)) {\n    v = c - '0';\n    return true;\n  } else if ('A' <= c && c <= 'F') {\n    v = c - 'A' + 10;\n    return true;\n  } else if ('a' <= c && c <= 'f') {\n    v = c - 'a' + 10;\n    return true;\n  }\n  return false;\n}\n\ninline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,\n                          int &val) {\n  if (i >= s.size()) { return false; }\n\n  val = 0;\n  for (; cnt; i++, cnt--) {\n    if (!s[i]) { return false; }\n    auto v = 0;\n    if (is_hex(s[i], v)) {\n      val = val * 16 + v;\n    } else {\n      return false;\n    }\n  }\n  return true;\n}\n\ninline std::string from_i_to_hex(size_t n) {\n  static const auto charset = \"0123456789abcdef\";\n  std::string ret;\n  do {\n    ret = charset[n & 15] + ret;\n    n >>= 4;\n  } while (n > 0);\n  return ret;\n}\n\ninline size_t to_utf8(int code, char *buff) {\n  if (code < 0x0080) {\n    buff[0] = static_cast<char>(code & 0x7F);\n    return 1;\n  } else if (code < 0x0800) {\n    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));\n    buff[1] = static_cast<char>(0x80 | (code & 0x3F));\n    return 2;\n  } else if (code < 0xD800) {\n    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));\n    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n    buff[2] = static_cast<char>(0x80 | (code & 0x3F));\n    return 3;\n  } else if (code < 0xE000) { // D800 - DFFF is invalid...\n    return 0;\n  } else if (code < 0x10000) {\n    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));\n    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n    buff[2] = static_cast<char>(0x80 | (code & 0x3F));\n    return 3;\n  } else if (code < 0x110000) {\n    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));\n    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));\n    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n    buff[3] = static_cast<char>(0x80 | (code & 0x3F));\n    return 4;\n  }\n\n  // NOTREACHED\n  return 0;\n}\n\n// NOTE: This code came up with the following stackoverflow post:\n// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c\ninline std::string base64_encode(const std::string &in) {\n  static const auto lookup =\n      \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n  std::string out;\n  out.reserve(in.size());\n\n  auto val = 0;\n  auto valb = -6;\n\n  for (auto c : in) {\n    val = (val << 8) + static_cast<uint8_t>(c);\n    valb += 8;\n    while (valb >= 0) {\n      out.push_back(lookup[(val >> valb) & 0x3F]);\n      valb -= 6;\n    }\n  }\n\n  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }\n\n  while (out.size() % 4) {\n    out.push_back('=');\n  }\n\n  return out;\n}\n\ninline bool is_valid_path(const std::string &path) {\n  size_t level = 0;\n  size_t i = 0;\n\n  // Skip slash\n  while (i < path.size() && path[i] == '/') {\n    i++;\n  }\n\n  while (i < path.size()) {\n    // Read component\n    auto beg = i;\n    while (i < path.size() && path[i] != '/') {\n      if (path[i] == '\\0') {\n        return false;\n      } else if (path[i] == '\\\\') {\n        return false;\n      }\n      i++;\n    }\n\n    auto len = i - beg;\n    assert(len > 0);\n\n    if (!path.compare(beg, len, \".\")) {\n      ;\n    } else if (!path.compare(beg, len, \"..\")) {\n      if (level == 0) { return false; }\n      level--;\n    } else {\n      level++;\n    }\n\n    // Skip slash\n    while (i < path.size() && path[i] == '/') {\n      i++;\n    }\n  }\n\n  return true;\n}\n\ninline FileStat::FileStat(const std::string &path) {\n  ret_ = stat(path.c_str(), &st_);\n}\ninline bool FileStat::is_file() const {\n  return ret_ >= 0 && S_ISREG(st_.st_mode);\n}\ninline bool FileStat::is_dir() const {\n  return ret_ >= 0 && S_ISDIR(st_.st_mode);\n}\n\ninline std::string encode_query_param(const std::string &value) {\n  std::ostringstream escaped;\n  escaped.fill('0');\n  escaped << std::hex;\n\n  for (auto c : value) {\n    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||\n        c == '.' || c == '!' || c == '~' || c == '*' || c == '\\'' || c == '(' ||\n        c == ')') {\n      escaped << c;\n    } else {\n      escaped << std::uppercase;\n      escaped << '%' << std::setw(2)\n              << static_cast<int>(static_cast<unsigned char>(c));\n      escaped << std::nouppercase;\n    }\n  }\n\n  return escaped.str();\n}\n\ninline std::string encode_url(const std::string &s) {\n  std::string result;\n  result.reserve(s.size());\n\n  for (size_t i = 0; s[i]; i++) {\n    switch (s[i]) {\n    case ' ': result += \"%20\"; break;\n    case '+': result += \"%2B\"; break;\n    case '\\r': result += \"%0D\"; break;\n    case '\\n': result += \"%0A\"; break;\n    case '\\'': result += \"%27\"; break;\n    case ',': result += \"%2C\"; break;\n    // case ':': result += \"%3A\"; break; // ok? probably...\n    case ';': result += \"%3B\"; break;\n    default:\n      auto c = static_cast<uint8_t>(s[i]);\n      if (c >= 0x80) {\n        result += '%';\n        char hex[4];\n        auto len = snprintf(hex, sizeof(hex) - 1, \"%02X\", c);\n        assert(len == 2);\n        result.append(hex, static_cast<size_t>(len));\n      } else {\n        result += s[i];\n      }\n      break;\n    }\n  }\n\n  return result;\n}\n\ninline std::string decode_url(const std::string &s,\n                              bool convert_plus_to_space) {\n  std::string result;\n\n  for (size_t i = 0; i < s.size(); i++) {\n    if (s[i] == '%' && i + 1 < s.size()) {\n      if (s[i + 1] == 'u') {\n        auto val = 0;\n        if (from_hex_to_i(s, i + 2, 4, val)) {\n          // 4 digits Unicode codes\n          char buff[4];\n          size_t len = to_utf8(val, buff);\n          if (len > 0) { result.append(buff, len); }\n          i += 5; // 'u0000'\n        } else {\n          result += s[i];\n        }\n      } else {\n        auto val = 0;\n        if (from_hex_to_i(s, i + 1, 2, val)) {\n          // 2 digits hex codes\n          result += static_cast<char>(val);\n          i += 2; // '00'\n        } else {\n          result += s[i];\n        }\n      }\n    } else if (convert_plus_to_space && s[i] == '+') {\n      result += ' ';\n    } else {\n      result += s[i];\n    }\n  }\n\n  return result;\n}\n\ninline void read_file(const std::string &path, std::string &out) {\n  std::ifstream fs(path, std::ios_base::binary);\n  fs.seekg(0, std::ios_base::end);\n  auto size = fs.tellg();\n  fs.seekg(0);\n  out.resize(static_cast<size_t>(size));\n  fs.read(&out[0], static_cast<std::streamsize>(size));\n}\n\ninline std::string file_extension(const std::string &path) {\n  std::smatch m;\n  static auto re = std::regex(\"\\\\.([a-zA-Z0-9]+)$\");\n  if (std::regex_search(path, m, re)) { return m[1].str(); }\n  return std::string();\n}\n\ninline bool is_space_or_tab(char c) { return c == ' ' || c == '\\t'; }\n\ninline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,\n                                      size_t right) {\n  while (b + left < e && is_space_or_tab(b[left])) {\n    left++;\n  }\n  while (right > 0 && is_space_or_tab(b[right - 1])) {\n    right--;\n  }\n  return std::make_pair(left, right);\n}\n\ninline std::string trim_copy(const std::string &s) {\n  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());\n  return s.substr(r.first, r.second - r.first);\n}\n\ninline std::string trim_double_quotes_copy(const std::string &s) {\n  if (s.length() >= 2 && s.front() == '\"' && s.back() == '\"') {\n    return s.substr(1, s.size() - 2);\n  }\n  return s;\n}\n\ninline void\ndivide(const char *data, std::size_t size, char d,\n       std::function<void(const char *, std::size_t, const char *, std::size_t)>\n           fn) {\n  const auto it = std::find(data, data + size, d);\n  const auto found = static_cast<std::size_t>(it != data + size);\n  const auto lhs_data = data;\n  const auto lhs_size = static_cast<std::size_t>(it - data);\n  const auto rhs_data = it + found;\n  const auto rhs_size = size - lhs_size - found;\n\n  fn(lhs_data, lhs_size, rhs_data, rhs_size);\n}\n\ninline void\ndivide(const std::string &str, char d,\n       std::function<void(const char *, std::size_t, const char *, std::size_t)>\n           fn) {\n  divide(str.data(), str.size(), d, std::move(fn));\n}\n\ninline void split(const char *b, const char *e, char d,\n                  std::function<void(const char *, const char *)> fn) {\n  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));\n}\n\ninline void split(const char *b, const char *e, char d, size_t m,\n                  std::function<void(const char *, const char *)> fn) {\n  size_t i = 0;\n  size_t beg = 0;\n  size_t count = 1;\n\n  while (e ? (b + i < e) : (b[i] != '\\0')) {\n    if (b[i] == d && count < m) {\n      auto r = trim(b, e, beg, i);\n      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }\n      beg = i + 1;\n      count++;\n    }\n    i++;\n  }\n\n  if (i) {\n    auto r = trim(b, e, beg, i);\n    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }\n  }\n}\n\ninline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,\n                                              size_t fixed_buffer_size)\n    : strm_(strm), fixed_buffer_(fixed_buffer),\n      fixed_buffer_size_(fixed_buffer_size) {}\n\ninline const char *stream_line_reader::ptr() const {\n  if (glowable_buffer_.empty()) {\n    return fixed_buffer_;\n  } else {\n    return glowable_buffer_.data();\n  }\n}\n\ninline size_t stream_line_reader::size() const {\n  if (glowable_buffer_.empty()) {\n    return fixed_buffer_used_size_;\n  } else {\n    return glowable_buffer_.size();\n  }\n}\n\ninline bool stream_line_reader::end_with_crlf() const {\n  auto end = ptr() + size();\n  return size() >= 2 && end[-2] == '\\r' && end[-1] == '\\n';\n}\n\ninline bool stream_line_reader::getline() {\n  fixed_buffer_used_size_ = 0;\n  glowable_buffer_.clear();\n\n#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n  char prev_byte = 0;\n#endif\n\n  for (size_t i = 0;; i++) {\n    char byte;\n    auto n = strm_.read(&byte, 1);\n\n    if (n < 0) {\n      return false;\n    } else if (n == 0) {\n      if (i == 0) {\n        return false;\n      } else {\n        break;\n      }\n    }\n\n    append(byte);\n\n#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n    if (byte == '\\n') { break; }\n#else\n    if (prev_byte == '\\r' && byte == '\\n') { break; }\n    prev_byte = byte;\n#endif\n  }\n\n  return true;\n}\n\ninline void stream_line_reader::append(char c) {\n  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {\n    fixed_buffer_[fixed_buffer_used_size_++] = c;\n    fixed_buffer_[fixed_buffer_used_size_] = '\\0';\n  } else {\n    if (glowable_buffer_.empty()) {\n      assert(fixed_buffer_[fixed_buffer_used_size_] == '\\0');\n      glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);\n    }\n    glowable_buffer_ += c;\n  }\n}\n\ninline mmap::mmap(const char *path) { open(path); }\n\ninline mmap::~mmap() { close(); }\n\ninline bool mmap::open(const char *path) {\n  close();\n\n#if defined(_WIN32)\n  std::wstring wpath;\n  for (size_t i = 0; i < strlen(path); i++) {\n    wpath += path[i];\n  }\n\n#if _WIN32_WINNT >= _WIN32_WINNT_WIN8\n  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,\n                         OPEN_EXISTING, NULL);\n#else\n  hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,\n                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n#endif\n\n  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }\n\n  LARGE_INTEGER size{};\n  if (!::GetFileSizeEx(hFile_, &size)) { return false; }\n  // If the following line doesn't compile due to QuadPart, update Windows SDK.\n  // See:\n  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721\n  if (static_cast<ULONGLONG>(size.QuadPart) >\n      (std::numeric_limits<decltype(size_)>::max)()) {\n    // `size_t` might be 32-bits, on 32-bits Windows.\n    return false;\n  }\n  size_ = static_cast<size_t>(size.QuadPart);\n\n#if _WIN32_WINNT >= _WIN32_WINNT_WIN8\n  hMapping_ =\n      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);\n#else\n  hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);\n#endif\n\n  // Special treatment for an empty file...\n  if (hMapping_ == NULL && size_ == 0) {\n    close();\n    is_open_empty_file = true;\n    return true;\n  }\n\n  if (hMapping_ == NULL) {\n    close();\n    return false;\n  }\n\n#if _WIN32_WINNT >= _WIN32_WINNT_WIN8\n  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);\n#else\n  addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);\n#endif\n\n  if (addr_ == nullptr) {\n    close();\n    return false;\n  }\n#else\n  fd_ = ::open(path, O_RDONLY);\n  if (fd_ == -1) { return false; }\n\n  struct stat sb;\n  if (fstat(fd_, &sb) == -1) {\n    close();\n    return false;\n  }\n  size_ = static_cast<size_t>(sb.st_size);\n\n  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);\n\n  // Special treatment for an empty file...\n  if (addr_ == MAP_FAILED && size_ == 0) {\n    close();\n    is_open_empty_file = true;\n    return false;\n  }\n#endif\n\n  return true;\n}\n\ninline bool mmap::is_open() const {\n  return is_open_empty_file ? true : addr_ != nullptr;\n}\n\ninline size_t mmap::size() const { return size_; }\n\ninline const char *mmap::data() const {\n  return is_open_empty_file ? \"\" : static_cast<const char *>(addr_);\n}\n\ninline void mmap::close() {\n#if defined(_WIN32)\n  if (addr_) {\n    ::UnmapViewOfFile(addr_);\n    addr_ = nullptr;\n  }\n\n  if (hMapping_) {\n    ::CloseHandle(hMapping_);\n    hMapping_ = NULL;\n  }\n\n  if (hFile_ != INVALID_HANDLE_VALUE) {\n    ::CloseHandle(hFile_);\n    hFile_ = INVALID_HANDLE_VALUE;\n  }\n\n  is_open_empty_file = false;\n#else\n  if (addr_ != nullptr) {\n    munmap(addr_, size_);\n    addr_ = nullptr;\n  }\n\n  if (fd_ != -1) {\n    ::close(fd_);\n    fd_ = -1;\n  }\n#endif\n  size_ = 0;\n}\ninline int close_socket(socket_t sock) {\n#ifdef _WIN32\n  return closesocket(sock);\n#else\n  return close(sock);\n#endif\n}\n\ntemplate <typename T> inline ssize_t handle_EINTR(T fn) {\n  ssize_t res = 0;\n  while (true) {\n    res = fn();\n    if (res < 0 && errno == EINTR) {\n      std::this_thread::sleep_for(std::chrono::microseconds{1});\n      continue;\n    }\n    break;\n  }\n  return res;\n}\n\ninline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {\n  return handle_EINTR([&]() {\n    return recv(sock,\n#ifdef _WIN32\n                static_cast<char *>(ptr), static_cast<int>(size),\n#else\n                ptr, size,\n#endif\n                flags);\n  });\n}\n\ninline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,\n                           int flags) {\n  return handle_EINTR([&]() {\n    return send(sock,\n#ifdef _WIN32\n                static_cast<const char *>(ptr), static_cast<int>(size),\n#else\n                ptr, size,\n#endif\n                flags);\n  });\n}\n\ninline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {\n#ifdef CPPHTTPLIB_USE_POLL\n  struct pollfd pfd_read;\n  pfd_read.fd = sock;\n  pfd_read.events = POLLIN;\n\n  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);\n\n  return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });\n#else\n#ifndef _WIN32\n  if (sock >= FD_SETSIZE) { return -1; }\n#endif\n\n  fd_set fds;\n  FD_ZERO(&fds);\n  FD_SET(sock, &fds);\n\n  timeval tv;\n  tv.tv_sec = static_cast<long>(sec);\n  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n  return handle_EINTR([&]() {\n    return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);\n  });\n#endif\n}\n\ninline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {\n#ifdef CPPHTTPLIB_USE_POLL\n  struct pollfd pfd_read;\n  pfd_read.fd = sock;\n  pfd_read.events = POLLOUT;\n\n  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);\n\n  return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });\n#else\n#ifndef _WIN32\n  if (sock >= FD_SETSIZE) { return -1; }\n#endif\n\n  fd_set fds;\n  FD_ZERO(&fds);\n  FD_SET(sock, &fds);\n\n  timeval tv;\n  tv.tv_sec = static_cast<long>(sec);\n  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n  return handle_EINTR([&]() {\n    return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);\n  });\n#endif\n}\n\ninline Error wait_until_socket_is_ready(socket_t sock, time_t sec,\n                                        time_t usec) {\n#ifdef CPPHTTPLIB_USE_POLL\n  struct pollfd pfd_read;\n  pfd_read.fd = sock;\n  pfd_read.events = POLLIN | POLLOUT;\n\n  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);\n\n  auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });\n\n  if (poll_res == 0) { return Error::ConnectionTimeout; }\n\n  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {\n    auto error = 0;\n    socklen_t len = sizeof(error);\n    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,\n                          reinterpret_cast<char *>(&error), &len);\n    auto successful = res >= 0 && !error;\n    return successful ? Error::Success : Error::Connection;\n  }\n\n  return Error::Connection;\n#else\n#ifndef _WIN32\n  if (sock >= FD_SETSIZE) { return Error::Connection; }\n#endif\n\n  fd_set fdsr;\n  FD_ZERO(&fdsr);\n  FD_SET(sock, &fdsr);\n\n  auto fdsw = fdsr;\n  auto fdse = fdsr;\n\n  timeval tv;\n  tv.tv_sec = static_cast<long>(sec);\n  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n  auto ret = handle_EINTR([&]() {\n    return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);\n  });\n\n  if (ret == 0) { return Error::ConnectionTimeout; }\n\n  if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {\n    auto error = 0;\n    socklen_t len = sizeof(error);\n    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,\n                          reinterpret_cast<char *>(&error), &len);\n    auto successful = res >= 0 && !error;\n    return successful ? Error::Success : Error::Connection;\n  }\n  return Error::Connection;\n#endif\n}\n\ninline bool is_socket_alive(socket_t sock) {\n  const auto val = detail::select_read(sock, 0, 0);\n  if (val == 0) {\n    return true;\n  } else if (val < 0 && errno == EBADF) {\n    return false;\n  }\n  char buf[1];\n  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;\n}\n\nclass SocketStream final : public Stream {\npublic:\n  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,\n               time_t write_timeout_sec, time_t write_timeout_usec);\n  ~SocketStream() override;\n\n  bool is_readable() const override;\n  bool is_writable() const override;\n  ssize_t read(char *ptr, size_t size) override;\n  ssize_t write(const char *ptr, size_t size) override;\n  void get_remote_ip_and_port(std::string &ip, int &port) const override;\n  void get_local_ip_and_port(std::string &ip, int &port) const override;\n  socket_t socket() const override;\n\nprivate:\n  socket_t sock_;\n  time_t read_timeout_sec_;\n  time_t read_timeout_usec_;\n  time_t write_timeout_sec_;\n  time_t write_timeout_usec_;\n\n  std::vector<char> read_buff_;\n  size_t read_buff_off_ = 0;\n  size_t read_buff_content_size_ = 0;\n\n  static const size_t read_buff_size_ = 1024l * 4;\n};\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nclass SSLSocketStream final : public Stream {\npublic:\n  SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,\n                  time_t read_timeout_usec, time_t write_timeout_sec,\n                  time_t write_timeout_usec);\n  ~SSLSocketStream() override;\n\n  bool is_readable() const override;\n  bool is_writable() const override;\n  ssize_t read(char *ptr, size_t size) override;\n  ssize_t write(const char *ptr, size_t size) override;\n  void get_remote_ip_and_port(std::string &ip, int &port) const override;\n  void get_local_ip_and_port(std::string &ip, int &port) const override;\n  socket_t socket() const override;\n\nprivate:\n  socket_t sock_;\n  SSL *ssl_;\n  time_t read_timeout_sec_;\n  time_t read_timeout_usec_;\n  time_t write_timeout_sec_;\n  time_t write_timeout_usec_;\n};\n#endif\n\ninline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,\n                       time_t keep_alive_timeout_sec) {\n  using namespace std::chrono;\n\n  const auto interval_usec =\n      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;\n\n  // Avoid expensive `steady_clock::now()` call for the first time\n  if (select_read(sock, 0, interval_usec) > 0) { return true; }\n\n  const auto start = steady_clock::now() - microseconds{interval_usec};\n  const auto timeout = seconds{keep_alive_timeout_sec};\n\n  while (true) {\n    if (svr_sock == INVALID_SOCKET) {\n      break; // Server socket is closed\n    }\n\n    auto val = select_read(sock, 0, interval_usec);\n    if (val < 0) {\n      break; // Ssocket error\n    } else if (val == 0) {\n      if (steady_clock::now() - start > timeout) {\n        break; // Timeout\n      }\n    } else {\n      return true; // Ready for read\n    }\n\n    std::this_thread::sleep_for(microseconds{interval_usec});\n  }\n\n  return false;\n}\n\ntemplate <typename T>\ninline bool\nprocess_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,\n                           size_t keep_alive_max_count,\n                           time_t keep_alive_timeout_sec, T callback) {\n  assert(keep_alive_max_count > 0);\n  auto ret = false;\n  auto count = keep_alive_max_count;\n  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {\n    auto close_connection = count == 1;\n    auto connection_closed = false;\n    ret = callback(close_connection, connection_closed);\n    if (!ret || connection_closed) { break; }\n    count--;\n  }\n  return ret;\n}\n\ntemplate <typename T>\ninline bool\nprocess_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,\n                      size_t keep_alive_max_count,\n                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,\n                      time_t read_timeout_usec, time_t write_timeout_sec,\n                      time_t write_timeout_usec, T callback) {\n  return process_server_socket_core(\n      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,\n      [&](bool close_connection, bool &connection_closed) {\n        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,\n                          write_timeout_sec, write_timeout_usec);\n        return callback(strm, close_connection, connection_closed);\n      });\n}\n\ninline bool process_client_socket(socket_t sock, time_t read_timeout_sec,\n                                  time_t read_timeout_usec,\n                                  time_t write_timeout_sec,\n                                  time_t write_timeout_usec,\n                                  std::function<bool(Stream &)> callback) {\n  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,\n                    write_timeout_sec, write_timeout_usec);\n  return callback(strm);\n}\n\ninline int shutdown_socket(socket_t sock) {\n#ifdef _WIN32\n  return shutdown(sock, SD_BOTH);\n#else\n  return shutdown(sock, SHUT_RDWR);\n#endif\n}\n\ninline std::string escape_abstract_namespace_unix_domain(const std::string &s) {\n  if (s.size() > 1 && s[0] == '\\0') {\n    auto ret = s;\n    ret[0] = '@';\n    return ret;\n  }\n  return s;\n}\n\ninline std::string\nunescape_abstract_namespace_unix_domain(const std::string &s) {\n  if (s.size() > 1 && s[0] == '@') {\n    auto ret = s;\n    ret[0] = '\\0';\n    return ret;\n  }\n  return s;\n}\n\ntemplate <typename BindOrConnect>\nsocket_t create_socket(const std::string &host, const std::string &ip, int port,\n                       int address_family, int socket_flags, bool tcp_nodelay,\n                       bool ipv6_v6only, SocketOptions socket_options,\n                       BindOrConnect bind_or_connect) {\n  // Get address info\n  const char *node = nullptr;\n  struct addrinfo hints;\n  struct addrinfo *result;\n\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = IPPROTO_IP;\n\n  if (!ip.empty()) {\n    node = ip.c_str();\n    // Ask getaddrinfo to convert IP in c-string to address\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_flags = AI_NUMERICHOST;\n  } else {\n    if (!host.empty()) { node = host.c_str(); }\n    hints.ai_family = address_family;\n    hints.ai_flags = socket_flags;\n  }\n\n#ifndef _WIN32\n  if (hints.ai_family == AF_UNIX) {\n    const auto addrlen = host.length();\n    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }\n\n#ifdef SOCK_CLOEXEC\n    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,\n                       hints.ai_protocol);\n#else\n    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);\n#endif\n\n    if (sock != INVALID_SOCKET) {\n      sockaddr_un addr{};\n      addr.sun_family = AF_UNIX;\n\n      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);\n      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);\n\n      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);\n      hints.ai_addrlen = static_cast<socklen_t>(\n          sizeof(addr) - sizeof(addr.sun_path) + addrlen);\n\n#ifndef SOCK_CLOEXEC\n      fcntl(sock, F_SETFD, FD_CLOEXEC);\n#endif\n\n      if (socket_options) { socket_options(sock); }\n\n      bool dummy;\n      if (!bind_or_connect(sock, hints, dummy)) {\n        close_socket(sock);\n        sock = INVALID_SOCKET;\n      }\n    }\n    return sock;\n  }\n#endif\n\n  auto service = std::to_string(port);\n\n  if (getaddrinfo(node, service.c_str(), &hints, &result)) {\n#if defined __linux__ && !defined __ANDROID__\n    res_init();\n#endif\n    return INVALID_SOCKET;\n  }\n  auto se = detail::scope_exit([&] { freeaddrinfo(result); });\n\n  for (auto rp = result; rp; rp = rp->ai_next) {\n    // Create a socket\n#ifdef _WIN32\n    auto sock =\n        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,\n                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);\n    /**\n     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1\n     * and above the socket creation fails on older Windows Systems.\n     *\n     * Let's try to create a socket the old way in this case.\n     *\n     * Reference:\n     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa\n     *\n     * WSA_FLAG_NO_HANDLE_INHERIT:\n     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with\n     * SP1, and later\n     *\n     */\n    if (sock == INVALID_SOCKET) {\n      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    }\n#else\n\n#ifdef SOCK_CLOEXEC\n    auto sock =\n        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);\n#else\n    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n#endif\n\n#endif\n    if (sock == INVALID_SOCKET) { continue; }\n\n#if !defined _WIN32 && !defined SOCK_CLOEXEC\n    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {\n      close_socket(sock);\n      continue;\n    }\n#endif\n\n    if (tcp_nodelay) {\n      auto opt = 1;\n#ifdef _WIN32\n      setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,\n                 reinterpret_cast<const char *>(&opt), sizeof(opt));\n#else\n      setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,\n                 reinterpret_cast<const void *>(&opt), sizeof(opt));\n#endif\n    }\n\n    if (rp->ai_family == AF_INET6) {\n      auto opt = ipv6_v6only ? 1 : 0;\n#ifdef _WIN32\n      setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,\n                 reinterpret_cast<const char *>(&opt), sizeof(opt));\n#else\n      setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,\n                 reinterpret_cast<const void *>(&opt), sizeof(opt));\n#endif\n    }\n\n    if (socket_options) { socket_options(sock); }\n\n    // bind or connect\n    auto quit = false;\n    if (bind_or_connect(sock, *rp, quit)) { return sock; }\n\n    close_socket(sock);\n\n    if (quit) { break; }\n  }\n\n  return INVALID_SOCKET;\n}\n\ninline void set_nonblocking(socket_t sock, bool nonblocking) {\n#ifdef _WIN32\n  auto flags = nonblocking ? 1UL : 0UL;\n  ioctlsocket(sock, FIONBIO, &flags);\n#else\n  auto flags = fcntl(sock, F_GETFL, 0);\n  fcntl(sock, F_SETFL,\n        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));\n#endif\n}\n\ninline bool is_connection_error() {\n#ifdef _WIN32\n  return WSAGetLastError() != WSAEWOULDBLOCK;\n#else\n  return errno != EINPROGRESS;\n#endif\n}\n\ninline bool bind_ip_address(socket_t sock, const std::string &host) {\n  struct addrinfo hints;\n  struct addrinfo *result;\n\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = 0;\n\n  if (getaddrinfo(host.c_str(), \"0\", &hints, &result)) { return false; }\n  auto se = detail::scope_exit([&] { freeaddrinfo(result); });\n\n  auto ret = false;\n  for (auto rp = result; rp; rp = rp->ai_next) {\n    const auto &ai = *rp;\n    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {\n      ret = true;\n      break;\n    }\n  }\n\n  return ret;\n}\n\n#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__\n#define USE_IF2IP\n#endif\n\n#ifdef USE_IF2IP\ninline std::string if2ip(int address_family, const std::string &ifn) {\n  struct ifaddrs *ifap;\n  getifaddrs(&ifap);\n  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });\n\n  std::string addr_candidate;\n  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {\n    if (ifa->ifa_addr && ifn == ifa->ifa_name &&\n        (AF_UNSPEC == address_family ||\n         ifa->ifa_addr->sa_family == address_family)) {\n      if (ifa->ifa_addr->sa_family == AF_INET) {\n        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);\n        char buf[INET_ADDRSTRLEN];\n        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {\n          return std::string(buf, INET_ADDRSTRLEN);\n        }\n      } else if (ifa->ifa_addr->sa_family == AF_INET6) {\n        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);\n        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {\n          char buf[INET6_ADDRSTRLEN] = {};\n          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {\n            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL\n            auto s6_addr_head = sa->sin6_addr.s6_addr[0];\n            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {\n              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);\n            } else {\n              return std::string(buf, INET6_ADDRSTRLEN);\n            }\n          }\n        }\n      }\n    }\n  }\n  return addr_candidate;\n}\n#endif\n\ninline socket_t create_client_socket(\n    const std::string &host, const std::string &ip, int port,\n    int address_family, bool tcp_nodelay, bool ipv6_v6only,\n    SocketOptions socket_options, time_t connection_timeout_sec,\n    time_t connection_timeout_usec, time_t read_timeout_sec,\n    time_t read_timeout_usec, time_t write_timeout_sec,\n    time_t write_timeout_usec, const std::string &intf, Error &error) {\n  auto sock = create_socket(\n      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,\n      std::move(socket_options),\n      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {\n        if (!intf.empty()) {\n#ifdef USE_IF2IP\n          auto ip_from_if = if2ip(address_family, intf);\n          if (ip_from_if.empty()) { ip_from_if = intf; }\n          if (!bind_ip_address(sock2, ip_from_if)) {\n            error = Error::BindIPAddress;\n            return false;\n          }\n#endif\n        }\n\n        set_nonblocking(sock2, true);\n\n        auto ret =\n            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));\n\n        if (ret < 0) {\n          if (is_connection_error()) {\n            error = Error::Connection;\n            return false;\n          }\n          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,\n                                             connection_timeout_usec);\n          if (error != Error::Success) {\n            if (error == Error::ConnectionTimeout) { quit = true; }\n            return false;\n          }\n        }\n\n        set_nonblocking(sock2, false);\n\n        {\n#ifdef _WIN32\n          auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 +\n                                               read_timeout_usec / 1000);\n          setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,\n                     reinterpret_cast<const char *>(&timeout), sizeof(timeout));\n#else\n          timeval tv;\n          tv.tv_sec = static_cast<long>(read_timeout_sec);\n          tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);\n          setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,\n                     reinterpret_cast<const void *>(&tv), sizeof(tv));\n#endif\n        }\n        {\n\n#ifdef _WIN32\n          auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 +\n                                               write_timeout_usec / 1000);\n          setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,\n                     reinterpret_cast<const char *>(&timeout), sizeof(timeout));\n#else\n          timeval tv;\n          tv.tv_sec = static_cast<long>(write_timeout_sec);\n          tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);\n          setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,\n                     reinterpret_cast<const void *>(&tv), sizeof(tv));\n#endif\n        }\n\n        error = Error::Success;\n        return true;\n      });\n\n  if (sock != INVALID_SOCKET) {\n    error = Error::Success;\n  } else {\n    if (error == Error::Success) { error = Error::Connection; }\n  }\n\n  return sock;\n}\n\ninline bool get_ip_and_port(const struct sockaddr_storage &addr,\n                            socklen_t addr_len, std::string &ip, int &port) {\n  if (addr.ss_family == AF_INET) {\n    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);\n  } else if (addr.ss_family == AF_INET6) {\n    port =\n        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);\n  } else {\n    return false;\n  }\n\n  std::array<char, NI_MAXHOST> ipstr{};\n  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,\n                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,\n                  0, NI_NUMERICHOST)) {\n    return false;\n  }\n\n  ip = ipstr.data();\n  return true;\n}\n\ninline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {\n  struct sockaddr_storage addr;\n  socklen_t addr_len = sizeof(addr);\n  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),\n                   &addr_len)) {\n    get_ip_and_port(addr, addr_len, ip, port);\n  }\n}\n\ninline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {\n  struct sockaddr_storage addr;\n  socklen_t addr_len = sizeof(addr);\n\n  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),\n                   &addr_len)) {\n#ifndef _WIN32\n    if (addr.ss_family == AF_UNIX) {\n#if defined(__linux__)\n      struct ucred ucred;\n      socklen_t len = sizeof(ucred);\n      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {\n        port = ucred.pid;\n      }\n#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__\n      pid_t pid;\n      socklen_t len = sizeof(pid);\n      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {\n        port = pid;\n      }\n#endif\n      return;\n    }\n#endif\n    get_ip_and_port(addr, addr_len, ip, port);\n  }\n}\n\ninline constexpr unsigned int str2tag_core(const char *s, size_t l,\n                                           unsigned int h) {\n  return (l == 0)\n             ? h\n             : str2tag_core(\n                   s + 1, l - 1,\n                   // Unsets the 6 high bits of h, therefore no overflow happens\n                   (((std::numeric_limits<unsigned int>::max)() >> 6) &\n                    h * 33) ^\n                       static_cast<unsigned char>(*s));\n}\n\ninline unsigned int str2tag(const std::string &s) {\n  return str2tag_core(s.data(), s.size(), 0);\n}\n\nnamespace udl {\n\ninline constexpr unsigned int operator\"\"_t(const char *s, size_t l) {\n  return str2tag_core(s, l, 0);\n}\n\n} // namespace udl\n\ninline std::string\nfind_content_type(const std::string &path,\n                  const std::map<std::string, std::string> &user_data,\n                  const std::string &default_content_type) {\n  auto ext = file_extension(path);\n\n  auto it = user_data.find(ext);\n  if (it != user_data.end()) { return it->second; }\n\n  using udl::operator\"\"_t;\n\n  switch (str2tag(ext)) {\n  default: return default_content_type;\n\n  case \"css\"_t: return \"text/css\";\n  case \"csv\"_t: return \"text/csv\";\n  case \"htm\"_t:\n  case \"html\"_t: return \"text/html\";\n  case \"js\"_t:\n  case \"mjs\"_t: return \"text/javascript\";\n  case \"txt\"_t: return \"text/plain\";\n  case \"vtt\"_t: return \"text/vtt\";\n\n  case \"apng\"_t: return \"image/apng\";\n  case \"avif\"_t: return \"image/avif\";\n  case \"bmp\"_t: return \"image/bmp\";\n  case \"gif\"_t: return \"image/gif\";\n  case \"png\"_t: return \"image/png\";\n  case \"svg\"_t: return \"image/svg+xml\";\n  case \"webp\"_t: return \"image/webp\";\n  case \"ico\"_t: return \"image/x-icon\";\n  case \"tif\"_t: return \"image/tiff\";\n  case \"tiff\"_t: return \"image/tiff\";\n  case \"jpg\"_t:\n  case \"jpeg\"_t: return \"image/jpeg\";\n\n  case \"mp4\"_t: return \"video/mp4\";\n  case \"mpeg\"_t: return \"video/mpeg\";\n  case \"webm\"_t: return \"video/webm\";\n\n  case \"mp3\"_t: return \"audio/mp3\";\n  case \"mpga\"_t: return \"audio/mpeg\";\n  case \"weba\"_t: return \"audio/webm\";\n  case \"wav\"_t: return \"audio/wave\";\n\n  case \"otf\"_t: return \"font/otf\";\n  case \"ttf\"_t: return \"font/ttf\";\n  case \"woff\"_t: return \"font/woff\";\n  case \"woff2\"_t: return \"font/woff2\";\n\n  case \"7z\"_t: return \"application/x-7z-compressed\";\n  case \"atom\"_t: return \"application/atom+xml\";\n  case \"pdf\"_t: return \"application/pdf\";\n  case \"json\"_t: return \"application/json\";\n  case \"rss\"_t: return \"application/rss+xml\";\n  case \"tar\"_t: return \"application/x-tar\";\n  case \"xht\"_t:\n  case \"xhtml\"_t: return \"application/xhtml+xml\";\n  case \"xslt\"_t: return \"application/xslt+xml\";\n  case \"xml\"_t: return \"application/xml\";\n  case \"gz\"_t: return \"application/gzip\";\n  case \"zip\"_t: return \"application/zip\";\n  case \"wasm\"_t: return \"application/wasm\";\n  }\n}\n\ninline bool can_compress_content_type(const std::string &content_type) {\n  using udl::operator\"\"_t;\n\n  auto tag = str2tag(content_type);\n\n  switch (tag) {\n  case \"image/svg+xml\"_t:\n  case \"application/javascript\"_t:\n  case \"application/json\"_t:\n  case \"application/xml\"_t:\n  case \"application/protobuf\"_t:\n  case \"application/xhtml+xml\"_t: return true;\n\n  case \"text/event-stream\"_t: return false;\n\n  default: return !content_type.rfind(\"text/\", 0);\n  }\n}\n\ninline EncodingType encoding_type(const Request &req, const Response &res) {\n  auto ret =\n      detail::can_compress_content_type(res.get_header_value(\"Content-Type\"));\n  if (!ret) { return EncodingType::None; }\n\n  const auto &s = req.get_header_value(\"Accept-Encoding\");\n  (void)(s);\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n  // TODO: 'Accept-Encoding' has br, not br;q=0\n  ret = s.find(\"br\") != std::string::npos;\n  if (ret) { return EncodingType::Brotli; }\n#endif\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n  // TODO: 'Accept-Encoding' has gzip, not gzip;q=0\n  ret = s.find(\"gzip\") != std::string::npos;\n  if (ret) { return EncodingType::Gzip; }\n#endif\n\n  return EncodingType::None;\n}\n\ninline bool nocompressor::compress(const char *data, size_t data_length,\n                                   bool /*last*/, Callback callback) {\n  if (!data_length) { return true; }\n  return callback(data, data_length);\n}\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\ninline gzip_compressor::gzip_compressor() {\n  std::memset(&strm_, 0, sizeof(strm_));\n  strm_.zalloc = Z_NULL;\n  strm_.zfree = Z_NULL;\n  strm_.opaque = Z_NULL;\n\n  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,\n                           Z_DEFAULT_STRATEGY) == Z_OK;\n}\n\ninline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }\n\ninline bool gzip_compressor::compress(const char *data, size_t data_length,\n                                      bool last, Callback callback) {\n  assert(is_valid_);\n\n  do {\n    constexpr size_t max_avail_in =\n        (std::numeric_limits<decltype(strm_.avail_in)>::max)();\n\n    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(\n        (std::min)(data_length, max_avail_in));\n    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));\n\n    data_length -= strm_.avail_in;\n    data += strm_.avail_in;\n\n    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;\n    auto ret = Z_OK;\n\n    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n    do {\n      strm_.avail_out = static_cast<uInt>(buff.size());\n      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());\n\n      ret = deflate(&strm_, flush);\n      if (ret == Z_STREAM_ERROR) { return false; }\n\n      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {\n        return false;\n      }\n    } while (strm_.avail_out == 0);\n\n    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||\n           (flush == Z_NO_FLUSH && ret == Z_OK));\n    assert(strm_.avail_in == 0);\n  } while (data_length > 0);\n\n  return true;\n}\n\ninline gzip_decompressor::gzip_decompressor() {\n  std::memset(&strm_, 0, sizeof(strm_));\n  strm_.zalloc = Z_NULL;\n  strm_.zfree = Z_NULL;\n  strm_.opaque = Z_NULL;\n\n  // 15 is the value of wbits, which should be at the maximum possible value\n  // to ensure that any gzip stream can be decoded. The offset of 32 specifies\n  // that the stream type should be automatically detected either gzip or\n  // deflate.\n  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;\n}\n\ninline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }\n\ninline bool gzip_decompressor::is_valid() const { return is_valid_; }\n\ninline bool gzip_decompressor::decompress(const char *data, size_t data_length,\n                                          Callback callback) {\n  assert(is_valid_);\n\n  auto ret = Z_OK;\n\n  do {\n    constexpr size_t max_avail_in =\n        (std::numeric_limits<decltype(strm_.avail_in)>::max)();\n\n    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(\n        (std::min)(data_length, max_avail_in));\n    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));\n\n    data_length -= strm_.avail_in;\n    data += strm_.avail_in;\n\n    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n    while (strm_.avail_in > 0 && ret == Z_OK) {\n      strm_.avail_out = static_cast<uInt>(buff.size());\n      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());\n\n      ret = inflate(&strm_, Z_NO_FLUSH);\n\n      assert(ret != Z_STREAM_ERROR);\n      switch (ret) {\n      case Z_NEED_DICT:\n      case Z_DATA_ERROR:\n      case Z_MEM_ERROR: inflateEnd(&strm_); return false;\n      }\n\n      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {\n        return false;\n      }\n    }\n\n    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }\n\n  } while (data_length > 0);\n\n  return true;\n}\n#endif\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\ninline brotli_compressor::brotli_compressor() {\n  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);\n}\n\ninline brotli_compressor::~brotli_compressor() {\n  BrotliEncoderDestroyInstance(state_);\n}\n\ninline bool brotli_compressor::compress(const char *data, size_t data_length,\n                                        bool last, Callback callback) {\n  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n\n  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;\n  auto available_in = data_length;\n  auto next_in = reinterpret_cast<const uint8_t *>(data);\n\n  for (;;) {\n    if (last) {\n      if (BrotliEncoderIsFinished(state_)) { break; }\n    } else {\n      if (!available_in) { break; }\n    }\n\n    auto available_out = buff.size();\n    auto next_out = buff.data();\n\n    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,\n                                     &available_out, &next_out, nullptr)) {\n      return false;\n    }\n\n    auto output_bytes = buff.size() - available_out;\n    if (output_bytes) {\n      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);\n    }\n  }\n\n  return true;\n}\n\ninline brotli_decompressor::brotli_decompressor() {\n  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);\n  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT\n                        : BROTLI_DECODER_RESULT_ERROR;\n}\n\ninline brotli_decompressor::~brotli_decompressor() {\n  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }\n}\n\ninline bool brotli_decompressor::is_valid() const { return decoder_s; }\n\ninline bool brotli_decompressor::decompress(const char *data,\n                                            size_t data_length,\n                                            Callback callback) {\n  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||\n      decoder_r == BROTLI_DECODER_RESULT_ERROR) {\n    return 0;\n  }\n\n  auto next_in = reinterpret_cast<const uint8_t *>(data);\n  size_t avail_in = data_length;\n  size_t total_out;\n\n  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;\n\n  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {\n    char *next_out = buff.data();\n    size_t avail_out = buff.size();\n\n    decoder_r = BrotliDecoderDecompressStream(\n        decoder_s, &avail_in, &next_in, &avail_out,\n        reinterpret_cast<uint8_t **>(&next_out), &total_out);\n\n    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }\n\n    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }\n  }\n\n  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||\n         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;\n}\n#endif\n\ninline bool has_header(const Headers &headers, const std::string &key) {\n  return headers.find(key) != headers.end();\n}\n\ninline const char *get_header_value(const Headers &headers,\n                                    const std::string &key, const char *def,\n                                    size_t id) {\n  auto rng = headers.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second.c_str(); }\n  return def;\n}\n\ntemplate <typename T>\ninline bool parse_header(const char *beg, const char *end, T fn) {\n  // Skip trailing spaces and tabs.\n  while (beg < end && is_space_or_tab(end[-1])) {\n    end--;\n  }\n\n  auto p = beg;\n  while (p < end && *p != ':') {\n    p++;\n  }\n\n  if (p == end) { return false; }\n\n  auto key_end = p;\n\n  if (*p++ != ':') { return false; }\n\n  while (p < end && is_space_or_tab(*p)) {\n    p++;\n  }\n\n  if (p <= end) {\n    auto key_len = key_end - beg;\n    if (!key_len) { return false; }\n\n    auto key = std::string(beg, key_end);\n    auto val = case_ignore::equal(key, \"Location\")\n                   ? std::string(p, end)\n                   : decode_url(std::string(p, end), false);\n\n    // NOTE: From RFC 9110:\n    // Field values containing CR, LF, or NUL characters are\n    // invalid and dangerous, due to the varying ways that\n    // implementations might parse and interpret those\n    // characters; a recipient of CR, LF, or NUL within a field\n    // value MUST either reject the message or replace each of\n    // those characters with SP before further processing or\n    // forwarding of that message.\n    static const std::string CR_LF_NUL(\"\\r\\n\\0\", 3);\n    if (val.find_first_of(CR_LF_NUL) != std::string::npos) { return false; }\n\n    fn(key, val);\n    return true;\n  }\n\n  return false;\n}\n\ninline bool read_headers(Stream &strm, Headers &headers) {\n  const auto bufsiz = 2048;\n  char buf[bufsiz];\n  stream_line_reader line_reader(strm, buf, bufsiz);\n\n  for (;;) {\n    if (!line_reader.getline()) { return false; }\n\n    // Check if the line ends with CRLF.\n    auto line_terminator_len = 2;\n    if (line_reader.end_with_crlf()) {\n      // Blank line indicates end of headers.\n      if (line_reader.size() == 2) { break; }\n    } else {\n#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n      // Blank line indicates end of headers.\n      if (line_reader.size() == 1) { break; }\n      line_terminator_len = 1;\n#else\n      continue; // Skip invalid line.\n#endif\n    }\n\n    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }\n\n    // Exclude line terminator\n    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;\n\n    if (!parse_header(line_reader.ptr(), end,\n                      [&](const std::string &key, std::string &val) {\n                        headers.emplace(key, val);\n                      })) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\ninline bool read_content_with_length(Stream &strm, uint64_t len,\n                                     Progress progress,\n                                     ContentReceiverWithProgress out) {\n  char buf[CPPHTTPLIB_RECV_BUFSIZ];\n\n  uint64_t r = 0;\n  while (r < len) {\n    auto read_len = static_cast<size_t>(len - r);\n    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));\n    if (n <= 0) { return false; }\n\n    if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }\n    r += static_cast<uint64_t>(n);\n\n    if (progress) {\n      if (!progress(r, len)) { return false; }\n    }\n  }\n\n  return true;\n}\n\ninline void skip_content_with_length(Stream &strm, uint64_t len) {\n  char buf[CPPHTTPLIB_RECV_BUFSIZ];\n  uint64_t r = 0;\n  while (r < len) {\n    auto read_len = static_cast<size_t>(len - r);\n    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));\n    if (n <= 0) { return; }\n    r += static_cast<uint64_t>(n);\n  }\n}\n\ninline bool read_content_without_length(Stream &strm,\n                                        ContentReceiverWithProgress out) {\n  char buf[CPPHTTPLIB_RECV_BUFSIZ];\n  uint64_t r = 0;\n  for (;;) {\n    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);\n    if (n <= 0) { return true; }\n\n    if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }\n    r += static_cast<uint64_t>(n);\n  }\n\n  return true;\n}\n\ntemplate <typename T>\ninline bool read_content_chunked(Stream &strm, T &x,\n                                 ContentReceiverWithProgress out) {\n  const auto bufsiz = 16;\n  char buf[bufsiz];\n\n  stream_line_reader line_reader(strm, buf, bufsiz);\n\n  if (!line_reader.getline()) { return false; }\n\n  unsigned long chunk_len;\n  while (true) {\n    char *end_ptr;\n\n    chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);\n\n    if (end_ptr == line_reader.ptr()) { return false; }\n    if (chunk_len == ULONG_MAX) { return false; }\n\n    if (chunk_len == 0) { break; }\n\n    if (!read_content_with_length(strm, chunk_len, nullptr, out)) {\n      return false;\n    }\n\n    if (!line_reader.getline()) { return false; }\n\n    if (strcmp(line_reader.ptr(), \"\\r\\n\") != 0) { return false; }\n\n    if (!line_reader.getline()) { return false; }\n  }\n\n  assert(chunk_len == 0);\n\n  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentiones \"The chunked\n  // transfer coding is complete when a chunk with a chunk-size of zero is\n  // received, possibly followed by a trailer section, and finally terminated by\n  // an empty line\". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1\n  //\n  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section\n  // does't care for the existence of the final CRLF. In other words, it seems\n  // to be ok whether the final CRLF exists or not in the chunked data.\n  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3\n  //\n  // According to the reference code in RFC 9112, cpp-htpplib now allows\n  // chuncked transfer coding data without the final CRLF.\n  if (!line_reader.getline()) { return true; }\n\n  while (strcmp(line_reader.ptr(), \"\\r\\n\") != 0) {\n    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }\n\n    // Exclude line terminator\n    constexpr auto line_terminator_len = 2;\n    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;\n\n    parse_header(line_reader.ptr(), end,\n                 [&](const std::string &key, const std::string &val) {\n                   x.headers.emplace(key, val);\n                 });\n\n    if (!line_reader.getline()) { return false; }\n  }\n\n  return true;\n}\n\ninline bool is_chunked_transfer_encoding(const Headers &headers) {\n  return case_ignore::equal(\n      get_header_value(headers, \"Transfer-Encoding\", \"\", 0), \"chunked\");\n}\n\ntemplate <typename T, typename U>\nbool prepare_content_receiver(T &x, int &status,\n                              ContentReceiverWithProgress receiver,\n                              bool decompress, U callback) {\n  if (decompress) {\n    std::string encoding = x.get_header_value(\"Content-Encoding\");\n    std::unique_ptr<decompressor> decompressor;\n\n    if (encoding == \"gzip\" || encoding == \"deflate\") {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n      decompressor = detail::make_unique<gzip_decompressor>();\n#else\n      status = StatusCode::UnsupportedMediaType_415;\n      return false;\n#endif\n    } else if (encoding.find(\"br\") != std::string::npos) {\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n      decompressor = detail::make_unique<brotli_decompressor>();\n#else\n      status = StatusCode::UnsupportedMediaType_415;\n      return false;\n#endif\n    }\n\n    if (decompressor) {\n      if (decompressor->is_valid()) {\n        ContentReceiverWithProgress out = [&](const char *buf, size_t n,\n                                              uint64_t off, uint64_t len) {\n          return decompressor->decompress(buf, n,\n                                          [&](const char *buf2, size_t n2) {\n                                            return receiver(buf2, n2, off, len);\n                                          });\n        };\n        return callback(std::move(out));\n      } else {\n        status = StatusCode::InternalServerError_500;\n        return false;\n      }\n    }\n  }\n\n  ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,\n                                        uint64_t len) {\n    return receiver(buf, n, off, len);\n  };\n  return callback(std::move(out));\n}\n\ntemplate <typename T>\nbool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,\n                  Progress progress, ContentReceiverWithProgress receiver,\n                  bool decompress) {\n  return prepare_content_receiver(\n      x, status, std::move(receiver), decompress,\n      [&](const ContentReceiverWithProgress &out) {\n        auto ret = true;\n        auto exceed_payload_max_length = false;\n\n        if (is_chunked_transfer_encoding(x.headers)) {\n          ret = read_content_chunked(strm, x, out);\n        } else if (!has_header(x.headers, \"Content-Length\")) {\n          ret = read_content_without_length(strm, out);\n        } else {\n          auto len = get_header_value_u64(x.headers, \"Content-Length\", 0, 0);\n          if (len > payload_max_length) {\n            exceed_payload_max_length = true;\n            skip_content_with_length(strm, len);\n            ret = false;\n          } else if (len > 0) {\n            ret = read_content_with_length(strm, len, std::move(progress), out);\n          }\n        }\n\n        if (!ret) {\n          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413\n                                             : StatusCode::BadRequest_400;\n        }\n        return ret;\n      });\n}\n\ninline ssize_t write_request_line(Stream &strm, const std::string &method,\n                                  const std::string &path) {\n  std::string s = method;\n  s += \" \";\n  s += path;\n  s += \" HTTP/1.1\\r\\n\";\n  return strm.write(s.data(), s.size());\n}\n\ninline ssize_t write_response_line(Stream &strm, int status) {\n  std::string s = \"HTTP/1.1 \";\n  s += std::to_string(status);\n  s += \" \";\n  s += httplib::status_message(status);\n  s += \"\\r\\n\";\n  return strm.write(s.data(), s.size());\n}\n\ninline ssize_t write_headers(Stream &strm, const Headers &headers) {\n  ssize_t write_len = 0;\n  for (const auto &x : headers) {\n    std::string s;\n    s = x.first;\n    s += \": \";\n    s += x.second;\n    s += \"\\r\\n\";\n\n    auto len = strm.write(s.data(), s.size());\n    if (len < 0) { return len; }\n    write_len += len;\n  }\n  auto len = strm.write(\"\\r\\n\");\n  if (len < 0) { return len; }\n  write_len += len;\n  return write_len;\n}\n\ninline bool write_data(Stream &strm, const char *d, size_t l) {\n  size_t offset = 0;\n  while (offset < l) {\n    auto length = strm.write(d + offset, l - offset);\n    if (length < 0) { return false; }\n    offset += static_cast<size_t>(length);\n  }\n  return true;\n}\n\ntemplate <typename T>\ninline bool write_content(Stream &strm, const ContentProvider &content_provider,\n                          size_t offset, size_t length, T is_shutting_down,\n                          Error &error) {\n  size_t end_offset = offset + length;\n  auto ok = true;\n  DataSink data_sink;\n\n  data_sink.write = [&](const char *d, size_t l) -> bool {\n    if (ok) {\n      if (strm.is_writable() && write_data(strm, d, l)) {\n        offset += l;\n      } else {\n        ok = false;\n      }\n    }\n    return ok;\n  };\n\n  data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };\n\n  while (offset < end_offset && !is_shutting_down()) {\n    if (!strm.is_writable()) {\n      error = Error::Write;\n      return false;\n    } else if (!content_provider(offset, end_offset - offset, data_sink)) {\n      error = Error::Canceled;\n      return false;\n    } else if (!ok) {\n      error = Error::Write;\n      return false;\n    }\n  }\n\n  error = Error::Success;\n  return true;\n}\n\ntemplate <typename T>\ninline bool write_content(Stream &strm, const ContentProvider &content_provider,\n                          size_t offset, size_t length,\n                          const T &is_shutting_down) {\n  auto error = Error::Success;\n  return write_content(strm, content_provider, offset, length, is_shutting_down,\n                       error);\n}\n\ntemplate <typename T>\ninline bool\nwrite_content_without_length(Stream &strm,\n                             const ContentProvider &content_provider,\n                             const T &is_shutting_down) {\n  size_t offset = 0;\n  auto data_available = true;\n  auto ok = true;\n  DataSink data_sink;\n\n  data_sink.write = [&](const char *d, size_t l) -> bool {\n    if (ok) {\n      offset += l;\n      if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }\n    }\n    return ok;\n  };\n\n  data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };\n\n  data_sink.done = [&](void) { data_available = false; };\n\n  while (data_available && !is_shutting_down()) {\n    if (!strm.is_writable()) {\n      return false;\n    } else if (!content_provider(offset, 0, data_sink)) {\n      return false;\n    } else if (!ok) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename T, typename U>\ninline bool\nwrite_content_chunked(Stream &strm, const ContentProvider &content_provider,\n                      const T &is_shutting_down, U &compressor, Error &error) {\n  size_t offset = 0;\n  auto data_available = true;\n  auto ok = true;\n  DataSink data_sink;\n\n  data_sink.write = [&](const char *d, size_t l) -> bool {\n    if (ok) {\n      data_available = l > 0;\n      offset += l;\n\n      std::string payload;\n      if (compressor.compress(d, l, false,\n                              [&](const char *data, size_t data_len) {\n                                payload.append(data, data_len);\n                                return true;\n                              })) {\n        if (!payload.empty()) {\n          // Emit chunked response header and footer for each chunk\n          auto chunk =\n              from_i_to_hex(payload.size()) + \"\\r\\n\" + payload + \"\\r\\n\";\n          if (!strm.is_writable() ||\n              !write_data(strm, chunk.data(), chunk.size())) {\n            ok = false;\n          }\n        }\n      } else {\n        ok = false;\n      }\n    }\n    return ok;\n  };\n\n  data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };\n\n  auto done_with_trailer = [&](const Headers *trailer) {\n    if (!ok) { return; }\n\n    data_available = false;\n\n    std::string payload;\n    if (!compressor.compress(nullptr, 0, true,\n                             [&](const char *data, size_t data_len) {\n                               payload.append(data, data_len);\n                               return true;\n                             })) {\n      ok = false;\n      return;\n    }\n\n    if (!payload.empty()) {\n      // Emit chunked response header and footer for each chunk\n      auto chunk = from_i_to_hex(payload.size()) + \"\\r\\n\" + payload + \"\\r\\n\";\n      if (!strm.is_writable() ||\n          !write_data(strm, chunk.data(), chunk.size())) {\n        ok = false;\n        return;\n      }\n    }\n\n    static const std::string done_marker(\"0\\r\\n\");\n    if (!write_data(strm, done_marker.data(), done_marker.size())) {\n      ok = false;\n    }\n\n    // Trailer\n    if (trailer) {\n      for (const auto &kv : *trailer) {\n        std::string field_line = kv.first + \": \" + kv.second + \"\\r\\n\";\n        if (!write_data(strm, field_line.data(), field_line.size())) {\n          ok = false;\n        }\n      }\n    }\n\n    static const std::string crlf(\"\\r\\n\");\n    if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }\n  };\n\n  data_sink.done = [&](void) { done_with_trailer(nullptr); };\n\n  data_sink.done_with_trailer = [&](const Headers &trailer) {\n    done_with_trailer(&trailer);\n  };\n\n  while (data_available && !is_shutting_down()) {\n    if (!strm.is_writable()) {\n      error = Error::Write;\n      return false;\n    } else if (!content_provider(offset, 0, data_sink)) {\n      error = Error::Canceled;\n      return false;\n    } else if (!ok) {\n      error = Error::Write;\n      return false;\n    }\n  }\n\n  error = Error::Success;\n  return true;\n}\n\ntemplate <typename T, typename U>\ninline bool write_content_chunked(Stream &strm,\n                                  const ContentProvider &content_provider,\n                                  const T &is_shutting_down, U &compressor) {\n  auto error = Error::Success;\n  return write_content_chunked(strm, content_provider, is_shutting_down,\n                               compressor, error);\n}\n\ntemplate <typename T>\ninline bool redirect(T &cli, Request &req, Response &res,\n                     const std::string &path, const std::string &location,\n                     Error &error) {\n  Request new_req = req;\n  new_req.path = path;\n  new_req.redirect_count_ -= 1;\n\n  if (res.status == StatusCode::SeeOther_303 &&\n      (req.method != \"GET\" && req.method != \"HEAD\")) {\n    new_req.method = \"GET\";\n    new_req.body.clear();\n    new_req.headers.clear();\n  }\n\n  Response new_res;\n\n  auto ret = cli.send(new_req, new_res, error);\n  if (ret) {\n    req = new_req;\n    res = new_res;\n\n    if (res.location.empty()) { res.location = location; }\n  }\n  return ret;\n}\n\ninline std::string params_to_query_str(const Params &params) {\n  std::string query;\n\n  for (auto it = params.begin(); it != params.end(); ++it) {\n    if (it != params.begin()) { query += \"&\"; }\n    query += it->first;\n    query += \"=\";\n    query += encode_query_param(it->second);\n  }\n  return query;\n}\n\ninline void parse_query_text(const char *data, std::size_t size,\n                             Params &params) {\n  std::set<std::string> cache;\n  split(data, data + size, '&', [&](const char *b, const char *e) {\n    std::string kv(b, e);\n    if (cache.find(kv) != cache.end()) { return; }\n    cache.insert(std::move(kv));\n\n    std::string key;\n    std::string val;\n    divide(b, static_cast<std::size_t>(e - b), '=',\n           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,\n               std::size_t rhs_size) {\n             key.assign(lhs_data, lhs_size);\n             val.assign(rhs_data, rhs_size);\n           });\n\n    if (!key.empty()) {\n      params.emplace(decode_url(key, true), decode_url(val, true));\n    }\n  });\n}\n\ninline void parse_query_text(const std::string &s, Params &params) {\n  parse_query_text(s.data(), s.size(), params);\n}\n\ninline bool parse_multipart_boundary(const std::string &content_type,\n                                     std::string &boundary) {\n  auto boundary_keyword = \"boundary=\";\n  auto pos = content_type.find(boundary_keyword);\n  if (pos == std::string::npos) { return false; }\n  auto end = content_type.find(';', pos);\n  auto beg = pos + strlen(boundary_keyword);\n  boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));\n  return !boundary.empty();\n}\n\ninline void parse_disposition_params(const std::string &s, Params &params) {\n  std::set<std::string> cache;\n  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {\n    std::string kv(b, e);\n    if (cache.find(kv) != cache.end()) { return; }\n    cache.insert(kv);\n\n    std::string key;\n    std::string val;\n    split(b, e, '=', [&](const char *b2, const char *e2) {\n      if (key.empty()) {\n        key.assign(b2, e2);\n      } else {\n        val.assign(b2, e2);\n      }\n    });\n\n    if (!key.empty()) {\n      params.emplace(trim_double_quotes_copy((key)),\n                     trim_double_quotes_copy((val)));\n    }\n  });\n}\n\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\ninline bool parse_range_header(const std::string &s, Ranges &ranges) {\n#else\ninline bool parse_range_header(const std::string &s, Ranges &ranges) try {\n#endif\n  auto is_valid = [](const std::string &str) {\n    return std::all_of(str.cbegin(), str.cend(),\n                       [](unsigned char c) { return std::isdigit(c); });\n  };\n\n  if (s.size() > 7 && s.compare(0, 6, \"bytes=\") == 0) {\n    const auto pos = static_cast<size_t>(6);\n    const auto len = static_cast<size_t>(s.size() - 6);\n    auto all_valid_ranges = true;\n    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {\n      if (!all_valid_ranges) { return; }\n\n      const auto it = std::find(b, e, '-');\n      if (it == e) {\n        all_valid_ranges = false;\n        return;\n      }\n\n      const auto lhs = std::string(b, it);\n      const auto rhs = std::string(it + 1, e);\n      if (!is_valid(lhs) || !is_valid(rhs)) {\n        all_valid_ranges = false;\n        return;\n      }\n\n      const auto first =\n          static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));\n      const auto last =\n          static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));\n      if ((first == -1 && last == -1) ||\n          (first != -1 && last != -1 && first > last)) {\n        all_valid_ranges = false;\n        return;\n      }\n\n      ranges.emplace_back(first, last);\n    });\n    return all_valid_ranges && !ranges.empty();\n  }\n  return false;\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\n}\n#else\n} catch (...) { return false; }\n#endif\n\nclass MultipartFormDataParser {\npublic:\n  MultipartFormDataParser() = default;\n\n  void set_boundary(std::string &&boundary) {\n    boundary_ = boundary;\n    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;\n    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;\n  }\n\n  bool is_valid() const { return is_valid_; }\n\n  bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,\n             const MultipartContentHeader &header_callback) {\n\n    buf_append(buf, n);\n\n    while (buf_size() > 0) {\n      switch (state_) {\n      case 0: { // Initial boundary\n        buf_erase(buf_find(dash_boundary_crlf_));\n        if (dash_boundary_crlf_.size() > buf_size()) { return true; }\n        if (!buf_start_with(dash_boundary_crlf_)) { return false; }\n        buf_erase(dash_boundary_crlf_.size());\n        state_ = 1;\n        break;\n      }\n      case 1: { // New entry\n        clear_file_info();\n        state_ = 2;\n        break;\n      }\n      case 2: { // Headers\n        auto pos = buf_find(crlf_);\n        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }\n        while (pos < buf_size()) {\n          // Empty line\n          if (pos == 0) {\n            if (!header_callback(file_)) {\n              is_valid_ = false;\n              return false;\n            }\n            buf_erase(crlf_.size());\n            state_ = 3;\n            break;\n          }\n\n          const auto header = buf_head(pos);\n\n          if (!parse_header(header.data(), header.data() + header.size(),\n                            [&](const std::string &, const std::string &) {})) {\n            is_valid_ = false;\n            return false;\n          }\n\n          static const std::string header_content_type = \"Content-Type:\";\n\n          if (start_with_case_ignore(header, header_content_type)) {\n            file_.content_type =\n                trim_copy(header.substr(header_content_type.size()));\n          } else {\n            static const std::regex re_content_disposition(\n                R\"~(^Content-Disposition:\\s*form-data;\\s*(.*)$)~\",\n                std::regex_constants::icase);\n\n            std::smatch m;\n            if (std::regex_match(header, m, re_content_disposition)) {\n              Params params;\n              parse_disposition_params(m[1], params);\n\n              auto it = params.find(\"name\");\n              if (it != params.end()) {\n                file_.name = it->second;\n              } else {\n                is_valid_ = false;\n                return false;\n              }\n\n              it = params.find(\"filename\");\n              if (it != params.end()) { file_.filename = it->second; }\n\n              it = params.find(\"filename*\");\n              if (it != params.end()) {\n                // Only allow UTF-8 enconnding...\n                static const std::regex re_rfc5987_encoding(\n                    R\"~(^UTF-8''(.+?)$)~\", std::regex_constants::icase);\n\n                std::smatch m2;\n                if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {\n                  file_.filename = decode_url(m2[1], false); // override...\n                } else {\n                  is_valid_ = false;\n                  return false;\n                }\n              }\n            }\n          }\n          buf_erase(pos + crlf_.size());\n          pos = buf_find(crlf_);\n        }\n        if (state_ != 3) { return true; }\n        break;\n      }\n      case 3: { // Body\n        if (crlf_dash_boundary_.size() > buf_size()) { return true; }\n        auto pos = buf_find(crlf_dash_boundary_);\n        if (pos < buf_size()) {\n          if (!content_callback(buf_data(), pos)) {\n            is_valid_ = false;\n            return false;\n          }\n          buf_erase(pos + crlf_dash_boundary_.size());\n          state_ = 4;\n        } else {\n          auto len = buf_size() - crlf_dash_boundary_.size();\n          if (len > 0) {\n            if (!content_callback(buf_data(), len)) {\n              is_valid_ = false;\n              return false;\n            }\n            buf_erase(len);\n          }\n          return true;\n        }\n        break;\n      }\n      case 4: { // Boundary\n        if (crlf_.size() > buf_size()) { return true; }\n        if (buf_start_with(crlf_)) {\n          buf_erase(crlf_.size());\n          state_ = 1;\n        } else {\n          if (dash_.size() > buf_size()) { return true; }\n          if (buf_start_with(dash_)) {\n            buf_erase(dash_.size());\n            is_valid_ = true;\n            buf_erase(buf_size()); // Remove epilogue\n          } else {\n            return true;\n          }\n        }\n        break;\n      }\n      }\n    }\n\n    return true;\n  }\n\nprivate:\n  void clear_file_info() {\n    file_.name.clear();\n    file_.filename.clear();\n    file_.content_type.clear();\n  }\n\n  bool start_with_case_ignore(const std::string &a,\n                              const std::string &b) const {\n    if (a.size() < b.size()) { return false; }\n    for (size_t i = 0; i < b.size(); i++) {\n      if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  const std::string dash_ = \"--\";\n  const std::string crlf_ = \"\\r\\n\";\n  std::string boundary_;\n  std::string dash_boundary_crlf_;\n  std::string crlf_dash_boundary_;\n\n  size_t state_ = 0;\n  bool is_valid_ = false;\n  MultipartFormData file_;\n\n  // Buffer\n  bool start_with(const std::string &a, size_t spos, size_t epos,\n                  const std::string &b) const {\n    if (epos - spos < b.size()) { return false; }\n    for (size_t i = 0; i < b.size(); i++) {\n      if (a[i + spos] != b[i]) { return false; }\n    }\n    return true;\n  }\n\n  size_t buf_size() const { return buf_epos_ - buf_spos_; }\n\n  const char *buf_data() const { return &buf_[buf_spos_]; }\n\n  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }\n\n  bool buf_start_with(const std::string &s) const {\n    return start_with(buf_, buf_spos_, buf_epos_, s);\n  }\n\n  size_t buf_find(const std::string &s) const {\n    auto c = s.front();\n\n    size_t off = buf_spos_;\n    while (off < buf_epos_) {\n      auto pos = off;\n      while (true) {\n        if (pos == buf_epos_) { return buf_size(); }\n        if (buf_[pos] == c) { break; }\n        pos++;\n      }\n\n      auto remaining_size = buf_epos_ - pos;\n      if (s.size() > remaining_size) { return buf_size(); }\n\n      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }\n\n      off = pos + 1;\n    }\n\n    return buf_size();\n  }\n\n  void buf_append(const char *data, size_t n) {\n    auto remaining_size = buf_size();\n    if (remaining_size > 0 && buf_spos_ > 0) {\n      for (size_t i = 0; i < remaining_size; i++) {\n        buf_[i] = buf_[buf_spos_ + i];\n      }\n    }\n    buf_spos_ = 0;\n    buf_epos_ = remaining_size;\n\n    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }\n\n    for (size_t i = 0; i < n; i++) {\n      buf_[buf_epos_ + i] = data[i];\n    }\n    buf_epos_ += n;\n  }\n\n  void buf_erase(size_t size) { buf_spos_ += size; }\n\n  std::string buf_;\n  size_t buf_spos_ = 0;\n  size_t buf_epos_ = 0;\n};\n\ninline std::string random_string(size_t length) {\n  static const char data[] =\n      \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n  // std::random_device might actually be deterministic on some\n  // platforms, but due to lack of support in the c++ standard library,\n  // doing better requires either some ugly hacks or breaking portability.\n  static std::random_device seed_gen;\n\n  // Request 128 bits of entropy for initialization\n  static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(),\n                                     seed_gen()};\n\n  static std::mt19937 engine(seed_sequence);\n\n  std::string result;\n  for (size_t i = 0; i < length; i++) {\n    result += data[engine() % (sizeof(data) - 1)];\n  }\n  return result;\n}\n\ninline std::string make_multipart_data_boundary() {\n  return \"--cpp-httplib-multipart-data-\" + detail::random_string(16);\n}\n\ninline bool is_multipart_boundary_chars_valid(const std::string &boundary) {\n  auto valid = true;\n  for (size_t i = 0; i < boundary.size(); i++) {\n    auto c = boundary[i];\n    if (!std::isalnum(c) && c != '-' && c != '_') {\n      valid = false;\n      break;\n    }\n  }\n  return valid;\n}\n\ntemplate <typename T>\ninline std::string\nserialize_multipart_formdata_item_begin(const T &item,\n                                        const std::string &boundary) {\n  std::string body = \"--\" + boundary + \"\\r\\n\";\n  body += \"Content-Disposition: form-data; name=\\\"\" + item.name + \"\\\"\";\n  if (!item.filename.empty()) {\n    body += \"; filename=\\\"\" + item.filename + \"\\\"\";\n  }\n  body += \"\\r\\n\";\n  if (!item.content_type.empty()) {\n    body += \"Content-Type: \" + item.content_type + \"\\r\\n\";\n  }\n  body += \"\\r\\n\";\n\n  return body;\n}\n\ninline std::string serialize_multipart_formdata_item_end() { return \"\\r\\n\"; }\n\ninline std::string\nserialize_multipart_formdata_finish(const std::string &boundary) {\n  return \"--\" + boundary + \"--\\r\\n\";\n}\n\ninline std::string\nserialize_multipart_formdata_get_content_type(const std::string &boundary) {\n  return \"multipart/form-data; boundary=\" + boundary;\n}\n\ninline std::string\nserialize_multipart_formdata(const MultipartFormDataItems &items,\n                             const std::string &boundary, bool finish = true) {\n  std::string body;\n\n  for (const auto &item : items) {\n    body += serialize_multipart_formdata_item_begin(item, boundary);\n    body += item.content + serialize_multipart_formdata_item_end();\n  }\n\n  if (finish) { body += serialize_multipart_formdata_finish(boundary); }\n\n  return body;\n}\n\ninline bool range_error(Request &req, Response &res) {\n  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {\n    ssize_t contant_len = static_cast<ssize_t>(\n        res.content_length_ ? res.content_length_ : res.body.size());\n\n    ssize_t prev_first_pos = -1;\n    ssize_t prev_last_pos = -1;\n    size_t overwrapping_count = 0;\n\n    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110\n    // 'HTTP Semantics' to avoid potential denial-of-service attacks.\n    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2\n\n    // Too many ranges\n    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }\n\n    for (auto &r : req.ranges) {\n      auto &first_pos = r.first;\n      auto &last_pos = r.second;\n\n      if (first_pos == -1 && last_pos == -1) {\n        first_pos = 0;\n        last_pos = contant_len;\n      }\n\n      if (first_pos == -1) {\n        first_pos = contant_len - last_pos;\n        last_pos = contant_len - 1;\n      }\n\n      if (last_pos == -1) { last_pos = contant_len - 1; }\n\n      // Range must be within content length\n      if (!(0 <= first_pos && first_pos <= last_pos &&\n            last_pos <= contant_len - 1)) {\n        return true;\n      }\n\n      // Ranges must be in ascending order\n      if (first_pos <= prev_first_pos) { return true; }\n\n      // Request must not have more than two overlapping ranges\n      if (first_pos <= prev_last_pos) {\n        overwrapping_count++;\n        if (overwrapping_count > 2) { return true; }\n      }\n\n      prev_first_pos = (std::max)(prev_first_pos, first_pos);\n      prev_last_pos = (std::max)(prev_last_pos, last_pos);\n    }\n  }\n\n  return false;\n}\n\ninline std::pair<size_t, size_t>\nget_range_offset_and_length(Range r, size_t content_length) {\n  assert(r.first != -1 && r.second != -1);\n  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));\n  assert(r.first <= r.second &&\n         r.second < static_cast<ssize_t>(content_length));\n  (void)(content_length);\n  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);\n}\n\ninline std::string make_content_range_header_field(\n    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {\n  auto st = offset_and_length.first;\n  auto ed = st + offset_and_length.second - 1;\n\n  std::string field = \"bytes \";\n  field += std::to_string(st);\n  field += \"-\";\n  field += std::to_string(ed);\n  field += \"/\";\n  field += std::to_string(content_length);\n  return field;\n}\n\ntemplate <typename SToken, typename CToken, typename Content>\nbool process_multipart_ranges_data(const Request &req,\n                                   const std::string &boundary,\n                                   const std::string &content_type,\n                                   size_t content_length, SToken stoken,\n                                   CToken ctoken, Content content) {\n  for (size_t i = 0; i < req.ranges.size(); i++) {\n    ctoken(\"--\");\n    stoken(boundary);\n    ctoken(\"\\r\\n\");\n    if (!content_type.empty()) {\n      ctoken(\"Content-Type: \");\n      stoken(content_type);\n      ctoken(\"\\r\\n\");\n    }\n\n    auto offset_and_length =\n        get_range_offset_and_length(req.ranges[i], content_length);\n\n    ctoken(\"Content-Range: \");\n    stoken(make_content_range_header_field(offset_and_length, content_length));\n    ctoken(\"\\r\\n\");\n    ctoken(\"\\r\\n\");\n\n    if (!content(offset_and_length.first, offset_and_length.second)) {\n      return false;\n    }\n    ctoken(\"\\r\\n\");\n  }\n\n  ctoken(\"--\");\n  stoken(boundary);\n  ctoken(\"--\");\n\n  return true;\n}\n\ninline void make_multipart_ranges_data(const Request &req, Response &res,\n                                       const std::string &boundary,\n                                       const std::string &content_type,\n                                       size_t content_length,\n                                       std::string &data) {\n  process_multipart_ranges_data(\n      req, boundary, content_type, content_length,\n      [&](const std::string &token) { data += token; },\n      [&](const std::string &token) { data += token; },\n      [&](size_t offset, size_t length) {\n        assert(offset + length <= content_length);\n        data += res.body.substr(offset, length);\n        return true;\n      });\n}\n\ninline size_t get_multipart_ranges_data_length(const Request &req,\n                                               const std::string &boundary,\n                                               const std::string &content_type,\n                                               size_t content_length) {\n  size_t data_length = 0;\n\n  process_multipart_ranges_data(\n      req, boundary, content_type, content_length,\n      [&](const std::string &token) { data_length += token.size(); },\n      [&](const std::string &token) { data_length += token.size(); },\n      [&](size_t /*offset*/, size_t length) {\n        data_length += length;\n        return true;\n      });\n\n  return data_length;\n}\n\ntemplate <typename T>\ninline bool\nwrite_multipart_ranges_data(Stream &strm, const Request &req, Response &res,\n                            const std::string &boundary,\n                            const std::string &content_type,\n                            size_t content_length, const T &is_shutting_down) {\n  return process_multipart_ranges_data(\n      req, boundary, content_type, content_length,\n      [&](const std::string &token) { strm.write(token); },\n      [&](const std::string &token) { strm.write(token); },\n      [&](size_t offset, size_t length) {\n        return write_content(strm, res.content_provider_, offset, length,\n                             is_shutting_down);\n      });\n}\n\ninline bool expect_content(const Request &req) {\n  if (req.method == \"POST\" || req.method == \"PUT\" || req.method == \"PATCH\" ||\n      req.method == \"PRI\" || req.method == \"DELETE\") {\n    return true;\n  }\n  // TODO: check if Content-Length is set\n  return false;\n}\n\ninline bool has_crlf(const std::string &s) {\n  auto p = s.c_str();\n  while (*p) {\n    if (*p == '\\r' || *p == '\\n') { return true; }\n    p++;\n  }\n  return false;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline std::string message_digest(const std::string &s, const EVP_MD *algo) {\n  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(\n      EVP_MD_CTX_new(), EVP_MD_CTX_free);\n\n  unsigned int hash_length = 0;\n  unsigned char hash[EVP_MAX_MD_SIZE];\n\n  EVP_DigestInit_ex(context.get(), algo, nullptr);\n  EVP_DigestUpdate(context.get(), s.c_str(), s.size());\n  EVP_DigestFinal_ex(context.get(), hash, &hash_length);\n\n  std::stringstream ss;\n  for (auto i = 0u; i < hash_length; ++i) {\n    ss << std::hex << std::setw(2) << std::setfill('0')\n       << static_cast<unsigned int>(hash[i]);\n  }\n\n  return ss.str();\n}\n\ninline std::string MD5(const std::string &s) {\n  return message_digest(s, EVP_md5());\n}\n\ninline std::string SHA_256(const std::string &s) {\n  return message_digest(s, EVP_sha256());\n}\n\ninline std::string SHA_512(const std::string &s) {\n  return message_digest(s, EVP_sha512());\n}\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n#ifdef _WIN32\n// NOTE: This code came up with the following stackoverflow post:\n// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store\ninline bool load_system_certs_on_windows(X509_STORE *store) {\n  auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L\"ROOT\");\n  if (!hStore) { return false; }\n\n  auto result = false;\n  PCCERT_CONTEXT pContext = NULL;\n  while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=\n         nullptr) {\n    auto encoded_cert =\n        static_cast<const unsigned char *>(pContext->pbCertEncoded);\n\n    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);\n    if (x509) {\n      X509_STORE_add_cert(store, x509);\n      X509_free(x509);\n      result = true;\n    }\n  }\n\n  CertFreeCertificateContext(pContext);\n  CertCloseStore(hStore, 0);\n\n  return result;\n}\n#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)\n#if TARGET_OS_OSX\ntemplate <typename T>\nusing CFObjectPtr =\n    std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;\n\ninline void cf_object_ptr_deleter(CFTypeRef obj) {\n  if (obj) { CFRelease(obj); }\n}\n\ninline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {\n  CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};\n  CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,\n                        kCFBooleanTrue};\n\n  CFObjectPtr<CFDictionaryRef> query(\n      CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,\n                         sizeof(keys) / sizeof(keys[0]),\n                         &kCFTypeDictionaryKeyCallBacks,\n                         &kCFTypeDictionaryValueCallBacks),\n      cf_object_ptr_deleter);\n\n  if (!query) { return false; }\n\n  CFTypeRef security_items = nullptr;\n  if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||\n      CFArrayGetTypeID() != CFGetTypeID(security_items)) {\n    return false;\n  }\n\n  certs.reset(reinterpret_cast<CFArrayRef>(security_items));\n  return true;\n}\n\ninline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {\n  CFArrayRef root_security_items = nullptr;\n  if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {\n    return false;\n  }\n\n  certs.reset(root_security_items);\n  return true;\n}\n\ninline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {\n  auto result = false;\n  for (auto i = 0; i < CFArrayGetCount(certs); ++i) {\n    const auto cert = reinterpret_cast<const __SecCertificate *>(\n        CFArrayGetValueAtIndex(certs, i));\n\n    if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }\n\n    CFDataRef cert_data = nullptr;\n    if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=\n        errSecSuccess) {\n      continue;\n    }\n\n    CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);\n\n    auto encoded_cert = static_cast<const unsigned char *>(\n        CFDataGetBytePtr(cert_data_ptr.get()));\n\n    auto x509 =\n        d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));\n\n    if (x509) {\n      X509_STORE_add_cert(store, x509);\n      X509_free(x509);\n      result = true;\n    }\n  }\n\n  return result;\n}\n\ninline bool load_system_certs_on_macos(X509_STORE *store) {\n  auto result = false;\n  CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);\n  if (retrieve_certs_from_keychain(certs) && certs) {\n    result = add_certs_to_x509_store(certs.get(), store);\n  }\n\n  if (retrieve_root_certs_from_keychain(certs) && certs) {\n    result = add_certs_to_x509_store(certs.get(), store) || result;\n  }\n\n  return result;\n}\n#endif // TARGET_OS_OSX\n#endif // _WIN32\n#endif // CPPHTTPLIB_OPENSSL_SUPPORT\n\n#ifdef _WIN32\nclass WSInit {\npublic:\n  WSInit() {\n    WSADATA wsaData;\n    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;\n  }\n\n  ~WSInit() {\n    if (is_valid_) WSACleanup();\n  }\n\n  bool is_valid_ = false;\n};\n\nstatic WSInit wsinit_;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline std::pair<std::string, std::string> make_digest_authentication_header(\n    const Request &req, const std::map<std::string, std::string> &auth,\n    size_t cnonce_count, const std::string &cnonce, const std::string &username,\n    const std::string &password, bool is_proxy = false) {\n  std::string nc;\n  {\n    std::stringstream ss;\n    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;\n    nc = ss.str();\n  }\n\n  std::string qop;\n  if (auth.find(\"qop\") != auth.end()) {\n    qop = auth.at(\"qop\");\n    if (qop.find(\"auth-int\") != std::string::npos) {\n      qop = \"auth-int\";\n    } else if (qop.find(\"auth\") != std::string::npos) {\n      qop = \"auth\";\n    } else {\n      qop.clear();\n    }\n  }\n\n  std::string algo = \"MD5\";\n  if (auth.find(\"algorithm\") != auth.end()) { algo = auth.at(\"algorithm\"); }\n\n  std::string response;\n  {\n    auto H = algo == \"SHA-256\"   ? detail::SHA_256\n             : algo == \"SHA-512\" ? detail::SHA_512\n                                 : detail::MD5;\n\n    auto A1 = username + \":\" + auth.at(\"realm\") + \":\" + password;\n\n    auto A2 = req.method + \":\" + req.path;\n    if (qop == \"auth-int\") { A2 += \":\" + H(req.body); }\n\n    if (qop.empty()) {\n      response = H(H(A1) + \":\" + auth.at(\"nonce\") + \":\" + H(A2));\n    } else {\n      response = H(H(A1) + \":\" + auth.at(\"nonce\") + \":\" + nc + \":\" + cnonce +\n                   \":\" + qop + \":\" + H(A2));\n    }\n  }\n\n  auto opaque = (auth.find(\"opaque\") != auth.end()) ? auth.at(\"opaque\") : \"\";\n\n  auto field = \"Digest username=\\\"\" + username + \"\\\", realm=\\\"\" +\n               auth.at(\"realm\") + \"\\\", nonce=\\\"\" + auth.at(\"nonce\") +\n               \"\\\", uri=\\\"\" + req.path + \"\\\", algorithm=\" + algo +\n               (qop.empty() ? \", response=\\\"\"\n                            : \", qop=\" + qop + \", nc=\" + nc + \", cnonce=\\\"\" +\n                                  cnonce + \"\\\", response=\\\"\") +\n               response + \"\\\"\" +\n               (opaque.empty() ? \"\" : \", opaque=\\\"\" + opaque + \"\\\"\");\n\n  auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n  return std::make_pair(key, field);\n}\n#endif\n\ninline bool parse_www_authenticate(const Response &res,\n                                   std::map<std::string, std::string> &auth,\n                                   bool is_proxy) {\n  auto auth_key = is_proxy ? \"Proxy-Authenticate\" : \"WWW-Authenticate\";\n  if (res.has_header(auth_key)) {\n    static auto re = std::regex(R\"~((?:(?:,\\s*)?(.+?)=(?:\"(.*?)\"|([^,]*))))~\");\n    auto s = res.get_header_value(auth_key);\n    auto pos = s.find(' ');\n    if (pos != std::string::npos) {\n      auto type = s.substr(0, pos);\n      if (type == \"Basic\") {\n        return false;\n      } else if (type == \"Digest\") {\n        s = s.substr(pos + 1);\n        auto beg = std::sregex_iterator(s.begin(), s.end(), re);\n        for (auto i = beg; i != std::sregex_iterator(); ++i) {\n          const auto &m = *i;\n          auto key = s.substr(static_cast<size_t>(m.position(1)),\n                              static_cast<size_t>(m.length(1)));\n          auto val = m.length(2) > 0\n                         ? s.substr(static_cast<size_t>(m.position(2)),\n                                    static_cast<size_t>(m.length(2)))\n                         : s.substr(static_cast<size_t>(m.position(3)),\n                                    static_cast<size_t>(m.length(3)));\n          auth[key] = val;\n        }\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\nclass ContentProviderAdapter {\npublic:\n  explicit ContentProviderAdapter(\n      ContentProviderWithoutLength &&content_provider)\n      : content_provider_(content_provider) {}\n\n  bool operator()(size_t offset, size_t, DataSink &sink) {\n    return content_provider_(offset, sink);\n  }\n\nprivate:\n  ContentProviderWithoutLength content_provider_;\n};\n\n} // namespace detail\n\ninline std::string hosted_at(const std::string &hostname) {\n  std::vector<std::string> addrs;\n  hosted_at(hostname, addrs);\n  if (addrs.empty()) { return std::string(); }\n  return addrs[0];\n}\n\ninline void hosted_at(const std::string &hostname,\n                      std::vector<std::string> &addrs) {\n  struct addrinfo hints;\n  struct addrinfo *result;\n\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = 0;\n\n  if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {\n#if defined __linux__ && !defined __ANDROID__\n    res_init();\n#endif\n    return;\n  }\n  auto se = detail::scope_exit([&] { freeaddrinfo(result); });\n\n  for (auto rp = result; rp; rp = rp->ai_next) {\n    const auto &addr =\n        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);\n    std::string ip;\n    auto dummy = -1;\n    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,\n                                dummy)) {\n      addrs.push_back(ip);\n    }\n  }\n}\n\ninline std::string append_query_params(const std::string &path,\n                                       const Params &params) {\n  std::string path_with_query = path;\n  const static std::regex re(\"[^?]+\\\\?.*\");\n  auto delm = std::regex_match(path, re) ? '&' : '?';\n  path_with_query += delm + detail::params_to_query_str(params);\n  return path_with_query;\n}\n\n// Header utilities\ninline std::pair<std::string, std::string>\nmake_range_header(const Ranges &ranges) {\n  std::string field = \"bytes=\";\n  auto i = 0;\n  for (const auto &r : ranges) {\n    if (i != 0) { field += \", \"; }\n    if (r.first != -1) { field += std::to_string(r.first); }\n    field += '-';\n    if (r.second != -1) { field += std::to_string(r.second); }\n    i++;\n  }\n  return std::make_pair(\"Range\", std::move(field));\n}\n\ninline std::pair<std::string, std::string>\nmake_basic_authentication_header(const std::string &username,\n                                 const std::string &password, bool is_proxy) {\n  auto field = \"Basic \" + detail::base64_encode(username + \":\" + password);\n  auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n  return std::make_pair(key, std::move(field));\n}\n\ninline std::pair<std::string, std::string>\nmake_bearer_token_authentication_header(const std::string &token,\n                                        bool is_proxy = false) {\n  auto field = \"Bearer \" + token;\n  auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n  return std::make_pair(key, std::move(field));\n}\n\n// Request implementation\ninline bool Request::has_header(const std::string &key) const {\n  return detail::has_header(headers, key);\n}\n\ninline std::string Request::get_header_value(const std::string &key,\n                                             const char *def, size_t id) const {\n  return detail::get_header_value(headers, key, def, id);\n}\n\ninline size_t Request::get_header_value_count(const std::string &key) const {\n  auto r = headers.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Request::set_header(const std::string &key,\n                                const std::string &val) {\n  if (!detail::has_crlf(key) && !detail::has_crlf(val)) {\n    headers.emplace(key, val);\n  }\n}\n\ninline bool Request::has_param(const std::string &key) const {\n  return params.find(key) != params.end();\n}\n\ninline std::string Request::get_param_value(const std::string &key,\n                                            size_t id) const {\n  auto rng = params.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second; }\n  return std::string();\n}\n\ninline size_t Request::get_param_value_count(const std::string &key) const {\n  auto r = params.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline bool Request::is_multipart_form_data() const {\n  const auto &content_type = get_header_value(\"Content-Type\");\n  return !content_type.rfind(\"multipart/form-data\", 0);\n}\n\ninline bool Request::has_file(const std::string &key) const {\n  return files.find(key) != files.end();\n}\n\ninline MultipartFormData Request::get_file_value(const std::string &key) const {\n  auto it = files.find(key);\n  if (it != files.end()) { return it->second; }\n  return MultipartFormData();\n}\n\ninline std::vector<MultipartFormData>\nRequest::get_file_values(const std::string &key) const {\n  std::vector<MultipartFormData> values;\n  auto rng = files.equal_range(key);\n  for (auto it = rng.first; it != rng.second; it++) {\n    values.push_back(it->second);\n  }\n  return values;\n}\n\n// Response implementation\ninline bool Response::has_header(const std::string &key) const {\n  return headers.find(key) != headers.end();\n}\n\ninline std::string Response::get_header_value(const std::string &key,\n                                              const char *def,\n                                              size_t id) const {\n  return detail::get_header_value(headers, key, def, id);\n}\n\ninline size_t Response::get_header_value_count(const std::string &key) const {\n  auto r = headers.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Response::set_header(const std::string &key,\n                                 const std::string &val) {\n  if (!detail::has_crlf(key) && !detail::has_crlf(val)) {\n    headers.emplace(key, val);\n  }\n}\n\ninline void Response::set_redirect(const std::string &url, int stat) {\n  if (!detail::has_crlf(url)) {\n    set_header(\"Location\", url);\n    if (300 <= stat && stat < 400) {\n      this->status = stat;\n    } else {\n      this->status = StatusCode::Found_302;\n    }\n  }\n}\n\ninline void Response::set_content(const char *s, size_t n,\n                                  const std::string &content_type) {\n  body.assign(s, n);\n\n  auto rng = headers.equal_range(\"Content-Type\");\n  headers.erase(rng.first, rng.second);\n  set_header(\"Content-Type\", content_type);\n}\n\ninline void Response::set_content(const std::string &s,\n                                  const std::string &content_type) {\n  set_content(s.data(), s.size(), content_type);\n}\n\ninline void Response::set_content(std::string &&s,\n                                  const std::string &content_type) {\n  body = std::move(s);\n\n  auto rng = headers.equal_range(\"Content-Type\");\n  headers.erase(rng.first, rng.second);\n  set_header(\"Content-Type\", content_type);\n}\n\ninline void Response::set_content_provider(\n    size_t in_length, const std::string &content_type, ContentProvider provider,\n    ContentProviderResourceReleaser resource_releaser) {\n  set_header(\"Content-Type\", content_type);\n  content_length_ = in_length;\n  if (in_length > 0) { content_provider_ = std::move(provider); }\n  content_provider_resource_releaser_ = std::move(resource_releaser);\n  is_chunked_content_provider_ = false;\n}\n\ninline void Response::set_content_provider(\n    const std::string &content_type, ContentProviderWithoutLength provider,\n    ContentProviderResourceReleaser resource_releaser) {\n  set_header(\"Content-Type\", content_type);\n  content_length_ = 0;\n  content_provider_ = detail::ContentProviderAdapter(std::move(provider));\n  content_provider_resource_releaser_ = std::move(resource_releaser);\n  is_chunked_content_provider_ = false;\n}\n\ninline void Response::set_chunked_content_provider(\n    const std::string &content_type, ContentProviderWithoutLength provider,\n    ContentProviderResourceReleaser resource_releaser) {\n  set_header(\"Content-Type\", content_type);\n  content_length_ = 0;\n  content_provider_ = detail::ContentProviderAdapter(std::move(provider));\n  content_provider_resource_releaser_ = std::move(resource_releaser);\n  is_chunked_content_provider_ = true;\n}\n\ninline void Response::set_file_content(const std::string &path,\n                                       const std::string &content_type) {\n  file_content_path_ = path;\n  file_content_content_type_ = content_type;\n}\n\ninline void Response::set_file_content(const std::string &path) {\n  file_content_path_ = path;\n}\n\n// Result implementation\ninline bool Result::has_request_header(const std::string &key) const {\n  return request_headers_.find(key) != request_headers_.end();\n}\n\ninline std::string Result::get_request_header_value(const std::string &key,\n                                                    const char *def,\n                                                    size_t id) const {\n  return detail::get_header_value(request_headers_, key, def, id);\n}\n\ninline size_t\nResult::get_request_header_value_count(const std::string &key) const {\n  auto r = request_headers_.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\n// Stream implementation\ninline ssize_t Stream::write(const char *ptr) {\n  return write(ptr, strlen(ptr));\n}\n\ninline ssize_t Stream::write(const std::string &s) {\n  return write(s.data(), s.size());\n}\n\nnamespace detail {\n\n// Socket stream implementation\ninline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,\n                                  time_t read_timeout_usec,\n                                  time_t write_timeout_sec,\n                                  time_t write_timeout_usec)\n    : sock_(sock), read_timeout_sec_(read_timeout_sec),\n      read_timeout_usec_(read_timeout_usec),\n      write_timeout_sec_(write_timeout_sec),\n      write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}\n\ninline SocketStream::~SocketStream() = default;\n\ninline bool SocketStream::is_readable() const {\n  return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;\n}\n\ninline bool SocketStream::is_writable() const {\n  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&\n         is_socket_alive(sock_);\n}\n\ninline ssize_t SocketStream::read(char *ptr, size_t size) {\n#ifdef _WIN32\n  size =\n      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));\n#else\n  size = (std::min)(size,\n                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));\n#endif\n\n  if (read_buff_off_ < read_buff_content_size_) {\n    auto remaining_size = read_buff_content_size_ - read_buff_off_;\n    if (size <= remaining_size) {\n      memcpy(ptr, read_buff_.data() + read_buff_off_, size);\n      read_buff_off_ += size;\n      return static_cast<ssize_t>(size);\n    } else {\n      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);\n      read_buff_off_ += remaining_size;\n      return static_cast<ssize_t>(remaining_size);\n    }\n  }\n\n  if (!is_readable()) { return -1; }\n\n  read_buff_off_ = 0;\n  read_buff_content_size_ = 0;\n\n  if (size < read_buff_size_) {\n    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,\n                         CPPHTTPLIB_RECV_FLAGS);\n    if (n <= 0) {\n      return n;\n    } else if (n <= static_cast<ssize_t>(size)) {\n      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));\n      return n;\n    } else {\n      memcpy(ptr, read_buff_.data(), size);\n      read_buff_off_ = size;\n      read_buff_content_size_ = static_cast<size_t>(n);\n      return static_cast<ssize_t>(size);\n    }\n  } else {\n    return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);\n  }\n}\n\ninline ssize_t SocketStream::write(const char *ptr, size_t size) {\n  if (!is_writable()) { return -1; }\n\n#if defined(_WIN32) && !defined(_WIN64)\n  size =\n      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));\n#endif\n\n  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);\n}\n\ninline void SocketStream::get_remote_ip_and_port(std::string &ip,\n                                                 int &port) const {\n  return detail::get_remote_ip_and_port(sock_, ip, port);\n}\n\ninline void SocketStream::get_local_ip_and_port(std::string &ip,\n                                                int &port) const {\n  return detail::get_local_ip_and_port(sock_, ip, port);\n}\n\ninline socket_t SocketStream::socket() const { return sock_; }\n\n// Buffer stream implementation\ninline bool BufferStream::is_readable() const { return true; }\n\ninline bool BufferStream::is_writable() const { return true; }\n\ninline ssize_t BufferStream::read(char *ptr, size_t size) {\n#if defined(_MSC_VER) && _MSC_VER < 1910\n  auto len_read = buffer._Copy_s(ptr, size, size, position);\n#else\n  auto len_read = buffer.copy(ptr, size, position);\n#endif\n  position += static_cast<size_t>(len_read);\n  return static_cast<ssize_t>(len_read);\n}\n\ninline ssize_t BufferStream::write(const char *ptr, size_t size) {\n  buffer.append(ptr, size);\n  return static_cast<ssize_t>(size);\n}\n\ninline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,\n                                                 int & /*port*/) const {}\n\ninline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,\n                                                int & /*port*/) const {}\n\ninline socket_t BufferStream::socket() const { return 0; }\n\ninline const std::string &BufferStream::get_buffer() const { return buffer; }\n\ninline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {\n  static constexpr char marker[] = \"/:\";\n\n  // One past the last ending position of a path param substring\n  std::size_t last_param_end = 0;\n\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n  // Needed to ensure that parameter names are unique during matcher\n  // construction\n  // If exceptions are disabled, only last duplicate path\n  // parameter will be set\n  std::unordered_set<std::string> param_name_set;\n#endif\n\n  while (true) {\n    const auto marker_pos = pattern.find(\n        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);\n    if (marker_pos == std::string::npos) { break; }\n\n    static_fragments_.push_back(\n        pattern.substr(last_param_end, marker_pos - last_param_end + 1));\n\n    const auto param_name_start = marker_pos + 2;\n\n    auto sep_pos = pattern.find(separator, param_name_start);\n    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }\n\n    auto param_name =\n        pattern.substr(param_name_start, sep_pos - param_name_start);\n\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n    if (param_name_set.find(param_name) != param_name_set.cend()) {\n      std::string msg = \"Encountered path parameter '\" + param_name +\n                        \"' multiple times in route pattern '\" + pattern + \"'.\";\n      throw std::invalid_argument(msg);\n    }\n#endif\n\n    param_names_.push_back(std::move(param_name));\n\n    last_param_end = sep_pos + 1;\n  }\n\n  if (last_param_end < pattern.length()) {\n    static_fragments_.push_back(pattern.substr(last_param_end));\n  }\n}\n\ninline bool PathParamsMatcher::match(Request &request) const {\n  request.matches = std::smatch();\n  request.path_params.clear();\n  request.path_params.reserve(param_names_.size());\n\n  // One past the position at which the path matched the pattern last time\n  std::size_t starting_pos = 0;\n  for (size_t i = 0; i < static_fragments_.size(); ++i) {\n    const auto &fragment = static_fragments_[i];\n\n    if (starting_pos + fragment.length() > request.path.length()) {\n      return false;\n    }\n\n    // Avoid unnecessary allocation by using strncmp instead of substr +\n    // comparison\n    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),\n                     fragment.length()) != 0) {\n      return false;\n    }\n\n    starting_pos += fragment.length();\n\n    // Should only happen when we have a static fragment after a param\n    // Example: '/users/:id/subscriptions'\n    // The 'subscriptions' fragment here does not have a corresponding param\n    if (i >= param_names_.size()) { continue; }\n\n    auto sep_pos = request.path.find(separator, starting_pos);\n    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }\n\n    const auto &param_name = param_names_[i];\n\n    request.path_params.emplace(\n        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));\n\n    // Mark everything up to '/' as matched\n    starting_pos = sep_pos + 1;\n  }\n  // Returns false if the path is longer than the pattern\n  return starting_pos >= request.path.length();\n}\n\ninline bool RegexMatcher::match(Request &request) const {\n  request.path_params.clear();\n  return std::regex_match(request.path, request.matches, regex_);\n}\n\n} // namespace detail\n\n// HTTP server implementation\ninline Server::Server()\n    : new_task_queue(\n          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {\n#ifndef _WIN32\n  signal(SIGPIPE, SIG_IGN);\n#endif\n}\n\ninline Server::~Server() = default;\n\ninline std::unique_ptr<detail::MatcherBase>\nServer::make_matcher(const std::string &pattern) {\n  if (pattern.find(\"/:\") != std::string::npos) {\n    return detail::make_unique<detail::PathParamsMatcher>(pattern);\n  } else {\n    return detail::make_unique<detail::RegexMatcher>(pattern);\n  }\n}\n\ninline Server &Server::Get(const std::string &pattern, Handler handler) {\n  get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Post(const std::string &pattern, Handler handler) {\n  post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Post(const std::string &pattern,\n                            HandlerWithContentReader handler) {\n  post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                 std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Put(const std::string &pattern, Handler handler) {\n  put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Put(const std::string &pattern,\n                           HandlerWithContentReader handler) {\n  put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Patch(const std::string &pattern, Handler handler) {\n  patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Patch(const std::string &pattern,\n                             HandlerWithContentReader handler) {\n  patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                  std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Delete(const std::string &pattern, Handler handler) {\n  delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Delete(const std::string &pattern,\n                              HandlerWithContentReader handler) {\n  delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                   std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Options(const std::string &pattern, Handler handler) {\n  options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline bool Server::set_base_dir(const std::string &dir,\n                                 const std::string &mount_point) {\n  return set_mount_point(mount_point, dir);\n}\n\ninline bool Server::set_mount_point(const std::string &mount_point,\n                                    const std::string &dir, Headers headers) {\n  detail::FileStat stat(dir);\n  if (stat.is_dir()) {\n    std::string mnt = !mount_point.empty() ? mount_point : \"/\";\n    if (!mnt.empty() && mnt[0] == '/') {\n      base_dirs_.push_back({mnt, dir, std::move(headers)});\n      return true;\n    }\n  }\n  return false;\n}\n\ninline bool Server::remove_mount_point(const std::string &mount_point) {\n  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {\n    if (it->mount_point == mount_point) {\n      base_dirs_.erase(it);\n      return true;\n    }\n  }\n  return false;\n}\n\ninline Server &\nServer::set_file_extension_and_mimetype_mapping(const std::string &ext,\n                                                const std::string &mime) {\n  file_extension_and_mimetype_map_[ext] = mime;\n  return *this;\n}\n\ninline Server &Server::set_default_file_mimetype(const std::string &mime) {\n  default_file_mimetype_ = mime;\n  return *this;\n}\n\ninline Server &Server::set_file_request_handler(Handler handler) {\n  file_request_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_error_handler_core(HandlerWithResponse handler,\n                                              std::true_type) {\n  error_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_error_handler_core(Handler handler,\n                                              std::false_type) {\n  error_handler_ = [handler](const Request &req, Response &res) {\n    handler(req, res);\n    return HandlerResponse::Handled;\n  };\n  return *this;\n}\n\ninline Server &Server::set_exception_handler(ExceptionHandler handler) {\n  exception_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {\n  pre_routing_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_post_routing_handler(Handler handler) {\n  post_routing_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_logger(Logger logger) {\n  logger_ = std::move(logger);\n  return *this;\n}\n\ninline Server &\nServer::set_expect_100_continue_handler(Expect100ContinueHandler handler) {\n  expect_100_continue_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_address_family(int family) {\n  address_family_ = family;\n  return *this;\n}\n\ninline Server &Server::set_tcp_nodelay(bool on) {\n  tcp_nodelay_ = on;\n  return *this;\n}\n\ninline Server &Server::set_ipv6_v6only(bool on) {\n  ipv6_v6only_ = on;\n  return *this;\n}\n\ninline Server &Server::set_socket_options(SocketOptions socket_options) {\n  socket_options_ = std::move(socket_options);\n  return *this;\n}\n\ninline Server &Server::set_default_headers(Headers headers) {\n  default_headers_ = std::move(headers);\n  return *this;\n}\n\ninline Server &Server::set_header_writer(\n    std::function<ssize_t(Stream &, Headers &)> const &writer) {\n  header_writer_ = writer;\n  return *this;\n}\n\ninline Server &Server::set_keep_alive_max_count(size_t count) {\n  keep_alive_max_count_ = count;\n  return *this;\n}\n\ninline Server &Server::set_keep_alive_timeout(time_t sec) {\n  keep_alive_timeout_sec_ = sec;\n  return *this;\n}\n\ninline Server &Server::set_read_timeout(time_t sec, time_t usec) {\n  read_timeout_sec_ = sec;\n  read_timeout_usec_ = usec;\n  return *this;\n}\n\ninline Server &Server::set_write_timeout(time_t sec, time_t usec) {\n  write_timeout_sec_ = sec;\n  write_timeout_usec_ = usec;\n  return *this;\n}\n\ninline Server &Server::set_idle_interval(time_t sec, time_t usec) {\n  idle_interval_sec_ = sec;\n  idle_interval_usec_ = usec;\n  return *this;\n}\n\ninline Server &Server::set_payload_max_length(size_t length) {\n  payload_max_length_ = length;\n  return *this;\n}\n\ninline bool Server::bind_to_port(const std::string &host, int port,\n                                 int socket_flags) {\n  auto ret = bind_internal(host, port, socket_flags);\n  if (ret == -1) { is_decommisioned = true; }\n  return ret >= 0;\n}\ninline int Server::bind_to_any_port(const std::string &host, int socket_flags) {\n  auto ret = bind_internal(host, 0, socket_flags);\n  if (ret == -1) { is_decommisioned = true; }\n  return ret;\n}\n\ninline bool Server::listen_after_bind() { return listen_internal(); }\n\ninline bool Server::listen(const std::string &host, int port,\n                           int socket_flags) {\n  return bind_to_port(host, port, socket_flags) && listen_internal();\n}\n\ninline bool Server::is_running() const { return is_running_; }\n\ninline void Server::wait_until_ready() const {\n  while (!is_running_ && !is_decommisioned) {\n    std::this_thread::sleep_for(std::chrono::milliseconds{1});\n  }\n}\n\ninline void Server::stop() {\n  if (is_running_) {\n    assert(svr_sock_ != INVALID_SOCKET);\n    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));\n    detail::shutdown_socket(sock);\n    detail::close_socket(sock);\n  }\n  is_decommisioned = false;\n}\n\ninline void Server::decommission() { is_decommisioned = true; }\n\ninline bool Server::parse_request_line(const char *s, Request &req) const {\n  auto len = strlen(s);\n  if (len < 2 || s[len - 2] != '\\r' || s[len - 1] != '\\n') { return false; }\n  len -= 2;\n\n  {\n    size_t count = 0;\n\n    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {\n      switch (count) {\n      case 0: req.method = std::string(b, e); break;\n      case 1: req.target = std::string(b, e); break;\n      case 2: req.version = std::string(b, e); break;\n      default: break;\n      }\n      count++;\n    });\n\n    if (count != 3) { return false; }\n  }\n\n  static const std::set<std::string> methods{\n      \"GET\",     \"HEAD\",    \"POST\",  \"PUT\",   \"DELETE\",\n      \"CONNECT\", \"OPTIONS\", \"TRACE\", \"PATCH\", \"PRI\"};\n\n  if (methods.find(req.method) == methods.end()) { return false; }\n\n  if (req.version != \"HTTP/1.1\" && req.version != \"HTTP/1.0\") { return false; }\n\n  {\n    // Skip URL fragment\n    for (size_t i = 0; i < req.target.size(); i++) {\n      if (req.target[i] == '#') {\n        req.target.erase(i);\n        break;\n      }\n    }\n\n    detail::divide(req.target, '?',\n                   [&](const char *lhs_data, std::size_t lhs_size,\n                       const char *rhs_data, std::size_t rhs_size) {\n                     req.path = detail::decode_url(\n                         std::string(lhs_data, lhs_size), false);\n                     detail::parse_query_text(rhs_data, rhs_size, req.params);\n                   });\n  }\n\n  return true;\n}\n\ninline bool Server::write_response(Stream &strm, bool close_connection,\n                                   Request &req, Response &res) {\n  // NOTE: `req.ranges` should be empty, otherwise it will be applied\n  // incorrectly to the error content.\n  req.ranges.clear();\n  return write_response_core(strm, close_connection, req, res, false);\n}\n\ninline bool Server::write_response_with_content(Stream &strm,\n                                                bool close_connection,\n                                                const Request &req,\n                                                Response &res) {\n  return write_response_core(strm, close_connection, req, res, true);\n}\n\ninline bool Server::write_response_core(Stream &strm, bool close_connection,\n                                        const Request &req, Response &res,\n                                        bool need_apply_ranges) {\n  assert(res.status != -1);\n\n  if (400 <= res.status && error_handler_ &&\n      error_handler_(req, res) == HandlerResponse::Handled) {\n    need_apply_ranges = true;\n  }\n\n  std::string content_type;\n  std::string boundary;\n  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }\n\n  // Prepare additional headers\n  if (close_connection || req.get_header_value(\"Connection\") == \"close\") {\n    res.set_header(\"Connection\", \"close\");\n  } else {\n    std::string s = \"timeout=\";\n    s += std::to_string(keep_alive_timeout_sec_);\n    s += \", max=\";\n    s += std::to_string(keep_alive_max_count_);\n    res.set_header(\"Keep-Alive\", s);\n  }\n\n  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&\n      !res.has_header(\"Content-Type\")) {\n    res.set_header(\"Content-Type\", \"text/plain\");\n  }\n\n  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&\n      !res.has_header(\"Content-Length\")) {\n    res.set_header(\"Content-Length\", \"0\");\n  }\n\n  if (req.method == \"HEAD\" && !res.has_header(\"Accept-Ranges\")) {\n    res.set_header(\"Accept-Ranges\", \"bytes\");\n  }\n\n  if (post_routing_handler_) { post_routing_handler_(req, res); }\n\n  // Response line and headers\n  {\n    detail::BufferStream bstrm;\n    if (!detail::write_response_line(bstrm, res.status)) { return false; }\n    if (!header_writer_(bstrm, res.headers)) { return false; }\n\n    // Flush buffer\n    auto &data = bstrm.get_buffer();\n    detail::write_data(strm, data.data(), data.size());\n  }\n\n  // Body\n  auto ret = true;\n  if (req.method != \"HEAD\") {\n    if (!res.body.empty()) {\n      if (!detail::write_data(strm, res.body.data(), res.body.size())) {\n        ret = false;\n      }\n    } else if (res.content_provider_) {\n      if (write_content_with_provider(strm, req, res, boundary, content_type)) {\n        res.content_provider_success_ = true;\n      } else {\n        ret = false;\n      }\n    }\n  }\n\n  // Log\n  if (logger_) { logger_(req, res); }\n\n  return ret;\n}\n\ninline bool\nServer::write_content_with_provider(Stream &strm, const Request &req,\n                                    Response &res, const std::string &boundary,\n                                    const std::string &content_type) {\n  auto is_shutting_down = [this]() {\n    return this->svr_sock_ == INVALID_SOCKET;\n  };\n\n  if (res.content_length_ > 0) {\n    if (req.ranges.empty()) {\n      return detail::write_content(strm, res.content_provider_, 0,\n                                   res.content_length_, is_shutting_down);\n    } else if (req.ranges.size() == 1) {\n      auto offset_and_length = detail::get_range_offset_and_length(\n          req.ranges[0], res.content_length_);\n\n      return detail::write_content(strm, res.content_provider_,\n                                   offset_and_length.first,\n                                   offset_and_length.second, is_shutting_down);\n    } else {\n      return detail::write_multipart_ranges_data(\n          strm, req, res, boundary, content_type, res.content_length_,\n          is_shutting_down);\n    }\n  } else {\n    if (res.is_chunked_content_provider_) {\n      auto type = detail::encoding_type(req, res);\n\n      std::unique_ptr<detail::compressor> compressor;\n      if (type == detail::EncodingType::Gzip) {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n        compressor = detail::make_unique<detail::gzip_compressor>();\n#endif\n      } else if (type == detail::EncodingType::Brotli) {\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n        compressor = detail::make_unique<detail::brotli_compressor>();\n#endif\n      } else {\n        compressor = detail::make_unique<detail::nocompressor>();\n      }\n      assert(compressor != nullptr);\n\n      return detail::write_content_chunked(strm, res.content_provider_,\n                                           is_shutting_down, *compressor);\n    } else {\n      return detail::write_content_without_length(strm, res.content_provider_,\n                                                  is_shutting_down);\n    }\n  }\n}\n\ninline bool Server::read_content(Stream &strm, Request &req, Response &res) {\n  MultipartFormDataMap::iterator cur;\n  auto file_count = 0;\n  if (read_content_core(\n          strm, req, res,\n          // Regular\n          [&](const char *buf, size_t n) {\n            if (req.body.size() + n > req.body.max_size()) { return false; }\n            req.body.append(buf, n);\n            return true;\n          },\n          // Multipart\n          [&](const MultipartFormData &file) {\n            if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {\n              return false;\n            }\n            cur = req.files.emplace(file.name, file);\n            return true;\n          },\n          [&](const char *buf, size_t n) {\n            auto &content = cur->second.content;\n            if (content.size() + n > content.max_size()) { return false; }\n            content.append(buf, n);\n            return true;\n          })) {\n    const auto &content_type = req.get_header_value(\"Content-Type\");\n    if (!content_type.find(\"application/x-www-form-urlencoded\")) {\n      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {\n        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?\n        return false;\n      }\n      detail::parse_query_text(req.body, req.params);\n    }\n    return true;\n  }\n  return false;\n}\n\ninline bool Server::read_content_with_content_receiver(\n    Stream &strm, Request &req, Response &res, ContentReceiver receiver,\n    MultipartContentHeader multipart_header,\n    ContentReceiver multipart_receiver) {\n  return read_content_core(strm, req, res, std::move(receiver),\n                           std::move(multipart_header),\n                           std::move(multipart_receiver));\n}\n\ninline bool\nServer::read_content_core(Stream &strm, Request &req, Response &res,\n                          ContentReceiver receiver,\n                          MultipartContentHeader multipart_header,\n                          ContentReceiver multipart_receiver) const {\n  detail::MultipartFormDataParser multipart_form_data_parser;\n  ContentReceiverWithProgress out;\n\n  if (req.is_multipart_form_data()) {\n    const auto &content_type = req.get_header_value(\"Content-Type\");\n    std::string boundary;\n    if (!detail::parse_multipart_boundary(content_type, boundary)) {\n      res.status = StatusCode::BadRequest_400;\n      return false;\n    }\n\n    multipart_form_data_parser.set_boundary(std::move(boundary));\n    out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {\n      /* For debug\n      size_t pos = 0;\n      while (pos < n) {\n        auto read_size = (std::min)<size_t>(1, n - pos);\n        auto ret = multipart_form_data_parser.parse(\n            buf + pos, read_size, multipart_receiver, multipart_header);\n        if (!ret) { return false; }\n        pos += read_size;\n      }\n      return true;\n      */\n      return multipart_form_data_parser.parse(buf, n, multipart_receiver,\n                                              multipart_header);\n    };\n  } else {\n    out = [receiver](const char *buf, size_t n, uint64_t /*off*/,\n                     uint64_t /*len*/) { return receiver(buf, n); };\n  }\n\n  if (req.method == \"DELETE\" && !req.has_header(\"Content-Length\")) {\n    return true;\n  }\n\n  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,\n                            out, true)) {\n    return false;\n  }\n\n  if (req.is_multipart_form_data()) {\n    if (!multipart_form_data_parser.is_valid()) {\n      res.status = StatusCode::BadRequest_400;\n      return false;\n    }\n  }\n\n  return true;\n}\n\ninline bool Server::handle_file_request(const Request &req, Response &res,\n                                        bool head) {\n  for (const auto &entry : base_dirs_) {\n    // Prefix match\n    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {\n      std::string sub_path = \"/\" + req.path.substr(entry.mount_point.size());\n      if (detail::is_valid_path(sub_path)) {\n        auto path = entry.base_dir + sub_path;\n        if (path.back() == '/') { path += \"index.html\"; }\n\n        detail::FileStat stat(path);\n\n        if (stat.is_dir()) {\n          res.set_redirect(sub_path + \"/\", StatusCode::MovedPermanently_301);\n          return true;\n        }\n\n        if (stat.is_file()) {\n          for (const auto &kv : entry.headers) {\n            res.set_header(kv.first, kv.second);\n          }\n\n          auto mm = std::make_shared<detail::mmap>(path.c_str());\n          if (!mm->is_open()) { return false; }\n\n          res.set_content_provider(\n              mm->size(),\n              detail::find_content_type(path, file_extension_and_mimetype_map_,\n                                        default_file_mimetype_),\n              [mm](size_t offset, size_t length, DataSink &sink) -> bool {\n                sink.write(mm->data() + offset, length);\n                return true;\n              });\n\n          if (!head && file_request_handler_) {\n            file_request_handler_(req, res);\n          }\n\n          return true;\n        }\n      }\n    }\n  }\n  return false;\n}\n\ninline socket_t\nServer::create_server_socket(const std::string &host, int port,\n                             int socket_flags,\n                             SocketOptions socket_options) const {\n  return detail::create_socket(\n      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,\n      ipv6_v6only_, std::move(socket_options),\n      [](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {\n        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {\n          return false;\n        }\n        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }\n        return true;\n      });\n}\n\ninline int Server::bind_internal(const std::string &host, int port,\n                                 int socket_flags) {\n  if (is_decommisioned) { return -1; }\n\n  if (!is_valid()) { return -1; }\n\n  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);\n  if (svr_sock_ == INVALID_SOCKET) { return -1; }\n\n  if (port == 0) {\n    struct sockaddr_storage addr;\n    socklen_t addr_len = sizeof(addr);\n    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),\n                    &addr_len) == -1) {\n      return -1;\n    }\n    if (addr.ss_family == AF_INET) {\n      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);\n    } else if (addr.ss_family == AF_INET6) {\n      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);\n    } else {\n      return -1;\n    }\n  } else {\n    return port;\n  }\n}\n\ninline bool Server::listen_internal() {\n  if (is_decommisioned) { return false; }\n\n  auto ret = true;\n  is_running_ = true;\n  auto se = detail::scope_exit([&]() { is_running_ = false; });\n\n  {\n    std::unique_ptr<TaskQueue> task_queue(new_task_queue());\n\n    while (svr_sock_ != INVALID_SOCKET) {\n#ifndef _WIN32\n      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {\n#endif\n        auto val = detail::select_read(svr_sock_, idle_interval_sec_,\n                                       idle_interval_usec_);\n        if (val == 0) { // Timeout\n          task_queue->on_idle();\n          continue;\n        }\n#ifndef _WIN32\n      }\n#endif\n\n#if defined _WIN32\n      // sockets conneced via WASAccept inherit flags NO_HANDLE_INHERIT,\n      // OVERLAPPED\n      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);\n#elif defined SOCK_CLOEXEC\n      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);\n#else\n      socket_t sock = accept(svr_sock_, nullptr, nullptr);\n#endif\n\n      if (sock == INVALID_SOCKET) {\n        if (errno == EMFILE) {\n          // The per-process limit of open file descriptors has been reached.\n          // Try to accept new connections after a short sleep.\n          std::this_thread::sleep_for(std::chrono::microseconds{1});\n          continue;\n        } else if (errno == EINTR || errno == EAGAIN) {\n          continue;\n        }\n        if (svr_sock_ != INVALID_SOCKET) {\n          detail::close_socket(svr_sock_);\n          ret = false;\n        } else {\n          ; // The server socket was closed by user.\n        }\n        break;\n      }\n\n      {\n#ifdef _WIN32\n        auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 +\n                                             read_timeout_usec_ / 1000);\n        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,\n                   reinterpret_cast<const char *>(&timeout), sizeof(timeout));\n#else\n        timeval tv;\n        tv.tv_sec = static_cast<long>(read_timeout_sec_);\n        tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);\n        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,\n                   reinterpret_cast<const void *>(&tv), sizeof(tv));\n#endif\n      }\n      {\n\n#ifdef _WIN32\n        auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 +\n                                             write_timeout_usec_ / 1000);\n        setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,\n                   reinterpret_cast<const char *>(&timeout), sizeof(timeout));\n#else\n        timeval tv;\n        tv.tv_sec = static_cast<long>(write_timeout_sec_);\n        tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);\n        setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,\n                   reinterpret_cast<const void *>(&tv), sizeof(tv));\n#endif\n      }\n\n      if (!task_queue->enqueue(\n              [this, sock]() { process_and_close_socket(sock); })) {\n        detail::shutdown_socket(sock);\n        detail::close_socket(sock);\n      }\n    }\n\n    task_queue->shutdown();\n  }\n\n  is_decommisioned = !ret;\n  return ret;\n}\n\ninline bool Server::routing(Request &req, Response &res, Stream &strm) {\n  if (pre_routing_handler_ &&\n      pre_routing_handler_(req, res) == HandlerResponse::Handled) {\n    return true;\n  }\n\n  // File handler\n  auto is_head_request = req.method == \"HEAD\";\n  if ((req.method == \"GET\" || is_head_request) &&\n      handle_file_request(req, res, is_head_request)) {\n    return true;\n  }\n\n  if (detail::expect_content(req)) {\n    // Content reader handler\n    {\n      ContentReader reader(\n          [&](ContentReceiver receiver) {\n            return read_content_with_content_receiver(\n                strm, req, res, std::move(receiver), nullptr, nullptr);\n          },\n          [&](MultipartContentHeader header, ContentReceiver receiver) {\n            return read_content_with_content_receiver(strm, req, res, nullptr,\n                                                      std::move(header),\n                                                      std::move(receiver));\n          });\n\n      if (req.method == \"POST\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                post_handlers_for_content_reader_)) {\n          return true;\n        }\n      } else if (req.method == \"PUT\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                put_handlers_for_content_reader_)) {\n          return true;\n        }\n      } else if (req.method == \"PATCH\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                patch_handlers_for_content_reader_)) {\n          return true;\n        }\n      } else if (req.method == \"DELETE\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                delete_handlers_for_content_reader_)) {\n          return true;\n        }\n      }\n    }\n\n    // Read content into `req.body`\n    if (!read_content(strm, req, res)) { return false; }\n  }\n\n  // Regular handler\n  if (req.method == \"GET\" || req.method == \"HEAD\") {\n    return dispatch_request(req, res, get_handlers_);\n  } else if (req.method == \"POST\") {\n    return dispatch_request(req, res, post_handlers_);\n  } else if (req.method == \"PUT\") {\n    return dispatch_request(req, res, put_handlers_);\n  } else if (req.method == \"DELETE\") {\n    return dispatch_request(req, res, delete_handlers_);\n  } else if (req.method == \"OPTIONS\") {\n    return dispatch_request(req, res, options_handlers_);\n  } else if (req.method == \"PATCH\") {\n    return dispatch_request(req, res, patch_handlers_);\n  }\n\n  res.status = StatusCode::BadRequest_400;\n  return false;\n}\n\ninline bool Server::dispatch_request(Request &req, Response &res,\n                                     const Handlers &handlers) const {\n  for (const auto &x : handlers) {\n    const auto &matcher = x.first;\n    const auto &handler = x.second;\n\n    if (matcher->match(req)) {\n      handler(req, res);\n      return true;\n    }\n  }\n  return false;\n}\n\ninline void Server::apply_ranges(const Request &req, Response &res,\n                                 std::string &content_type,\n                                 std::string &boundary) const {\n  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {\n    auto it = res.headers.find(\"Content-Type\");\n    if (it != res.headers.end()) {\n      content_type = it->second;\n      res.headers.erase(it);\n    }\n\n    boundary = detail::make_multipart_data_boundary();\n\n    res.set_header(\"Content-Type\",\n                   \"multipart/byteranges; boundary=\" + boundary);\n  }\n\n  auto type = detail::encoding_type(req, res);\n\n  if (res.body.empty()) {\n    if (res.content_length_ > 0) {\n      size_t length = 0;\n      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {\n        length = res.content_length_;\n      } else if (req.ranges.size() == 1) {\n        auto offset_and_length = detail::get_range_offset_and_length(\n            req.ranges[0], res.content_length_);\n\n        length = offset_and_length.second;\n\n        auto content_range = detail::make_content_range_header_field(\n            offset_and_length, res.content_length_);\n        res.set_header(\"Content-Range\", content_range);\n      } else {\n        length = detail::get_multipart_ranges_data_length(\n            req, boundary, content_type, res.content_length_);\n      }\n      res.set_header(\"Content-Length\", std::to_string(length));\n    } else {\n      if (res.content_provider_) {\n        if (res.is_chunked_content_provider_) {\n          res.set_header(\"Transfer-Encoding\", \"chunked\");\n          if (type == detail::EncodingType::Gzip) {\n            res.set_header(\"Content-Encoding\", \"gzip\");\n          } else if (type == detail::EncodingType::Brotli) {\n            res.set_header(\"Content-Encoding\", \"br\");\n          }\n        }\n      }\n    }\n  } else {\n    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {\n      ;\n    } else if (req.ranges.size() == 1) {\n      auto offset_and_length =\n          detail::get_range_offset_and_length(req.ranges[0], res.body.size());\n      auto offset = offset_and_length.first;\n      auto length = offset_and_length.second;\n\n      auto content_range = detail::make_content_range_header_field(\n          offset_and_length, res.body.size());\n      res.set_header(\"Content-Range\", content_range);\n\n      assert(offset + length <= res.body.size());\n      res.body = res.body.substr(offset, length);\n    } else {\n      std::string data;\n      detail::make_multipart_ranges_data(req, res, boundary, content_type,\n                                         res.body.size(), data);\n      res.body.swap(data);\n    }\n\n    if (type != detail::EncodingType::None) {\n      std::unique_ptr<detail::compressor> compressor;\n      std::string content_encoding;\n\n      if (type == detail::EncodingType::Gzip) {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n        compressor = detail::make_unique<detail::gzip_compressor>();\n        content_encoding = \"gzip\";\n#endif\n      } else if (type == detail::EncodingType::Brotli) {\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n        compressor = detail::make_unique<detail::brotli_compressor>();\n        content_encoding = \"br\";\n#endif\n      }\n\n      if (compressor) {\n        std::string compressed;\n        if (compressor->compress(res.body.data(), res.body.size(), true,\n                                 [&](const char *data, size_t data_len) {\n                                   compressed.append(data, data_len);\n                                   return true;\n                                 })) {\n          res.body.swap(compressed);\n          res.set_header(\"Content-Encoding\", content_encoding);\n        }\n      }\n    }\n\n    auto length = std::to_string(res.body.size());\n    res.set_header(\"Content-Length\", length);\n  }\n}\n\ninline bool Server::dispatch_request_for_content_reader(\n    Request &req, Response &res, ContentReader content_reader,\n    const HandlersForContentReader &handlers) const {\n  for (const auto &x : handlers) {\n    const auto &matcher = x.first;\n    const auto &handler = x.second;\n\n    if (matcher->match(req)) {\n      handler(req, res, content_reader);\n      return true;\n    }\n  }\n  return false;\n}\n\ninline bool\nServer::process_request(Stream &strm, const std::string &remote_addr,\n                        int remote_port, const std::string &local_addr,\n                        int local_port, bool close_connection,\n                        bool &connection_closed,\n                        const std::function<void(Request &)> &setup_request) {\n  std::array<char, 2048> buf{};\n\n  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());\n\n  // Connection has been closed on client\n  if (!line_reader.getline()) { return false; }\n\n  Request req;\n\n  Response res;\n  res.version = \"HTTP/1.1\";\n  res.headers = default_headers_;\n\n#ifdef _WIN32\n  // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).\n#else\n#ifndef CPPHTTPLIB_USE_POLL\n  // Socket file descriptor exceeded FD_SETSIZE...\n  if (strm.socket() >= FD_SETSIZE) {\n    Headers dummy;\n    detail::read_headers(strm, dummy);\n    res.status = StatusCode::InternalServerError_500;\n    return write_response(strm, close_connection, req, res);\n  }\n#endif\n#endif\n\n  // Check if the request URI doesn't exceed the limit\n  if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {\n    Headers dummy;\n    detail::read_headers(strm, dummy);\n    res.status = StatusCode::UriTooLong_414;\n    return write_response(strm, close_connection, req, res);\n  }\n\n  // Request line and headers\n  if (!parse_request_line(line_reader.ptr(), req) ||\n      !detail::read_headers(strm, req.headers)) {\n    res.status = StatusCode::BadRequest_400;\n    return write_response(strm, close_connection, req, res);\n  }\n\n  if (req.get_header_value(\"Connection\") == \"close\") {\n    connection_closed = true;\n  }\n\n  if (req.version == \"HTTP/1.0\" &&\n      req.get_header_value(\"Connection\") != \"Keep-Alive\") {\n    connection_closed = true;\n  }\n\n  req.remote_addr = remote_addr;\n  req.remote_port = remote_port;\n  req.set_header(\"REMOTE_ADDR\", req.remote_addr);\n  req.set_header(\"REMOTE_PORT\", std::to_string(req.remote_port));\n\n  req.local_addr = local_addr;\n  req.local_port = local_port;\n  req.set_header(\"LOCAL_ADDR\", req.local_addr);\n  req.set_header(\"LOCAL_PORT\", std::to_string(req.local_port));\n\n  if (req.has_header(\"Range\")) {\n    const auto &range_header_value = req.get_header_value(\"Range\");\n    if (!detail::parse_range_header(range_header_value, req.ranges)) {\n      res.status = StatusCode::RangeNotSatisfiable_416;\n      return write_response(strm, close_connection, req, res);\n    }\n  }\n\n  if (setup_request) { setup_request(req); }\n\n  if (req.get_header_value(\"Expect\") == \"100-continue\") {\n    int status = StatusCode::Continue_100;\n    if (expect_100_continue_handler_) {\n      status = expect_100_continue_handler_(req, res);\n    }\n    switch (status) {\n    case StatusCode::Continue_100:\n    case StatusCode::ExpectationFailed_417:\n      detail::write_response_line(strm, status);\n      strm.write(\"\\r\\n\");\n      break;\n    default:\n      connection_closed = true;\n      return write_response(strm, true, req, res);\n    }\n  }\n\n  // Routing\n  auto routed = false;\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\n  routed = routing(req, res, strm);\n#else\n  try {\n    routed = routing(req, res, strm);\n  } catch (std::exception &e) {\n    if (exception_handler_) {\n      auto ep = std::current_exception();\n      exception_handler_(req, res, ep);\n      routed = true;\n    } else {\n      res.status = StatusCode::InternalServerError_500;\n      std::string val;\n      auto s = e.what();\n      for (size_t i = 0; s[i]; i++) {\n        switch (s[i]) {\n        case '\\r': val += \"\\\\r\"; break;\n        case '\\n': val += \"\\\\n\"; break;\n        default: val += s[i]; break;\n        }\n      }\n      res.set_header(\"EXCEPTION_WHAT\", val);\n    }\n  } catch (...) {\n    if (exception_handler_) {\n      auto ep = std::current_exception();\n      exception_handler_(req, res, ep);\n      routed = true;\n    } else {\n      res.status = StatusCode::InternalServerError_500;\n      res.set_header(\"EXCEPTION_WHAT\", \"UNKNOWN\");\n    }\n  }\n#endif\n  if (routed) {\n    if (res.status == -1) {\n      res.status = req.ranges.empty() ? StatusCode::OK_200\n                                      : StatusCode::PartialContent_206;\n    }\n\n    if (detail::range_error(req, res)) {\n      res.body.clear();\n      res.content_length_ = 0;\n      res.content_provider_ = nullptr;\n      res.status = StatusCode::RangeNotSatisfiable_416;\n      return write_response(strm, close_connection, req, res);\n    }\n\n    // Serve file content by using a content provider\n    if (!res.file_content_path_.empty()) {\n      const auto &path = res.file_content_path_;\n      auto mm = std::make_shared<detail::mmap>(path.c_str());\n      if (!mm->is_open()) {\n        res.body.clear();\n        res.content_length_ = 0;\n        res.content_provider_ = nullptr;\n        res.status = StatusCode::NotFound_404;\n        return write_response(strm, close_connection, req, res);\n      }\n\n      auto content_type = res.file_content_content_type_;\n      if (content_type.empty()) {\n        content_type = detail::find_content_type(\n            path, file_extension_and_mimetype_map_, default_file_mimetype_);\n      }\n\n      res.set_content_provider(\n          mm->size(), content_type,\n          [mm](size_t offset, size_t length, DataSink &sink) -> bool {\n            sink.write(mm->data() + offset, length);\n            return true;\n          });\n    }\n\n    return write_response_with_content(strm, close_connection, req, res);\n  } else {\n    if (res.status == -1) { res.status = StatusCode::NotFound_404; }\n\n    return write_response(strm, close_connection, req, res);\n  }\n}\n\ninline bool Server::is_valid() const { return true; }\n\ninline bool Server::process_and_close_socket(socket_t sock) {\n  std::string remote_addr;\n  int remote_port = 0;\n  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);\n\n  std::string local_addr;\n  int local_port = 0;\n  detail::get_local_ip_and_port(sock, local_addr, local_port);\n\n  auto ret = detail::process_server_socket(\n      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,\n      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n      write_timeout_usec_,\n      [&](Stream &strm, bool close_connection, bool &connection_closed) {\n        return process_request(strm, remote_addr, remote_port, local_addr,\n                               local_port, close_connection, connection_closed,\n                               nullptr);\n      });\n\n  detail::shutdown_socket(sock);\n  detail::close_socket(sock);\n  return ret;\n}\n\n// HTTP client implementation\ninline ClientImpl::ClientImpl(const std::string &host)\n    : ClientImpl(host, 80, std::string(), std::string()) {}\n\ninline ClientImpl::ClientImpl(const std::string &host, int port)\n    : ClientImpl(host, port, std::string(), std::string()) {}\n\ninline ClientImpl::ClientImpl(const std::string &host, int port,\n                              const std::string &client_cert_path,\n                              const std::string &client_key_path)\n    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),\n      host_and_port_(adjust_host_string(host_) + \":\" + std::to_string(port)),\n      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}\n\ninline ClientImpl::~ClientImpl() {\n  std::lock_guard<std::mutex> guard(socket_mutex_);\n  shutdown_socket(socket_);\n  close_socket(socket_);\n}\n\ninline bool ClientImpl::is_valid() const { return true; }\n\ninline void ClientImpl::copy_settings(const ClientImpl &rhs) {\n  client_cert_path_ = rhs.client_cert_path_;\n  client_key_path_ = rhs.client_key_path_;\n  connection_timeout_sec_ = rhs.connection_timeout_sec_;\n  read_timeout_sec_ = rhs.read_timeout_sec_;\n  read_timeout_usec_ = rhs.read_timeout_usec_;\n  write_timeout_sec_ = rhs.write_timeout_sec_;\n  write_timeout_usec_ = rhs.write_timeout_usec_;\n  basic_auth_username_ = rhs.basic_auth_username_;\n  basic_auth_password_ = rhs.basic_auth_password_;\n  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  digest_auth_username_ = rhs.digest_auth_username_;\n  digest_auth_password_ = rhs.digest_auth_password_;\n#endif\n  keep_alive_ = rhs.keep_alive_;\n  follow_location_ = rhs.follow_location_;\n  url_encode_ = rhs.url_encode_;\n  address_family_ = rhs.address_family_;\n  tcp_nodelay_ = rhs.tcp_nodelay_;\n  ipv6_v6only_ = rhs.ipv6_v6only_;\n  socket_options_ = rhs.socket_options_;\n  compress_ = rhs.compress_;\n  decompress_ = rhs.decompress_;\n  interface_ = rhs.interface_;\n  proxy_host_ = rhs.proxy_host_;\n  proxy_port_ = rhs.proxy_port_;\n  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;\n  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;\n  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;\n  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;\n#endif\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  ca_cert_file_path_ = rhs.ca_cert_file_path_;\n  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;\n  ca_cert_store_ = rhs.ca_cert_store_;\n#endif\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  server_certificate_verification_ = rhs.server_certificate_verification_;\n  server_hostname_verification_ = rhs.server_hostname_verification_;\n  server_certificate_verifier_ = rhs.server_certificate_verifier_;\n#endif\n  logger_ = rhs.logger_;\n}\n\ninline socket_t ClientImpl::create_client_socket(Error &error) const {\n  if (!proxy_host_.empty() && proxy_port_ != -1) {\n    return detail::create_client_socket(\n        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,\n        ipv6_v6only_, socket_options_, connection_timeout_sec_,\n        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,\n        write_timeout_sec_, write_timeout_usec_, interface_, error);\n  }\n\n  // Check is custom IP specified for host_\n  std::string ip;\n  auto it = addr_map_.find(host_);\n  if (it != addr_map_.end()) { ip = it->second; }\n\n  return detail::create_client_socket(\n      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,\n      socket_options_, connection_timeout_sec_, connection_timeout_usec_,\n      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n      write_timeout_usec_, interface_, error);\n}\n\ninline bool ClientImpl::create_and_connect_socket(Socket &socket,\n                                                  Error &error) {\n  auto sock = create_client_socket(error);\n  if (sock == INVALID_SOCKET) { return false; }\n  socket.sock = sock;\n  return true;\n}\n\ninline void ClientImpl::shutdown_ssl(Socket & /*socket*/,\n                                     bool /*shutdown_gracefully*/) {\n  // If there are any requests in flight from threads other than us, then it's\n  // a thread-unsafe race because individual ssl* objects are not thread-safe.\n  assert(socket_requests_in_flight_ == 0 ||\n         socket_requests_are_from_thread_ == std::this_thread::get_id());\n}\n\ninline void ClientImpl::shutdown_socket(Socket &socket) const {\n  if (socket.sock == INVALID_SOCKET) { return; }\n  detail::shutdown_socket(socket.sock);\n}\n\ninline void ClientImpl::close_socket(Socket &socket) {\n  // If there are requests in flight in another thread, usually closing\n  // the socket will be fine and they will simply receive an error when\n  // using the closed socket, but it is still a bug since rarely the OS\n  // may reassign the socket id to be used for a new socket, and then\n  // suddenly they will be operating on a live socket that is different\n  // than the one they intended!\n  assert(socket_requests_in_flight_ == 0 ||\n         socket_requests_are_from_thread_ == std::this_thread::get_id());\n\n  // It is also a bug if this happens while SSL is still active\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  assert(socket.ssl == nullptr);\n#endif\n  if (socket.sock == INVALID_SOCKET) { return; }\n  detail::close_socket(socket.sock);\n  socket.sock = INVALID_SOCKET;\n}\n\ninline bool ClientImpl::read_response_line(Stream &strm, const Request &req,\n                                           Response &res) const {\n  std::array<char, 2048> buf{};\n\n  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());\n\n  if (!line_reader.getline()) { return false; }\n\n#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n  const static std::regex re(\"(HTTP/1\\\\.[01]) (\\\\d{3})(?: (.*?))?\\r?\\n\");\n#else\n  const static std::regex re(\"(HTTP/1\\\\.[01]) (\\\\d{3})(?: (.*?))?\\r\\n\");\n#endif\n\n  std::cmatch m;\n  if (!std::regex_match(line_reader.ptr(), m, re)) {\n    return req.method == \"CONNECT\";\n  }\n  res.version = std::string(m[1]);\n  res.status = std::stoi(std::string(m[2]));\n  res.reason = std::string(m[3]);\n\n  // Ignore '100 Continue'\n  while (res.status == StatusCode::Continue_100) {\n    if (!line_reader.getline()) { return false; } // CRLF\n    if (!line_reader.getline()) { return false; } // next response line\n\n    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }\n    res.version = std::string(m[1]);\n    res.status = std::stoi(std::string(m[2]));\n    res.reason = std::string(m[3]);\n  }\n\n  return true;\n}\n\ninline bool ClientImpl::send(Request &req, Response &res, Error &error) {\n  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);\n  auto ret = send_(req, res, error);\n  if (error == Error::SSLPeerCouldBeClosed_) {\n    assert(!ret);\n    ret = send_(req, res, error);\n  }\n  return ret;\n}\n\ninline bool ClientImpl::send_(Request &req, Response &res, Error &error) {\n  {\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n\n    // Set this to false immediately - if it ever gets set to true by the end of\n    // the request, we know another thread instructed us to close the socket.\n    socket_should_be_closed_when_request_is_done_ = false;\n\n    auto is_alive = false;\n    if (socket_.is_open()) {\n      is_alive = detail::is_socket_alive(socket_.sock);\n      if (!is_alive) {\n        // Attempt to avoid sigpipe by shutting down nongracefully if it seems\n        // like the other side has already closed the connection Also, there\n        // cannot be any requests in flight from other threads since we locked\n        // request_mutex_, so safe to close everything immediately\n        const bool shutdown_gracefully = false;\n        shutdown_ssl(socket_, shutdown_gracefully);\n        shutdown_socket(socket_);\n        close_socket(socket_);\n      }\n    }\n\n    if (!is_alive) {\n      if (!create_and_connect_socket(socket_, error)) { return false; }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n      // TODO: refactoring\n      if (is_ssl()) {\n        auto &scli = static_cast<SSLClient &>(*this);\n        if (!proxy_host_.empty() && proxy_port_ != -1) {\n          auto success = false;\n          if (!scli.connect_with_proxy(socket_, res, success, error)) {\n            return success;\n          }\n        }\n\n        if (!scli.initialize_ssl(socket_, error)) { return false; }\n      }\n#endif\n    }\n\n    // Mark the current socket as being in use so that it cannot be closed by\n    // anyone else while this request is ongoing, even though we will be\n    // releasing the mutex.\n    if (socket_requests_in_flight_ > 1) {\n      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());\n    }\n    socket_requests_in_flight_ += 1;\n    socket_requests_are_from_thread_ = std::this_thread::get_id();\n  }\n\n  for (const auto &header : default_headers_) {\n    if (req.headers.find(header.first) == req.headers.end()) {\n      req.headers.insert(header);\n    }\n  }\n\n  auto ret = false;\n  auto close_connection = !keep_alive_;\n\n  auto se = detail::scope_exit([&]() {\n    // Briefly lock mutex in order to mark that a request is no longer ongoing\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n    socket_requests_in_flight_ -= 1;\n    if (socket_requests_in_flight_ <= 0) {\n      assert(socket_requests_in_flight_ == 0);\n      socket_requests_are_from_thread_ = std::thread::id();\n    }\n\n    if (socket_should_be_closed_when_request_is_done_ || close_connection ||\n        !ret) {\n      shutdown_ssl(socket_, true);\n      shutdown_socket(socket_);\n      close_socket(socket_);\n    }\n  });\n\n  ret = process_socket(socket_, [&](Stream &strm) {\n    return handle_request(strm, req, res, close_connection, error);\n  });\n\n  if (!ret) {\n    if (error == Error::Success) { error = Error::Unknown; }\n  }\n\n  return ret;\n}\n\ninline Result ClientImpl::send(const Request &req) {\n  auto req2 = req;\n  return send_(std::move(req2));\n}\n\ninline Result ClientImpl::send_(Request &&req) {\n  auto res = detail::make_unique<Response>();\n  auto error = Error::Success;\n  auto ret = send(req, *res, error);\n  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};\n}\n\ninline bool ClientImpl::handle_request(Stream &strm, Request &req,\n                                       Response &res, bool close_connection,\n                                       Error &error) {\n  if (req.path.empty()) {\n    error = Error::Connection;\n    return false;\n  }\n\n  auto req_save = req;\n\n  bool ret;\n\n  if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {\n    auto req2 = req;\n    req2.path = \"http://\" + host_and_port_ + req.path;\n    ret = process_request(strm, req2, res, close_connection, error);\n    req = req2;\n    req.path = req_save.path;\n  } else {\n    ret = process_request(strm, req, res, close_connection, error);\n  }\n\n  if (!ret) { return false; }\n\n  if (res.get_header_value(\"Connection\") == \"close\" ||\n      (res.version == \"HTTP/1.0\" && res.reason != \"Connection established\")) {\n    // TODO this requires a not-entirely-obvious chain of calls to be correct\n    // for this to be safe.\n\n    // This is safe to call because handle_request is only called by send_\n    // which locks the request mutex during the process. It would be a bug\n    // to call it from a different thread since it's a thread-safety issue\n    // to do these things to the socket if another thread is using the socket.\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n    shutdown_ssl(socket_, true);\n    shutdown_socket(socket_);\n    close_socket(socket_);\n  }\n\n  if (300 < res.status && res.status < 400 && follow_location_) {\n    req = req_save;\n    ret = redirect(req, res, error);\n  }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  if ((res.status == StatusCode::Unauthorized_401 ||\n       res.status == StatusCode::ProxyAuthenticationRequired_407) &&\n      req.authorization_count_ < 5) {\n    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;\n    const auto &username =\n        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;\n    const auto &password =\n        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;\n\n    if (!username.empty() && !password.empty()) {\n      std::map<std::string, std::string> auth;\n      if (detail::parse_www_authenticate(res, auth, is_proxy)) {\n        Request new_req = req;\n        new_req.authorization_count_ += 1;\n        new_req.headers.erase(is_proxy ? \"Proxy-Authorization\"\n                                       : \"Authorization\");\n        new_req.headers.insert(detail::make_digest_authentication_header(\n            req, auth, new_req.authorization_count_, detail::random_string(10),\n            username, password, is_proxy));\n\n        Response new_res;\n\n        ret = send(new_req, new_res, error);\n        if (ret) { res = new_res; }\n      }\n    }\n  }\n#endif\n\n  return ret;\n}\n\ninline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {\n  if (req.redirect_count_ == 0) {\n    error = Error::ExceedRedirectCount;\n    return false;\n  }\n\n  auto location = res.get_header_value(\"location\");\n  if (location.empty()) { return false; }\n\n  const static std::regex re(\n      R\"((?:(https?):)?(?://(?:\\[([a-fA-F\\d:]+)\\]|([^:/?#]+))(?::(\\d+))?)?([^?#]*)(\\?[^#]*)?(?:#.*)?)\");\n\n  std::smatch m;\n  if (!std::regex_match(location, m, re)) { return false; }\n\n  auto scheme = is_ssl() ? \"https\" : \"http\";\n\n  auto next_scheme = m[1].str();\n  auto next_host = m[2].str();\n  if (next_host.empty()) { next_host = m[3].str(); }\n  auto port_str = m[4].str();\n  auto next_path = m[5].str();\n  auto next_query = m[6].str();\n\n  auto next_port = port_;\n  if (!port_str.empty()) {\n    next_port = std::stoi(port_str);\n  } else if (!next_scheme.empty()) {\n    next_port = next_scheme == \"https\" ? 443 : 80;\n  }\n\n  if (next_scheme.empty()) { next_scheme = scheme; }\n  if (next_host.empty()) { next_host = host_; }\n  if (next_path.empty()) { next_path = \"/\"; }\n\n  auto path = detail::decode_url(next_path, true) + next_query;\n\n  if (next_scheme == scheme && next_host == host_ && next_port == port_) {\n    return detail::redirect(*this, req, res, path, location, error);\n  } else {\n    if (next_scheme == \"https\") {\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n      SSLClient cli(next_host, next_port);\n      cli.copy_settings(*this);\n      if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }\n      return detail::redirect(cli, req, res, path, location, error);\n#else\n      return false;\n#endif\n    } else {\n      ClientImpl cli(next_host, next_port);\n      cli.copy_settings(*this);\n      return detail::redirect(cli, req, res, path, location, error);\n    }\n  }\n}\n\ninline bool ClientImpl::write_content_with_provider(Stream &strm,\n                                                    const Request &req,\n                                                    Error &error) const {\n  auto is_shutting_down = []() { return false; };\n\n  if (req.is_chunked_content_provider_) {\n    // TODO: Brotli support\n    std::unique_ptr<detail::compressor> compressor;\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n    if (compress_) {\n      compressor = detail::make_unique<detail::gzip_compressor>();\n    } else\n#endif\n    {\n      compressor = detail::make_unique<detail::nocompressor>();\n    }\n\n    return detail::write_content_chunked(strm, req.content_provider_,\n                                         is_shutting_down, *compressor, error);\n  } else {\n    return detail::write_content(strm, req.content_provider_, 0,\n                                 req.content_length_, is_shutting_down, error);\n  }\n}\n\ninline bool ClientImpl::write_request(Stream &strm, Request &req,\n                                      bool close_connection, Error &error) {\n  // Prepare additional headers\n  if (close_connection) {\n    if (!req.has_header(\"Connection\")) {\n      req.set_header(\"Connection\", \"close\");\n    }\n  }\n\n  if (!req.has_header(\"Host\")) {\n    if (is_ssl()) {\n      if (port_ == 443) {\n        req.set_header(\"Host\", host_);\n      } else {\n        req.set_header(\"Host\", host_and_port_);\n      }\n    } else {\n      if (port_ == 80) {\n        req.set_header(\"Host\", host_);\n      } else {\n        req.set_header(\"Host\", host_and_port_);\n      }\n    }\n  }\n\n  if (!req.has_header(\"Accept\")) { req.set_header(\"Accept\", \"*/*\"); }\n\n#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT\n  if (!req.has_header(\"User-Agent\")) {\n    auto agent = std::string(\"cpp-httplib/\") + CPPHTTPLIB_VERSION;\n    req.set_header(\"User-Agent\", agent);\n  }\n#endif\n\n  if (req.body.empty()) {\n    if (req.content_provider_) {\n      if (!req.is_chunked_content_provider_) {\n        if (!req.has_header(\"Content-Length\")) {\n          auto length = std::to_string(req.content_length_);\n          req.set_header(\"Content-Length\", length);\n        }\n      }\n    } else {\n      if (req.method == \"POST\" || req.method == \"PUT\" ||\n          req.method == \"PATCH\") {\n        req.set_header(\"Content-Length\", \"0\");\n      }\n    }\n  } else {\n    if (!req.has_header(\"Content-Type\")) {\n      req.set_header(\"Content-Type\", \"text/plain\");\n    }\n\n    if (!req.has_header(\"Content-Length\")) {\n      auto length = std::to_string(req.body.size());\n      req.set_header(\"Content-Length\", length);\n    }\n  }\n\n  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {\n    if (!req.has_header(\"Authorization\")) {\n      req.headers.insert(make_basic_authentication_header(\n          basic_auth_username_, basic_auth_password_, false));\n    }\n  }\n\n  if (!proxy_basic_auth_username_.empty() &&\n      !proxy_basic_auth_password_.empty()) {\n    if (!req.has_header(\"Proxy-Authorization\")) {\n      req.headers.insert(make_basic_authentication_header(\n          proxy_basic_auth_username_, proxy_basic_auth_password_, true));\n    }\n  }\n\n  if (!bearer_token_auth_token_.empty()) {\n    if (!req.has_header(\"Authorization\")) {\n      req.headers.insert(make_bearer_token_authentication_header(\n          bearer_token_auth_token_, false));\n    }\n  }\n\n  if (!proxy_bearer_token_auth_token_.empty()) {\n    if (!req.has_header(\"Proxy-Authorization\")) {\n      req.headers.insert(make_bearer_token_authentication_header(\n          proxy_bearer_token_auth_token_, true));\n    }\n  }\n\n  // Request line and headers\n  {\n    detail::BufferStream bstrm;\n\n    const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;\n    detail::write_request_line(bstrm, req.method, path);\n\n    header_writer_(bstrm, req.headers);\n\n    // Flush buffer\n    auto &data = bstrm.get_buffer();\n    if (!detail::write_data(strm, data.data(), data.size())) {\n      error = Error::Write;\n      return false;\n    }\n  }\n\n  // Body\n  if (req.body.empty()) {\n    return write_content_with_provider(strm, req, error);\n  }\n\n  if (!detail::write_data(strm, req.body.data(), req.body.size())) {\n    error = Error::Write;\n    return false;\n  }\n\n  return true;\n}\n\ninline std::unique_ptr<Response> ClientImpl::send_with_content_provider(\n    Request &req, const char *body, size_t content_length,\n    ContentProvider content_provider,\n    ContentProviderWithoutLength content_provider_without_length,\n    const std::string &content_type, Error &error) {\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n  if (compress_) { req.set_header(\"Content-Encoding\", \"gzip\"); }\n#endif\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n  if (compress_ && !content_provider_without_length) {\n    // TODO: Brotli support\n    detail::gzip_compressor compressor;\n\n    if (content_provider) {\n      auto ok = true;\n      size_t offset = 0;\n      DataSink data_sink;\n\n      data_sink.write = [&](const char *data, size_t data_len) -> bool {\n        if (ok) {\n          auto last = offset + data_len == content_length;\n\n          auto ret = compressor.compress(\n              data, data_len, last,\n              [&](const char *compressed_data, size_t compressed_data_len) {\n                req.body.append(compressed_data, compressed_data_len);\n                return true;\n              });\n\n          if (ret) {\n            offset += data_len;\n          } else {\n            ok = false;\n          }\n        }\n        return ok;\n      };\n\n      while (ok && offset < content_length) {\n        if (!content_provider(offset, content_length - offset, data_sink)) {\n          error = Error::Canceled;\n          return nullptr;\n        }\n      }\n    } else {\n      if (!compressor.compress(body, content_length, true,\n                               [&](const char *data, size_t data_len) {\n                                 req.body.append(data, data_len);\n                                 return true;\n                               })) {\n        error = Error::Compression;\n        return nullptr;\n      }\n    }\n  } else\n#endif\n  {\n    if (content_provider) {\n      req.content_length_ = content_length;\n      req.content_provider_ = std::move(content_provider);\n      req.is_chunked_content_provider_ = false;\n    } else if (content_provider_without_length) {\n      req.content_length_ = 0;\n      req.content_provider_ = detail::ContentProviderAdapter(\n          std::move(content_provider_without_length));\n      req.is_chunked_content_provider_ = true;\n      req.set_header(\"Transfer-Encoding\", \"chunked\");\n    } else {\n      req.body.assign(body, content_length);\n    }\n  }\n\n  auto res = detail::make_unique<Response>();\n  return send(req, *res, error) ? std::move(res) : nullptr;\n}\n\ninline Result ClientImpl::send_with_content_provider(\n    const std::string &method, const std::string &path, const Headers &headers,\n    const char *body, size_t content_length, ContentProvider content_provider,\n    ContentProviderWithoutLength content_provider_without_length,\n    const std::string &content_type, Progress progress) {\n  Request req;\n  req.method = method;\n  req.headers = headers;\n  req.path = path;\n  req.progress = progress;\n\n  auto error = Error::Success;\n\n  auto res = send_with_content_provider(\n      req, body, content_length, std::move(content_provider),\n      std::move(content_provider_without_length), content_type, error);\n\n  return Result{std::move(res), error, std::move(req.headers)};\n}\n\ninline std::string\nClientImpl::adjust_host_string(const std::string &host) const {\n  if (host.find(':') != std::string::npos) { return \"[\" + host + \"]\"; }\n  return host;\n}\n\ninline bool ClientImpl::process_request(Stream &strm, Request &req,\n                                        Response &res, bool close_connection,\n                                        Error &error) {\n  // Send request\n  if (!write_request(strm, req, close_connection, error)) { return false; }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  if (is_ssl()) {\n    auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;\n    if (!is_proxy_enabled) {\n      char buf[1];\n      if (SSL_peek(socket_.ssl, buf, 1) == 0 &&\n          SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {\n        error = Error::SSLPeerCouldBeClosed_;\n        return false;\n      }\n    }\n  }\n#endif\n\n  // Receive response and headers\n  if (!read_response_line(strm, req, res) ||\n      !detail::read_headers(strm, res.headers)) {\n    error = Error::Read;\n    return false;\n  }\n\n  // Body\n  if ((res.status != StatusCode::NoContent_204) && req.method != \"HEAD\" &&\n      req.method != \"CONNECT\") {\n    auto redirect = 300 < res.status && res.status < 400 && follow_location_;\n\n    if (req.response_handler && !redirect) {\n      if (!req.response_handler(res)) {\n        error = Error::Canceled;\n        return false;\n      }\n    }\n\n    auto out =\n        req.content_receiver\n            ? static_cast<ContentReceiverWithProgress>(\n                  [&](const char *buf, size_t n, uint64_t off, uint64_t len) {\n                    if (redirect) { return true; }\n                    auto ret = req.content_receiver(buf, n, off, len);\n                    if (!ret) { error = Error::Canceled; }\n                    return ret;\n                  })\n            : static_cast<ContentReceiverWithProgress>(\n                  [&](const char *buf, size_t n, uint64_t /*off*/,\n                      uint64_t /*len*/) {\n                    if (res.body.size() + n > res.body.max_size()) {\n                      return false;\n                    }\n                    res.body.append(buf, n);\n                    return true;\n                  });\n\n    auto progress = [&](uint64_t current, uint64_t total) {\n      if (!req.progress || redirect) { return true; }\n      auto ret = req.progress(current, total);\n      if (!ret) { error = Error::Canceled; }\n      return ret;\n    };\n\n    if (res.has_header(\"Content-Length\")) {\n      if (!req.content_receiver) {\n        auto len = std::min<size_t>(res.get_header_value_u64(\"Content-Length\"),\n                                    res.body.max_size());\n        if (len > 0) { res.body.reserve(len); }\n      }\n    }\n\n    int dummy_status;\n    if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),\n                              dummy_status, std::move(progress), std::move(out),\n                              decompress_)) {\n      if (error != Error::Canceled) { error = Error::Read; }\n      return false;\n    }\n  }\n\n  // Log\n  if (logger_) { logger_(req, res); }\n\n  return true;\n}\n\ninline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(\n    const std::string &boundary, const MultipartFormDataItems &items,\n    const MultipartFormDataProviderItems &provider_items) const {\n  size_t cur_item = 0;\n  size_t cur_start = 0;\n  // cur_item and cur_start are copied to within the std::function and maintain\n  // state between successive calls\n  return [&, cur_item, cur_start](size_t offset,\n                                  DataSink &sink) mutable -> bool {\n    if (!offset && !items.empty()) {\n      sink.os << detail::serialize_multipart_formdata(items, boundary, false);\n      return true;\n    } else if (cur_item < provider_items.size()) {\n      if (!cur_start) {\n        const auto &begin = detail::serialize_multipart_formdata_item_begin(\n            provider_items[cur_item], boundary);\n        offset += begin.size();\n        cur_start = offset;\n        sink.os << begin;\n      }\n\n      DataSink cur_sink;\n      auto has_data = true;\n      cur_sink.write = sink.write;\n      cur_sink.done = [&]() { has_data = false; };\n\n      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {\n        return false;\n      }\n\n      if (!has_data) {\n        sink.os << detail::serialize_multipart_formdata_item_end();\n        cur_item++;\n        cur_start = 0;\n      }\n      return true;\n    } else {\n      sink.os << detail::serialize_multipart_formdata_finish(boundary);\n      sink.done();\n      return true;\n    }\n  };\n}\n\ninline bool\nClientImpl::process_socket(const Socket &socket,\n                           std::function<bool(Stream &strm)> callback) {\n  return detail::process_client_socket(\n      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n      write_timeout_usec_, std::move(callback));\n}\n\ninline bool ClientImpl::is_ssl() const { return false; }\n\ninline Result ClientImpl::Get(const std::string &path) {\n  return Get(path, Headers(), Progress());\n}\n\ninline Result ClientImpl::Get(const std::string &path, Progress progress) {\n  return Get(path, Headers(), std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers) {\n  return Get(path, headers, Progress());\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              Progress progress) {\n  Request req;\n  req.method = \"GET\";\n  req.path = path;\n  req.headers = headers;\n  req.progress = std::move(progress);\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Get(const std::string &path,\n                              ContentReceiver content_receiver) {\n  return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);\n}\n\ninline Result ClientImpl::Get(const std::string &path,\n                              ContentReceiver content_receiver,\n                              Progress progress) {\n  return Get(path, Headers(), nullptr, std::move(content_receiver),\n             std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              ContentReceiver content_receiver) {\n  return Get(path, headers, nullptr, std::move(content_receiver), nullptr);\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              ContentReceiver content_receiver,\n                              Progress progress) {\n  return Get(path, headers, nullptr, std::move(content_receiver),\n             std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver) {\n  return Get(path, Headers(), std::move(response_handler),\n             std::move(content_receiver), nullptr);\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver) {\n  return Get(path, headers, std::move(response_handler),\n             std::move(content_receiver), nullptr);\n}\n\ninline Result ClientImpl::Get(const std::string &path,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver,\n                              Progress progress) {\n  return Get(path, Headers(), std::move(response_handler),\n             std::move(content_receiver), std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver,\n                              Progress progress) {\n  Request req;\n  req.method = \"GET\";\n  req.path = path;\n  req.headers = headers;\n  req.response_handler = std::move(response_handler);\n  req.content_receiver =\n      [content_receiver](const char *data, size_t data_length,\n                         uint64_t /*offset*/, uint64_t /*total_length*/) {\n        return content_receiver(data, data_length);\n      };\n  req.progress = std::move(progress);\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Params &params,\n                              const Headers &headers, Progress progress) {\n  if (params.empty()) { return Get(path, headers); }\n\n  std::string path_with_query = append_query_params(path, params);\n  return Get(path_with_query, headers, std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Params &params,\n                              const Headers &headers,\n                              ContentReceiver content_receiver,\n                              Progress progress) {\n  return Get(path, params, headers, nullptr, std::move(content_receiver),\n             std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Params &params,\n                              const Headers &headers,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver,\n                              Progress progress) {\n  if (params.empty()) {\n    return Get(path, headers, std::move(response_handler),\n               std::move(content_receiver), std::move(progress));\n  }\n\n  std::string path_with_query = append_query_params(path, params);\n  return Get(path_with_query, headers, std::move(response_handler),\n             std::move(content_receiver), std::move(progress));\n}\n\ninline Result ClientImpl::Head(const std::string &path) {\n  return Head(path, Headers());\n}\n\ninline Result ClientImpl::Head(const std::string &path,\n                               const Headers &headers) {\n  Request req;\n  req.method = \"HEAD\";\n  req.headers = headers;\n  req.path = path;\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Post(const std::string &path) {\n  return Post(path, std::string(), std::string());\n}\n\ninline Result ClientImpl::Post(const std::string &path,\n                               const Headers &headers) {\n  return Post(path, headers, nullptr, 0, std::string());\n}\n\ninline Result ClientImpl::Post(const std::string &path, const char *body,\n                               size_t content_length,\n                               const std::string &content_type) {\n  return Post(path, Headers(), body, content_length, content_type, nullptr);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const char *body, size_t content_length,\n                               const std::string &content_type) {\n  return send_with_content_provider(\"POST\", path, headers, body, content_length,\n                                    nullptr, nullptr, content_type, nullptr);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const char *body, size_t content_length,\n                               const std::string &content_type,\n                               Progress progress) {\n  return send_with_content_provider(\"POST\", path, headers, body, content_length,\n                                    nullptr, nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const std::string &body,\n                               const std::string &content_type) {\n  return Post(path, Headers(), body, content_type);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const std::string &body,\n                               const std::string &content_type,\n                               Progress progress) {\n  return Post(path, Headers(), body, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const std::string &body,\n                               const std::string &content_type) {\n  return send_with_content_provider(\"POST\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    nullptr);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const std::string &body,\n                               const std::string &content_type,\n                               Progress progress) {\n  return send_with_content_provider(\"POST\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Params &params) {\n  return Post(path, Headers(), params);\n}\n\ninline Result ClientImpl::Post(const std::string &path, size_t content_length,\n                               ContentProvider content_provider,\n                               const std::string &content_type) {\n  return Post(path, Headers(), content_length, std::move(content_provider),\n              content_type);\n}\n\ninline Result ClientImpl::Post(const std::string &path,\n                               ContentProviderWithoutLength content_provider,\n                               const std::string &content_type) {\n  return Post(path, Headers(), std::move(content_provider), content_type);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               size_t content_length,\n                               ContentProvider content_provider,\n                               const std::string &content_type) {\n  return send_with_content_provider(\"POST\", path, headers, nullptr,\n                                    content_length, std::move(content_provider),\n                                    nullptr, content_type, nullptr);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               ContentProviderWithoutLength content_provider,\n                               const std::string &content_type) {\n  return send_with_content_provider(\"POST\", path, headers, nullptr, 0, nullptr,\n                                    std::move(content_provider), content_type,\n                                    nullptr);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const Params &params) {\n  auto query = detail::params_to_query_str(params);\n  return Post(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const Params &params, Progress progress) {\n  auto query = detail::params_to_query_str(params);\n  return Post(path, headers, query, \"application/x-www-form-urlencoded\",\n              progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path,\n                               const MultipartFormDataItems &items) {\n  return Post(path, Headers(), items);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const MultipartFormDataItems &items) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Post(path, headers, body, content_type);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const MultipartFormDataItems &items,\n                               const std::string &boundary) {\n  if (!detail::is_multipart_boundary_chars_valid(boundary)) {\n    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};\n  }\n\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Post(path, headers, body, content_type);\n}\n\ninline Result\nClientImpl::Post(const std::string &path, const Headers &headers,\n                 const MultipartFormDataItems &items,\n                 const MultipartFormDataProviderItems &provider_items) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  return send_with_content_provider(\n      \"POST\", path, headers, nullptr, 0, nullptr,\n      get_multipart_content_provider(boundary, items, provider_items),\n      content_type, nullptr);\n}\n\ninline Result ClientImpl::Put(const std::string &path) {\n  return Put(path, std::string(), std::string());\n}\n\ninline Result ClientImpl::Put(const std::string &path, const char *body,\n                              size_t content_length,\n                              const std::string &content_type) {\n  return Put(path, Headers(), body, content_length, content_type);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const char *body, size_t content_length,\n                              const std::string &content_type) {\n  return send_with_content_provider(\"PUT\", path, headers, body, content_length,\n                                    nullptr, nullptr, content_type, nullptr);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const char *body, size_t content_length,\n                              const std::string &content_type,\n                              Progress progress) {\n  return send_with_content_provider(\"PUT\", path, headers, body, content_length,\n                                    nullptr, nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const std::string &body,\n                              const std::string &content_type) {\n  return Put(path, Headers(), body, content_type);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const std::string &body,\n                              const std::string &content_type,\n                              Progress progress) {\n  return Put(path, Headers(), body, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const std::string &body,\n                              const std::string &content_type) {\n  return send_with_content_provider(\"PUT\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    nullptr);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const std::string &body,\n                              const std::string &content_type,\n                              Progress progress) {\n  return send_with_content_provider(\"PUT\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, size_t content_length,\n                              ContentProvider content_provider,\n                              const std::string &content_type) {\n  return Put(path, Headers(), content_length, std::move(content_provider),\n             content_type);\n}\n\ninline Result ClientImpl::Put(const std::string &path,\n                              ContentProviderWithoutLength content_provider,\n                              const std::string &content_type) {\n  return Put(path, Headers(), std::move(content_provider), content_type);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              size_t content_length,\n                              ContentProvider content_provider,\n                              const std::string &content_type) {\n  return send_with_content_provider(\"PUT\", path, headers, nullptr,\n                                    content_length, std::move(content_provider),\n                                    nullptr, content_type, nullptr);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              ContentProviderWithoutLength content_provider,\n                              const std::string &content_type) {\n  return send_with_content_provider(\"PUT\", path, headers, nullptr, 0, nullptr,\n                                    std::move(content_provider), content_type,\n                                    nullptr);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Params &params) {\n  return Put(path, Headers(), params);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const Params &params) {\n  auto query = detail::params_to_query_str(params);\n  return Put(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const Params &params, Progress progress) {\n  auto query = detail::params_to_query_str(params);\n  return Put(path, headers, query, \"application/x-www-form-urlencoded\",\n             progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path,\n                              const MultipartFormDataItems &items) {\n  return Put(path, Headers(), items);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const MultipartFormDataItems &items) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Put(path, headers, body, content_type);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const MultipartFormDataItems &items,\n                              const std::string &boundary) {\n  if (!detail::is_multipart_boundary_chars_valid(boundary)) {\n    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};\n  }\n\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Put(path, headers, body, content_type);\n}\n\ninline Result\nClientImpl::Put(const std::string &path, const Headers &headers,\n                const MultipartFormDataItems &items,\n                const MultipartFormDataProviderItems &provider_items) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  return send_with_content_provider(\n      \"PUT\", path, headers, nullptr, 0, nullptr,\n      get_multipart_content_provider(boundary, items, provider_items),\n      content_type, nullptr);\n}\ninline Result ClientImpl::Patch(const std::string &path) {\n  return Patch(path, std::string(), std::string());\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const char *body,\n                                size_t content_length,\n                                const std::string &content_type) {\n  return Patch(path, Headers(), body, content_length, content_type);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const char *body,\n                                size_t content_length,\n                                const std::string &content_type,\n                                Progress progress) {\n  return Patch(path, Headers(), body, content_length, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const char *body, size_t content_length,\n                                const std::string &content_type) {\n  return Patch(path, headers, body, content_length, content_type, nullptr);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const char *body, size_t content_length,\n                                const std::string &content_type,\n                                Progress progress) {\n  return send_with_content_provider(\"PATCH\", path, headers, body,\n                                    content_length, nullptr, nullptr,\n                                    content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path,\n                                const std::string &body,\n                                const std::string &content_type) {\n  return Patch(path, Headers(), body, content_type);\n}\n\ninline Result ClientImpl::Patch(const std::string &path,\n                                const std::string &body,\n                                const std::string &content_type,\n                                Progress progress) {\n  return Patch(path, Headers(), body, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const std::string &body,\n                                const std::string &content_type) {\n  return Patch(path, headers, body, content_type, nullptr);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const std::string &body,\n                                const std::string &content_type,\n                                Progress progress) {\n  return send_with_content_provider(\"PATCH\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, size_t content_length,\n                                ContentProvider content_provider,\n                                const std::string &content_type) {\n  return Patch(path, Headers(), content_length, std::move(content_provider),\n               content_type);\n}\n\ninline Result ClientImpl::Patch(const std::string &path,\n                                ContentProviderWithoutLength content_provider,\n                                const std::string &content_type) {\n  return Patch(path, Headers(), std::move(content_provider), content_type);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                size_t content_length,\n                                ContentProvider content_provider,\n                                const std::string &content_type) {\n  return send_with_content_provider(\"PATCH\", path, headers, nullptr,\n                                    content_length, std::move(content_provider),\n                                    nullptr, content_type, nullptr);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                ContentProviderWithoutLength content_provider,\n                                const std::string &content_type) {\n  return send_with_content_provider(\"PATCH\", path, headers, nullptr, 0, nullptr,\n                                    std::move(content_provider), content_type,\n                                    nullptr);\n}\n\ninline Result ClientImpl::Delete(const std::string &path) {\n  return Delete(path, Headers(), std::string(), std::string());\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers) {\n  return Delete(path, headers, std::string(), std::string());\n}\n\ninline Result ClientImpl::Delete(const std::string &path, const char *body,\n                                 size_t content_length,\n                                 const std::string &content_type) {\n  return Delete(path, Headers(), body, content_length, content_type);\n}\n\ninline Result ClientImpl::Delete(const std::string &path, const char *body,\n                                 size_t content_length,\n                                 const std::string &content_type,\n                                 Progress progress) {\n  return Delete(path, Headers(), body, content_length, content_type, progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers, const char *body,\n                                 size_t content_length,\n                                 const std::string &content_type) {\n  return Delete(path, headers, body, content_length, content_type, nullptr);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers, const char *body,\n                                 size_t content_length,\n                                 const std::string &content_type,\n                                 Progress progress) {\n  Request req;\n  req.method = \"DELETE\";\n  req.headers = headers;\n  req.path = path;\n  req.progress = progress;\n\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n  req.body.assign(body, content_length);\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const std::string &body,\n                                 const std::string &content_type) {\n  return Delete(path, Headers(), body.data(), body.size(), content_type);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const std::string &body,\n                                 const std::string &content_type,\n                                 Progress progress) {\n  return Delete(path, Headers(), body.data(), body.size(), content_type,\n                progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers,\n                                 const std::string &body,\n                                 const std::string &content_type) {\n  return Delete(path, headers, body.data(), body.size(), content_type);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers,\n                                 const std::string &body,\n                                 const std::string &content_type,\n                                 Progress progress) {\n  return Delete(path, headers, body.data(), body.size(), content_type,\n                progress);\n}\n\ninline Result ClientImpl::Options(const std::string &path) {\n  return Options(path, Headers());\n}\n\ninline Result ClientImpl::Options(const std::string &path,\n                                  const Headers &headers) {\n  Request req;\n  req.method = \"OPTIONS\";\n  req.headers = headers;\n  req.path = path;\n\n  return send_(std::move(req));\n}\n\ninline void ClientImpl::stop() {\n  std::lock_guard<std::mutex> guard(socket_mutex_);\n\n  // If there is anything ongoing right now, the ONLY thread-safe thing we can\n  // do is to shutdown_socket, so that threads using this socket suddenly\n  // discover they can't read/write any more and error out. Everything else\n  // (closing the socket, shutting ssl down) is unsafe because these actions are\n  // not thread-safe.\n  if (socket_requests_in_flight_ > 0) {\n    shutdown_socket(socket_);\n\n    // Aside from that, we set a flag for the socket to be closed when we're\n    // done.\n    socket_should_be_closed_when_request_is_done_ = true;\n    return;\n  }\n\n  // Otherwise, still holding the mutex, we can shut everything down ourselves\n  shutdown_ssl(socket_, true);\n  shutdown_socket(socket_);\n  close_socket(socket_);\n}\n\ninline std::string ClientImpl::host() const { return host_; }\n\ninline int ClientImpl::port() const { return port_; }\n\ninline size_t ClientImpl::is_socket_open() const {\n  std::lock_guard<std::mutex> guard(socket_mutex_);\n  return socket_.is_open();\n}\n\ninline socket_t ClientImpl::socket() const { return socket_.sock; }\n\ninline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {\n  connection_timeout_sec_ = sec;\n  connection_timeout_usec_ = usec;\n}\n\ninline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {\n  read_timeout_sec_ = sec;\n  read_timeout_usec_ = usec;\n}\n\ninline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {\n  write_timeout_sec_ = sec;\n  write_timeout_usec_ = usec;\n}\n\ninline void ClientImpl::set_basic_auth(const std::string &username,\n                                       const std::string &password) {\n  basic_auth_username_ = username;\n  basic_auth_password_ = password;\n}\n\ninline void ClientImpl::set_bearer_token_auth(const std::string &token) {\n  bearer_token_auth_token_ = token;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void ClientImpl::set_digest_auth(const std::string &username,\n                                        const std::string &password) {\n  digest_auth_username_ = username;\n  digest_auth_password_ = password;\n}\n#endif\n\ninline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }\n\ninline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }\n\ninline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }\n\ninline void\nClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {\n  addr_map_ = std::move(addr_map);\n}\n\ninline void ClientImpl::set_default_headers(Headers headers) {\n  default_headers_ = std::move(headers);\n}\n\ninline void ClientImpl::set_header_writer(\n    std::function<ssize_t(Stream &, Headers &)> const &writer) {\n  header_writer_ = writer;\n}\n\ninline void ClientImpl::set_address_family(int family) {\n  address_family_ = family;\n}\n\ninline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }\n\ninline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }\n\ninline void ClientImpl::set_socket_options(SocketOptions socket_options) {\n  socket_options_ = std::move(socket_options);\n}\n\ninline void ClientImpl::set_compress(bool on) { compress_ = on; }\n\ninline void ClientImpl::set_decompress(bool on) { decompress_ = on; }\n\ninline void ClientImpl::set_interface(const std::string &intf) {\n  interface_ = intf;\n}\n\ninline void ClientImpl::set_proxy(const std::string &host, int port) {\n  proxy_host_ = host;\n  proxy_port_ = port;\n}\n\ninline void ClientImpl::set_proxy_basic_auth(const std::string &username,\n                                             const std::string &password) {\n  proxy_basic_auth_username_ = username;\n  proxy_basic_auth_password_ = password;\n}\n\ninline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {\n  proxy_bearer_token_auth_token_ = token;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void ClientImpl::set_proxy_digest_auth(const std::string &username,\n                                              const std::string &password) {\n  proxy_digest_auth_username_ = username;\n  proxy_digest_auth_password_ = password;\n}\n\ninline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,\n                                         const std::string &ca_cert_dir_path) {\n  ca_cert_file_path_ = ca_cert_file_path;\n  ca_cert_dir_path_ = ca_cert_dir_path;\n}\n\ninline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {\n  if (ca_cert_store && ca_cert_store != ca_cert_store_) {\n    ca_cert_store_ = ca_cert_store;\n  }\n}\n\ninline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,\n                                                    std::size_t size) const {\n  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));\n  auto se = detail::scope_exit([&] { BIO_free_all(mem); });\n  if (!mem) { return nullptr; }\n\n  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);\n  if (!inf) { return nullptr; }\n\n  auto cts = X509_STORE_new();\n  if (cts) {\n    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {\n      auto itmp = sk_X509_INFO_value(inf, i);\n      if (!itmp) { continue; }\n\n      if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }\n      if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }\n    }\n  }\n\n  sk_X509_INFO_pop_free(inf, X509_INFO_free);\n  return cts;\n}\n\ninline void ClientImpl::enable_server_certificate_verification(bool enabled) {\n  server_certificate_verification_ = enabled;\n}\n\ninline void ClientImpl::enable_server_hostname_verification(bool enabled) {\n  server_hostname_verification_ = enabled;\n}\n\ninline void ClientImpl::set_server_certificate_verifier(\n    std::function<bool(SSL *ssl)> verifier) {\n  server_certificate_verifier_ = verifier;\n}\n#endif\n\ninline void ClientImpl::set_logger(Logger logger) {\n  logger_ = std::move(logger);\n}\n\n/*\n * SSL Implementation\n */\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nnamespace detail {\n\ntemplate <typename U, typename V>\ninline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,\n                    U SSL_connect_or_accept, V setup) {\n  SSL *ssl = nullptr;\n  {\n    std::lock_guard<std::mutex> guard(ctx_mutex);\n    ssl = SSL_new(ctx);\n  }\n\n  if (ssl) {\n    set_nonblocking(sock, true);\n    auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);\n    BIO_set_nbio(bio, 1);\n    SSL_set_bio(ssl, bio, bio);\n\n    if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {\n      SSL_shutdown(ssl);\n      {\n        std::lock_guard<std::mutex> guard(ctx_mutex);\n        SSL_free(ssl);\n      }\n      set_nonblocking(sock, false);\n      return nullptr;\n    }\n    BIO_set_nbio(bio, 0);\n    set_nonblocking(sock, false);\n  }\n\n  return ssl;\n}\n\ninline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,\n                       bool shutdown_gracefully) {\n  // sometimes we may want to skip this to try to avoid SIGPIPE if we know\n  // the remote has closed the network connection\n  // Note that it is not always possible to avoid SIGPIPE, this is merely a\n  // best-efforts.\n  if (shutdown_gracefully) {\n#ifdef _WIN32\n    SSL_shutdown(ssl);\n#else\n    timeval tv;\n    tv.tv_sec = 1;\n    tv.tv_usec = 0;\n    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,\n               reinterpret_cast<const void *>(&tv), sizeof(tv));\n\n    auto ret = SSL_shutdown(ssl);\n    while (ret == 0) {\n      std::this_thread::sleep_for(std::chrono::milliseconds{100});\n      ret = SSL_shutdown(ssl);\n    }\n#endif\n  }\n\n  std::lock_guard<std::mutex> guard(ctx_mutex);\n  SSL_free(ssl);\n}\n\ntemplate <typename U>\nbool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,\n                                       U ssl_connect_or_accept,\n                                       time_t timeout_sec,\n                                       time_t timeout_usec) {\n  auto res = 0;\n  while ((res = ssl_connect_or_accept(ssl)) != 1) {\n    auto err = SSL_get_error(ssl, res);\n    switch (err) {\n    case SSL_ERROR_WANT_READ:\n      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }\n      break;\n    case SSL_ERROR_WANT_WRITE:\n      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }\n      break;\n    default: break;\n    }\n    return false;\n  }\n  return true;\n}\n\ntemplate <typename T>\ninline bool process_server_socket_ssl(\n    const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,\n    size_t keep_alive_max_count, time_t keep_alive_timeout_sec,\n    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,\n    time_t write_timeout_usec, T callback) {\n  return process_server_socket_core(\n      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,\n      [&](bool close_connection, bool &connection_closed) {\n        SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,\n                             write_timeout_sec, write_timeout_usec);\n        return callback(strm, close_connection, connection_closed);\n      });\n}\n\ntemplate <typename T>\ninline bool\nprocess_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,\n                          time_t read_timeout_usec, time_t write_timeout_sec,\n                          time_t write_timeout_usec, T callback) {\n  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,\n                       write_timeout_sec, write_timeout_usec);\n  return callback(strm);\n}\n\nclass SSLInit {\npublic:\n  SSLInit() {\n    OPENSSL_init_ssl(\n        OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);\n  }\n};\n\n// SSL socket stream implementation\ninline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,\n                                        time_t read_timeout_sec,\n                                        time_t read_timeout_usec,\n                                        time_t write_timeout_sec,\n                                        time_t write_timeout_usec)\n    : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),\n      read_timeout_usec_(read_timeout_usec),\n      write_timeout_sec_(write_timeout_sec),\n      write_timeout_usec_(write_timeout_usec) {\n  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);\n}\n\ninline SSLSocketStream::~SSLSocketStream() = default;\n\ninline bool SSLSocketStream::is_readable() const {\n  return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;\n}\n\ninline bool SSLSocketStream::is_writable() const {\n  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&\n         is_socket_alive(sock_);\n}\n\ninline ssize_t SSLSocketStream::read(char *ptr, size_t size) {\n  if (SSL_pending(ssl_) > 0) {\n    return SSL_read(ssl_, ptr, static_cast<int>(size));\n  } else if (is_readable()) {\n    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));\n    if (ret < 0) {\n      auto err = SSL_get_error(ssl_, ret);\n      auto n = 1000;\n#ifdef _WIN32\n      while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||\n                          (err == SSL_ERROR_SYSCALL &&\n                           WSAGetLastError() == WSAETIMEDOUT))) {\n#else\n      while (--n >= 0 && err == SSL_ERROR_WANT_READ) {\n#endif\n        if (SSL_pending(ssl_) > 0) {\n          return SSL_read(ssl_, ptr, static_cast<int>(size));\n        } else if (is_readable()) {\n          std::this_thread::sleep_for(std::chrono::microseconds{10});\n          ret = SSL_read(ssl_, ptr, static_cast<int>(size));\n          if (ret >= 0) { return ret; }\n          err = SSL_get_error(ssl_, ret);\n        } else {\n          return -1;\n        }\n      }\n    }\n    return ret;\n  }\n  return -1;\n}\n\ninline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {\n  if (is_writable()) {\n    auto handle_size = static_cast<int>(\n        std::min<size_t>(size, (std::numeric_limits<int>::max)()));\n\n    auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));\n    if (ret < 0) {\n      auto err = SSL_get_error(ssl_, ret);\n      auto n = 1000;\n#ifdef _WIN32\n      while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||\n                          (err == SSL_ERROR_SYSCALL &&\n                           WSAGetLastError() == WSAETIMEDOUT))) {\n#else\n      while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {\n#endif\n        if (is_writable()) {\n          std::this_thread::sleep_for(std::chrono::microseconds{10});\n          ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));\n          if (ret >= 0) { return ret; }\n          err = SSL_get_error(ssl_, ret);\n        } else {\n          return -1;\n        }\n      }\n    }\n    return ret;\n  }\n  return -1;\n}\n\ninline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,\n                                                    int &port) const {\n  detail::get_remote_ip_and_port(sock_, ip, port);\n}\n\ninline void SSLSocketStream::get_local_ip_and_port(std::string &ip,\n                                                   int &port) const {\n  detail::get_local_ip_and_port(sock_, ip, port);\n}\n\ninline socket_t SSLSocketStream::socket() const { return sock_; }\n\nstatic SSLInit sslinit_;\n\n} // namespace detail\n\n// SSL HTTP server implementation\ninline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,\n                            const char *client_ca_cert_file_path,\n                            const char *client_ca_cert_dir_path,\n                            const char *private_key_password) {\n  ctx_ = SSL_CTX_new(TLS_server_method());\n\n  if (ctx_) {\n    SSL_CTX_set_options(ctx_,\n                        SSL_OP_NO_COMPRESSION |\n                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);\n\n    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);\n\n    if (private_key_password != nullptr && (private_key_password[0] != '\\0')) {\n      SSL_CTX_set_default_passwd_cb_userdata(\n          ctx_,\n          reinterpret_cast<void *>(const_cast<char *>(private_key_password)));\n    }\n\n    if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||\n        SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=\n            1 ||\n        SSL_CTX_check_private_key(ctx_) != 1) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {\n      SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,\n                                    client_ca_cert_dir_path);\n\n      SSL_CTX_set_verify(\n          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);\n    }\n  }\n}\n\ninline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,\n                            X509_STORE *client_ca_cert_store) {\n  ctx_ = SSL_CTX_new(TLS_server_method());\n\n  if (ctx_) {\n    SSL_CTX_set_options(ctx_,\n                        SSL_OP_NO_COMPRESSION |\n                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);\n\n    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);\n\n    if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||\n        SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    } else if (client_ca_cert_store) {\n      SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);\n\n      SSL_CTX_set_verify(\n          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);\n    }\n  }\n}\n\ninline SSLServer::SSLServer(\n    const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {\n  ctx_ = SSL_CTX_new(TLS_method());\n  if (ctx_) {\n    if (!setup_ssl_ctx_callback(*ctx_)) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    }\n  }\n}\n\ninline SSLServer::~SSLServer() {\n  if (ctx_) { SSL_CTX_free(ctx_); }\n}\n\ninline bool SSLServer::is_valid() const { return ctx_; }\n\ninline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }\n\ninline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,\n                                    X509_STORE *client_ca_cert_store) {\n\n  std::lock_guard<std::mutex> guard(ctx_mutex_);\n\n  SSL_CTX_use_certificate(ctx_, cert);\n  SSL_CTX_use_PrivateKey(ctx_, private_key);\n\n  if (client_ca_cert_store != nullptr) {\n    SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);\n  }\n}\n\ninline bool SSLServer::process_and_close_socket(socket_t sock) {\n  auto ssl = detail::ssl_new(\n      sock, ctx_, ctx_mutex_,\n      [&](SSL *ssl2) {\n        return detail::ssl_connect_or_accept_nonblocking(\n            sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);\n      },\n      [](SSL * /*ssl2*/) { return true; });\n\n  auto ret = false;\n  if (ssl) {\n    std::string remote_addr;\n    int remote_port = 0;\n    detail::get_remote_ip_and_port(sock, remote_addr, remote_port);\n\n    std::string local_addr;\n    int local_port = 0;\n    detail::get_local_ip_and_port(sock, local_addr, local_port);\n\n    ret = detail::process_server_socket_ssl(\n        svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,\n        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n        write_timeout_usec_,\n        [&](Stream &strm, bool close_connection, bool &connection_closed) {\n          return process_request(strm, remote_addr, remote_port, local_addr,\n                                 local_port, close_connection,\n                                 connection_closed,\n                                 [&](Request &req) { req.ssl = ssl; });\n        });\n\n    // Shutdown gracefully if the result seemed successful, non-gracefully if\n    // the connection appeared to be closed.\n    const bool shutdown_gracefully = ret;\n    detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);\n  }\n\n  detail::shutdown_socket(sock);\n  detail::close_socket(sock);\n  return ret;\n}\n\n// SSL HTTP client implementation\ninline SSLClient::SSLClient(const std::string &host)\n    : SSLClient(host, 443, std::string(), std::string()) {}\n\ninline SSLClient::SSLClient(const std::string &host, int port)\n    : SSLClient(host, port, std::string(), std::string()) {}\n\ninline SSLClient::SSLClient(const std::string &host, int port,\n                            const std::string &client_cert_path,\n                            const std::string &client_key_path,\n                            const std::string &private_key_password)\n    : ClientImpl(host, port, client_cert_path, client_key_path) {\n  ctx_ = SSL_CTX_new(TLS_client_method());\n\n  SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);\n\n  detail::split(&host_[0], &host_[host_.size()], '.',\n                [&](const char *b, const char *e) {\n                  host_components_.emplace_back(b, e);\n                });\n\n  if (!client_cert_path.empty() && !client_key_path.empty()) {\n    if (!private_key_password.empty()) {\n      SSL_CTX_set_default_passwd_cb_userdata(\n          ctx_, reinterpret_cast<void *>(\n                    const_cast<char *>(private_key_password.c_str())));\n    }\n\n    if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),\n                                     SSL_FILETYPE_PEM) != 1 ||\n        SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),\n                                    SSL_FILETYPE_PEM) != 1) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    }\n  }\n}\n\ninline SSLClient::SSLClient(const std::string &host, int port,\n                            X509 *client_cert, EVP_PKEY *client_key,\n                            const std::string &private_key_password)\n    : ClientImpl(host, port) {\n  ctx_ = SSL_CTX_new(TLS_client_method());\n\n  detail::split(&host_[0], &host_[host_.size()], '.',\n                [&](const char *b, const char *e) {\n                  host_components_.emplace_back(b, e);\n                });\n\n  if (client_cert != nullptr && client_key != nullptr) {\n    if (!private_key_password.empty()) {\n      SSL_CTX_set_default_passwd_cb_userdata(\n          ctx_, reinterpret_cast<void *>(\n                    const_cast<char *>(private_key_password.c_str())));\n    }\n\n    if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||\n        SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    }\n  }\n}\n\ninline SSLClient::~SSLClient() {\n  if (ctx_) { SSL_CTX_free(ctx_); }\n  // Make sure to shut down SSL since shutdown_ssl will resolve to the\n  // base function rather than the derived function once we get to the\n  // base class destructor, and won't free the SSL (causing a leak).\n  shutdown_ssl_impl(socket_, true);\n}\n\ninline bool SSLClient::is_valid() const { return ctx_; }\n\ninline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {\n  if (ca_cert_store) {\n    if (ctx_) {\n      if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {\n        // Free memory allocated for old cert and use new store `ca_cert_store`\n        SSL_CTX_set_cert_store(ctx_, ca_cert_store);\n      }\n    } else {\n      X509_STORE_free(ca_cert_store);\n    }\n  }\n}\n\ninline void SSLClient::load_ca_cert_store(const char *ca_cert,\n                                          std::size_t size) {\n  set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));\n}\n\ninline long SSLClient::get_openssl_verify_result() const {\n  return verify_result_;\n}\n\ninline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }\n\ninline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {\n  return is_valid() && ClientImpl::create_and_connect_socket(socket, error);\n}\n\n// Assumes that socket_mutex_ is locked and that there are no requests in flight\ninline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,\n                                          bool &success, Error &error) {\n  success = true;\n  Response proxy_res;\n  if (!detail::process_client_socket(\n          socket.sock, read_timeout_sec_, read_timeout_usec_,\n          write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {\n            Request req2;\n            req2.method = \"CONNECT\";\n            req2.path = host_and_port_;\n            return process_request(strm, req2, proxy_res, false, error);\n          })) {\n    // Thread-safe to close everything because we are assuming there are no\n    // requests in flight\n    shutdown_ssl(socket, true);\n    shutdown_socket(socket);\n    close_socket(socket);\n    success = false;\n    return false;\n  }\n\n  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {\n    if (!proxy_digest_auth_username_.empty() &&\n        !proxy_digest_auth_password_.empty()) {\n      std::map<std::string, std::string> auth;\n      if (detail::parse_www_authenticate(proxy_res, auth, true)) {\n        proxy_res = Response();\n        if (!detail::process_client_socket(\n                socket.sock, read_timeout_sec_, read_timeout_usec_,\n                write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {\n                  Request req3;\n                  req3.method = \"CONNECT\";\n                  req3.path = host_and_port_;\n                  req3.headers.insert(detail::make_digest_authentication_header(\n                      req3, auth, 1, detail::random_string(10),\n                      proxy_digest_auth_username_, proxy_digest_auth_password_,\n                      true));\n                  return process_request(strm, req3, proxy_res, false, error);\n                })) {\n          // Thread-safe to close everything because we are assuming there are\n          // no requests in flight\n          shutdown_ssl(socket, true);\n          shutdown_socket(socket);\n          close_socket(socket);\n          success = false;\n          return false;\n        }\n      }\n    }\n  }\n\n  // If status code is not 200, proxy request is failed.\n  // Set error to ProxyConnection and return proxy response\n  // as the response of the request\n  if (proxy_res.status != StatusCode::OK_200) {\n    error = Error::ProxyConnection;\n    res = std::move(proxy_res);\n    // Thread-safe to close everything because we are assuming there are\n    // no requests in flight\n    shutdown_ssl(socket, true);\n    shutdown_socket(socket);\n    close_socket(socket);\n    return false;\n  }\n\n  return true;\n}\n\ninline bool SSLClient::load_certs() {\n  auto ret = true;\n\n  std::call_once(initialize_cert_, [&]() {\n    std::lock_guard<std::mutex> guard(ctx_mutex_);\n    if (!ca_cert_file_path_.empty()) {\n      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),\n                                         nullptr)) {\n        ret = false;\n      }\n    } else if (!ca_cert_dir_path_.empty()) {\n      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,\n                                         ca_cert_dir_path_.c_str())) {\n        ret = false;\n      }\n    } else {\n      auto loaded = false;\n#ifdef _WIN32\n      loaded =\n          detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));\n#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)\n#if TARGET_OS_OSX\n      loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));\n#endif // TARGET_OS_OSX\n#endif // _WIN32\n      if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }\n    }\n  });\n\n  return ret;\n}\n\ninline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {\n  auto ssl = detail::ssl_new(\n      socket.sock, ctx_, ctx_mutex_,\n      [&](SSL *ssl2) {\n        if (server_certificate_verification_) {\n          if (!load_certs()) {\n            error = Error::SSLLoadingCerts;\n            return false;\n          }\n          SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);\n        }\n\n        if (!detail::ssl_connect_or_accept_nonblocking(\n                socket.sock, ssl2, SSL_connect, connection_timeout_sec_,\n                connection_timeout_usec_)) {\n          error = Error::SSLConnection;\n          return false;\n        }\n\n        if (server_certificate_verification_) {\n          if (server_certificate_verifier_) {\n            if (!server_certificate_verifier_(ssl2)) {\n              error = Error::SSLServerVerification;\n              return false;\n            }\n          } else {\n            verify_result_ = SSL_get_verify_result(ssl2);\n\n            if (verify_result_ != X509_V_OK) {\n              error = Error::SSLServerVerification;\n              return false;\n            }\n\n            auto server_cert = SSL_get1_peer_certificate(ssl2);\n            auto se = detail::scope_exit([&] { X509_free(server_cert); });\n\n            if (server_cert == nullptr) {\n              error = Error::SSLServerVerification;\n              return false;\n            }\n\n            if (server_hostname_verification_) {\n              if (!verify_host(server_cert)) {\n                error = Error::SSLServerHostnameVerification;\n                return false;\n              }\n            }\n          }\n        }\n\n        return true;\n      },\n      [&](SSL *ssl2) {\n#if defined(OPENSSL_IS_BORINGSSL)\n        SSL_set_tlsext_host_name(ssl2, host_.c_str());\n#else\n        // NOTE: Direct call instead of using the OpenSSL macro to suppress\n        // -Wold-style-cast warning\n        SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,\n                 static_cast<void *>(const_cast<char *>(host_.c_str())));\n#endif\n        return true;\n      });\n\n  if (ssl) {\n    socket.ssl = ssl;\n    return true;\n  }\n\n  shutdown_socket(socket);\n  close_socket(socket);\n  return false;\n}\n\ninline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {\n  shutdown_ssl_impl(socket, shutdown_gracefully);\n}\n\ninline void SSLClient::shutdown_ssl_impl(Socket &socket,\n                                         bool shutdown_gracefully) {\n  if (socket.sock == INVALID_SOCKET) {\n    assert(socket.ssl == nullptr);\n    return;\n  }\n  if (socket.ssl) {\n    detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,\n                       shutdown_gracefully);\n    socket.ssl = nullptr;\n  }\n  assert(socket.ssl == nullptr);\n}\n\ninline bool\nSSLClient::process_socket(const Socket &socket,\n                          std::function<bool(Stream &strm)> callback) {\n  assert(socket.ssl);\n  return detail::process_client_socket_ssl(\n      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,\n      write_timeout_sec_, write_timeout_usec_, std::move(callback));\n}\n\ninline bool SSLClient::is_ssl() const { return true; }\n\ninline bool SSLClient::verify_host(X509 *server_cert) const {\n  /* Quote from RFC2818 section 3.1 \"Server Identity\"\n\n     If a subjectAltName extension of type dNSName is present, that MUST\n     be used as the identity. Otherwise, the (most specific) Common Name\n     field in the Subject field of the certificate MUST be used. Although\n     the use of the Common Name is existing practice, it is deprecated and\n     Certification Authorities are encouraged to use the dNSName instead.\n\n     Matching is performed using the matching rules specified by\n     [RFC2459].  If more than one identity of a given type is present in\n     the certificate (e.g., more than one dNSName name, a match in any one\n     of the set is considered acceptable.) Names may contain the wildcard\n     character * which is considered to match any single domain name\n     component or component fragment. E.g., *.a.com matches foo.a.com but\n     not bar.foo.a.com. f*.com matches foo.com but not bar.com.\n\n     In some cases, the URI is specified as an IP address rather than a\n     hostname. In this case, the iPAddress subjectAltName must be present\n     in the certificate and must exactly match the IP in the URI.\n\n  */\n  return verify_host_with_subject_alt_name(server_cert) ||\n         verify_host_with_common_name(server_cert);\n}\n\ninline bool\nSSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {\n  auto ret = false;\n\n  auto type = GEN_DNS;\n\n  struct in6_addr addr6{};\n  struct in_addr addr{};\n  size_t addr_len = 0;\n\n#ifndef __MINGW32__\n  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {\n    type = GEN_IPADD;\n    addr_len = sizeof(struct in6_addr);\n  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {\n    type = GEN_IPADD;\n    addr_len = sizeof(struct in_addr);\n  }\n#endif\n\n  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(\n      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));\n\n  if (alt_names) {\n    auto dsn_matched = false;\n    auto ip_matched = false;\n\n    auto count = sk_GENERAL_NAME_num(alt_names);\n\n    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {\n      auto val = sk_GENERAL_NAME_value(alt_names, i);\n      if (val->type == type) {\n        auto name =\n            reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));\n        auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));\n\n        switch (type) {\n        case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;\n\n        case GEN_IPADD:\n          if (!memcmp(&addr6, name, addr_len) ||\n              !memcmp(&addr, name, addr_len)) {\n            ip_matched = true;\n          }\n          break;\n        }\n      }\n    }\n\n    if (dsn_matched || ip_matched) { ret = true; }\n  }\n\n  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(\n      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));\n  return ret;\n}\n\ninline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {\n  const auto subject_name = X509_get_subject_name(server_cert);\n\n  if (subject_name != nullptr) {\n    char name[BUFSIZ];\n    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,\n                                              name, sizeof(name));\n\n    if (name_len != -1) {\n      return check_host_name(name, static_cast<size_t>(name_len));\n    }\n  }\n\n  return false;\n}\n\ninline bool SSLClient::check_host_name(const char *pattern,\n                                       size_t pattern_len) const {\n  if (host_.size() == pattern_len && host_ == pattern) { return true; }\n\n  // Wildcard match\n  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484\n  std::vector<std::string> pattern_components;\n  detail::split(&pattern[0], &pattern[pattern_len], '.',\n                [&](const char *b, const char *e) {\n                  pattern_components.emplace_back(b, e);\n                });\n\n  if (host_components_.size() != pattern_components.size()) { return false; }\n\n  auto itr = pattern_components.begin();\n  for (const auto &h : host_components_) {\n    auto &p = *itr;\n    if (p != h && p != \"*\") {\n      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&\n                            !p.compare(0, p.size() - 1, h));\n      if (!partial_match) { return false; }\n    }\n    ++itr;\n  }\n\n  return true;\n}\n#endif\n\n// Universal client implementation\ninline Client::Client(const std::string &scheme_host_port)\n    : Client(scheme_host_port, std::string(), std::string()) {}\n\ninline Client::Client(const std::string &scheme_host_port,\n                      const std::string &client_cert_path,\n                      const std::string &client_key_path) {\n  const static std::regex re(\n      R\"((?:([a-z]+):\\/\\/)?(?:\\[([a-fA-F\\d:]+)\\]|([^:/?#]+))(?::(\\d+))?)\");\n\n  std::smatch m;\n  if (std::regex_match(scheme_host_port, m, re)) {\n    auto scheme = m[1].str();\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    if (!scheme.empty() && (scheme != \"http\" && scheme != \"https\")) {\n#else\n    if (!scheme.empty() && scheme != \"http\") {\n#endif\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n      std::string msg = \"'\" + scheme + \"' scheme is not supported.\";\n      throw std::invalid_argument(msg);\n#endif\n      return;\n    }\n\n    auto is_ssl = scheme == \"https\";\n\n    auto host = m[2].str();\n    if (host.empty()) { host = m[3].str(); }\n\n    auto port_str = m[4].str();\n    auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);\n\n    if (is_ssl) {\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,\n                                            client_key_path);\n      is_ssl_ = is_ssl;\n#endif\n    } else {\n      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,\n                                             client_key_path);\n    }\n  } else {\n    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)\n    // if port param below changes.\n    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,\n                                           client_cert_path, client_key_path);\n  }\n} // namespace detail\n\ninline Client::Client(const std::string &host, int port)\n    : cli_(detail::make_unique<ClientImpl>(host, port)) {}\n\ninline Client::Client(const std::string &host, int port,\n                      const std::string &client_cert_path,\n                      const std::string &client_key_path)\n    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,\n                                           client_key_path)) {}\n\ninline Client::~Client() = default;\n\ninline bool Client::is_valid() const {\n  return cli_ != nullptr && cli_->is_valid();\n}\n\ninline Result Client::Get(const std::string &path) { return cli_->Get(path); }\ninline Result Client::Get(const std::string &path, const Headers &headers) {\n  return cli_->Get(path, headers);\n}\ninline Result Client::Get(const std::string &path, Progress progress) {\n  return cli_->Get(path, std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          Progress progress) {\n  return cli_->Get(path, headers, std::move(progress));\n}\ninline Result Client::Get(const std::string &path,\n                          ContentReceiver content_receiver) {\n  return cli_->Get(path, std::move(content_receiver));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          ContentReceiver content_receiver) {\n  return cli_->Get(path, headers, std::move(content_receiver));\n}\ninline Result Client::Get(const std::string &path,\n                          ContentReceiver content_receiver, Progress progress) {\n  return cli_->Get(path, std::move(content_receiver), std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          ContentReceiver content_receiver, Progress progress) {\n  return cli_->Get(path, headers, std::move(content_receiver),\n                   std::move(progress));\n}\ninline Result Client::Get(const std::string &path,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver) {\n  return cli_->Get(path, std::move(response_handler),\n                   std::move(content_receiver));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver) {\n  return cli_->Get(path, headers, std::move(response_handler),\n                   std::move(content_receiver));\n}\ninline Result Client::Get(const std::string &path,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver, Progress progress) {\n  return cli_->Get(path, std::move(response_handler),\n                   std::move(content_receiver), std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver, Progress progress) {\n  return cli_->Get(path, headers, std::move(response_handler),\n                   std::move(content_receiver), std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Params &params,\n                          const Headers &headers, Progress progress) {\n  return cli_->Get(path, params, headers, std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Params &params,\n                          const Headers &headers,\n                          ContentReceiver content_receiver, Progress progress) {\n  return cli_->Get(path, params, headers, std::move(content_receiver),\n                   std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Params &params,\n                          const Headers &headers,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver, Progress progress) {\n  return cli_->Get(path, params, headers, std::move(response_handler),\n                   std::move(content_receiver), std::move(progress));\n}\n\ninline Result Client::Head(const std::string &path) { return cli_->Head(path); }\ninline Result Client::Head(const std::string &path, const Headers &headers) {\n  return cli_->Head(path, headers);\n}\n\ninline Result Client::Post(const std::string &path) { return cli_->Post(path); }\ninline Result Client::Post(const std::string &path, const Headers &headers) {\n  return cli_->Post(path, headers);\n}\ninline Result Client::Post(const std::string &path, const char *body,\n                           size_t content_length,\n                           const std::string &content_type) {\n  return cli_->Post(path, body, content_length, content_type);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const char *body, size_t content_length,\n                           const std::string &content_type) {\n  return cli_->Post(path, headers, body, content_length, content_type);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const char *body, size_t content_length,\n                           const std::string &content_type, Progress progress) {\n  return cli_->Post(path, headers, body, content_length, content_type,\n                    progress);\n}\ninline Result Client::Post(const std::string &path, const std::string &body,\n                           const std::string &content_type) {\n  return cli_->Post(path, body, content_type);\n}\ninline Result Client::Post(const std::string &path, const std::string &body,\n                           const std::string &content_type, Progress progress) {\n  return cli_->Post(path, body, content_type, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const std::string &body,\n                           const std::string &content_type) {\n  return cli_->Post(path, headers, body, content_type);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const std::string &body,\n                           const std::string &content_type, Progress progress) {\n  return cli_->Post(path, headers, body, content_type, progress);\n}\ninline Result Client::Post(const std::string &path, size_t content_length,\n                           ContentProvider content_provider,\n                           const std::string &content_type) {\n  return cli_->Post(path, content_length, std::move(content_provider),\n                    content_type);\n}\ninline Result Client::Post(const std::string &path,\n                           ContentProviderWithoutLength content_provider,\n                           const std::string &content_type) {\n  return cli_->Post(path, std::move(content_provider), content_type);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           size_t content_length,\n                           ContentProvider content_provider,\n                           const std::string &content_type) {\n  return cli_->Post(path, headers, content_length, std::move(content_provider),\n                    content_type);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           ContentProviderWithoutLength content_provider,\n                           const std::string &content_type) {\n  return cli_->Post(path, headers, std::move(content_provider), content_type);\n}\ninline Result Client::Post(const std::string &path, const Params &params) {\n  return cli_->Post(path, params);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const Params &params) {\n  return cli_->Post(path, headers, params);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const Params &params, Progress progress) {\n  return cli_->Post(path, headers, params, progress);\n}\ninline Result Client::Post(const std::string &path,\n                           const MultipartFormDataItems &items) {\n  return cli_->Post(path, items);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const MultipartFormDataItems &items) {\n  return cli_->Post(path, headers, items);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const MultipartFormDataItems &items,\n                           const std::string &boundary) {\n  return cli_->Post(path, headers, items, boundary);\n}\ninline Result\nClient::Post(const std::string &path, const Headers &headers,\n             const MultipartFormDataItems &items,\n             const MultipartFormDataProviderItems &provider_items) {\n  return cli_->Post(path, headers, items, provider_items);\n}\ninline Result Client::Put(const std::string &path) { return cli_->Put(path); }\ninline Result Client::Put(const std::string &path, const char *body,\n                          size_t content_length,\n                          const std::string &content_type) {\n  return cli_->Put(path, body, content_length, content_type);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const char *body, size_t content_length,\n                          const std::string &content_type) {\n  return cli_->Put(path, headers, body, content_length, content_type);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const char *body, size_t content_length,\n                          const std::string &content_type, Progress progress) {\n  return cli_->Put(path, headers, body, content_length, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const std::string &body,\n                          const std::string &content_type) {\n  return cli_->Put(path, body, content_type);\n}\ninline Result Client::Put(const std::string &path, const std::string &body,\n                          const std::string &content_type, Progress progress) {\n  return cli_->Put(path, body, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const std::string &body,\n                          const std::string &content_type) {\n  return cli_->Put(path, headers, body, content_type);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const std::string &body,\n                          const std::string &content_type, Progress progress) {\n  return cli_->Put(path, headers, body, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, size_t content_length,\n                          ContentProvider content_provider,\n                          const std::string &content_type) {\n  return cli_->Put(path, content_length, std::move(content_provider),\n                   content_type);\n}\ninline Result Client::Put(const std::string &path,\n                          ContentProviderWithoutLength content_provider,\n                          const std::string &content_type) {\n  return cli_->Put(path, std::move(content_provider), content_type);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          size_t content_length,\n                          ContentProvider content_provider,\n                          const std::string &content_type) {\n  return cli_->Put(path, headers, content_length, std::move(content_provider),\n                   content_type);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          ContentProviderWithoutLength content_provider,\n                          const std::string &content_type) {\n  return cli_->Put(path, headers, std::move(content_provider), content_type);\n}\ninline Result Client::Put(const std::string &path, const Params &params) {\n  return cli_->Put(path, params);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const Params &params) {\n  return cli_->Put(path, headers, params);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const Params &params, Progress progress) {\n  return cli_->Put(path, headers, params, progress);\n}\ninline Result Client::Put(const std::string &path,\n                          const MultipartFormDataItems &items) {\n  return cli_->Put(path, items);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const MultipartFormDataItems &items) {\n  return cli_->Put(path, headers, items);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const MultipartFormDataItems &items,\n                          const std::string &boundary) {\n  return cli_->Put(path, headers, items, boundary);\n}\ninline Result\nClient::Put(const std::string &path, const Headers &headers,\n            const MultipartFormDataItems &items,\n            const MultipartFormDataProviderItems &provider_items) {\n  return cli_->Put(path, headers, items, provider_items);\n}\ninline Result Client::Patch(const std::string &path) {\n  return cli_->Patch(path);\n}\ninline Result Client::Patch(const std::string &path, const char *body,\n                            size_t content_length,\n                            const std::string &content_type) {\n  return cli_->Patch(path, body, content_length, content_type);\n}\ninline Result Client::Patch(const std::string &path, const char *body,\n                            size_t content_length,\n                            const std::string &content_type,\n                            Progress progress) {\n  return cli_->Patch(path, body, content_length, content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const char *body, size_t content_length,\n                            const std::string &content_type) {\n  return cli_->Patch(path, headers, body, content_length, content_type);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const char *body, size_t content_length,\n                            const std::string &content_type,\n                            Progress progress) {\n  return cli_->Patch(path, headers, body, content_length, content_type,\n                     progress);\n}\ninline Result Client::Patch(const std::string &path, const std::string &body,\n                            const std::string &content_type) {\n  return cli_->Patch(path, body, content_type);\n}\ninline Result Client::Patch(const std::string &path, const std::string &body,\n                            const std::string &content_type,\n                            Progress progress) {\n  return cli_->Patch(path, body, content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const std::string &body,\n                            const std::string &content_type) {\n  return cli_->Patch(path, headers, body, content_type);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const std::string &body,\n                            const std::string &content_type,\n                            Progress progress) {\n  return cli_->Patch(path, headers, body, content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, size_t content_length,\n                            ContentProvider content_provider,\n                            const std::string &content_type) {\n  return cli_->Patch(path, content_length, std::move(content_provider),\n                     content_type);\n}\ninline Result Client::Patch(const std::string &path,\n                            ContentProviderWithoutLength content_provider,\n                            const std::string &content_type) {\n  return cli_->Patch(path, std::move(content_provider), content_type);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            size_t content_length,\n                            ContentProvider content_provider,\n                            const std::string &content_type) {\n  return cli_->Patch(path, headers, content_length, std::move(content_provider),\n                     content_type);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            ContentProviderWithoutLength content_provider,\n                            const std::string &content_type) {\n  return cli_->Patch(path, headers, std::move(content_provider), content_type);\n}\ninline Result Client::Delete(const std::string &path) {\n  return cli_->Delete(path);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers) {\n  return cli_->Delete(path, headers);\n}\ninline Result Client::Delete(const std::string &path, const char *body,\n                             size_t content_length,\n                             const std::string &content_type) {\n  return cli_->Delete(path, body, content_length, content_type);\n}\ninline Result Client::Delete(const std::string &path, const char *body,\n                             size_t content_length,\n                             const std::string &content_type,\n                             Progress progress) {\n  return cli_->Delete(path, body, content_length, content_type, progress);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const char *body, size_t content_length,\n                             const std::string &content_type) {\n  return cli_->Delete(path, headers, body, content_length, content_type);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const char *body, size_t content_length,\n                             const std::string &content_type,\n                             Progress progress) {\n  return cli_->Delete(path, headers, body, content_length, content_type,\n                      progress);\n}\ninline Result Client::Delete(const std::string &path, const std::string &body,\n                             const std::string &content_type) {\n  return cli_->Delete(path, body, content_type);\n}\ninline Result Client::Delete(const std::string &path, const std::string &body,\n                             const std::string &content_type,\n                             Progress progress) {\n  return cli_->Delete(path, body, content_type, progress);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const std::string &body,\n                             const std::string &content_type) {\n  return cli_->Delete(path, headers, body, content_type);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const std::string &body,\n                             const std::string &content_type,\n                             Progress progress) {\n  return cli_->Delete(path, headers, body, content_type, progress);\n}\ninline Result Client::Options(const std::string &path) {\n  return cli_->Options(path);\n}\ninline Result Client::Options(const std::string &path, const Headers &headers) {\n  return cli_->Options(path, headers);\n}\n\ninline bool Client::send(Request &req, Response &res, Error &error) {\n  return cli_->send(req, res, error);\n}\n\ninline Result Client::send(const Request &req) { return cli_->send(req); }\n\ninline void Client::stop() { cli_->stop(); }\n\ninline std::string Client::host() const { return cli_->host(); }\n\ninline int Client::port() const { return cli_->port(); }\n\ninline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }\n\ninline socket_t Client::socket() const { return cli_->socket(); }\n\ninline void\nClient::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {\n  cli_->set_hostname_addr_map(std::move(addr_map));\n}\n\ninline void Client::set_default_headers(Headers headers) {\n  cli_->set_default_headers(std::move(headers));\n}\n\ninline void Client::set_header_writer(\n    std::function<ssize_t(Stream &, Headers &)> const &writer) {\n  cli_->set_header_writer(writer);\n}\n\ninline void Client::set_address_family(int family) {\n  cli_->set_address_family(family);\n}\n\ninline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }\n\ninline void Client::set_socket_options(SocketOptions socket_options) {\n  cli_->set_socket_options(std::move(socket_options));\n}\n\ninline void Client::set_connection_timeout(time_t sec, time_t usec) {\n  cli_->set_connection_timeout(sec, usec);\n}\n\ninline void Client::set_read_timeout(time_t sec, time_t usec) {\n  cli_->set_read_timeout(sec, usec);\n}\n\ninline void Client::set_write_timeout(time_t sec, time_t usec) {\n  cli_->set_write_timeout(sec, usec);\n}\n\ninline void Client::set_basic_auth(const std::string &username,\n                                   const std::string &password) {\n  cli_->set_basic_auth(username, password);\n}\ninline void Client::set_bearer_token_auth(const std::string &token) {\n  cli_->set_bearer_token_auth(token);\n}\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::set_digest_auth(const std::string &username,\n                                    const std::string &password) {\n  cli_->set_digest_auth(username, password);\n}\n#endif\n\ninline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }\ninline void Client::set_follow_location(bool on) {\n  cli_->set_follow_location(on);\n}\n\ninline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }\n\ninline void Client::set_compress(bool on) { cli_->set_compress(on); }\n\ninline void Client::set_decompress(bool on) { cli_->set_decompress(on); }\n\ninline void Client::set_interface(const std::string &intf) {\n  cli_->set_interface(intf);\n}\n\ninline void Client::set_proxy(const std::string &host, int port) {\n  cli_->set_proxy(host, port);\n}\ninline void Client::set_proxy_basic_auth(const std::string &username,\n                                         const std::string &password) {\n  cli_->set_proxy_basic_auth(username, password);\n}\ninline void Client::set_proxy_bearer_token_auth(const std::string &token) {\n  cli_->set_proxy_bearer_token_auth(token);\n}\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::set_proxy_digest_auth(const std::string &username,\n                                          const std::string &password) {\n  cli_->set_proxy_digest_auth(username, password);\n}\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::enable_server_certificate_verification(bool enabled) {\n  cli_->enable_server_certificate_verification(enabled);\n}\n\ninline void Client::enable_server_hostname_verification(bool enabled) {\n  cli_->enable_server_hostname_verification(enabled);\n}\n\ninline void Client::set_server_certificate_verifier(\n    std::function<bool(SSL *ssl)> verifier) {\n  cli_->set_server_certificate_verifier(verifier);\n}\n#endif\n\ninline void Client::set_logger(Logger logger) {\n  cli_->set_logger(std::move(logger));\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,\n                                     const std::string &ca_cert_dir_path) {\n  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);\n}\n\ninline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {\n  if (is_ssl_) {\n    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);\n  } else {\n    cli_->set_ca_cert_store(ca_cert_store);\n  }\n}\n\ninline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {\n  set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));\n}\n\ninline long Client::get_openssl_verify_result() const {\n  if (is_ssl_) {\n    return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();\n  }\n  return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???\n}\n\ninline SSL_CTX *Client::ssl_context() const {\n  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }\n  return nullptr;\n}\n#endif\n\n// ----------------------------------------------------------------------------\n\n} // namespace httplib\n\n#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)\n#undef poll\n#endif\n\n#endif // CPPHTTPLIB_HTTPLIB_H"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/interface/Interface.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include \"Interface.h\"\r\n\r\n#include \"MeshViewer.h\"\r\n#include <core/view/InteractiveCameraHandler.hpp>\r\n\r\n#include <imgui/imgui.h>\r\n\r\nnamespace sibr {\r\n\r\n\tMultiViewInterface::MultiViewInterface()\r\n\t{\r\n\t\tnumImgs = 0;\r\n\t\tcurrentLayer = 0;\r\n\t\tcurrentScale = 0;\r\n\t\tgrid = sibr::Vector2i(4, 4);\r\n\t\thightligthChanged = false;\r\n\t\treproMeshMode = sibr::Mesh::FillRenderMode;\r\n\t\treproMeshBackFace = true;\r\n\r\n\t\timagesViewBase = std::make_shared<MultiViewInterfaceView>(this, MultiViewInterfaceView::ViewType::IMAGES);\r\n\t\tmeshViewBase = std::make_shared<MultiViewInterfaceView>(this, MultiViewInterfaceView::ViewType::MESH);\r\n\t}\r\n\r\n\tvoid MultiViewInterface::displayLoop(sibr::Window & window, std::function<void(MultiViewInterface*)> f)\r\n\t{\r\n\t\t\r\n\t\tif (layersData.size() == 0) {\r\n\t\t\tSIBR_ERR << \" cant display interface without image layer added\" << std::endl;\r\n\t\t}\r\n\r\n\t\tutils.initAllShaders();\r\n\r\n\t\tglClearColor(0.8f, 0.8f, 0.8f, 1.0f);\r\n\r\n\t\twindow.size(window.size().x(), (int)std::ceil(window.size().x() / scalesData[0].imRatio));\r\n\r\n\t\tstd::cout << \" window size : \" << window.size().transpose() << std::endl;\r\n\r\n\t\timagesView.viewport = sibr::Viewport(&window.viewport(),0,0,1,1);\r\n\t\timagesView.isActive = true;\r\n\r\n\t\tif (cpuMesh.get()) {\r\n\t\t\tmeshViewer.setMainMesh(*cpuMesh, sibr::Mesh::FillRenderMode, false, true);\r\n\t\t} else {\r\n\t\t\tstd::cout << \" no mesh \" << std::endl;\r\n\t\t}\r\n\t\t\r\n\t\twinSize = window.size().cast<float>();\r\n\r\n\t\twhile (window.isOpened()) {\r\n\r\n\t\t\t//std::cout << \".\" << std::flush;\r\n\r\n\t\t\twindow.makeContextCurrent();\r\n\t\t\tsibr::Input::poll();\r\n\r\n\t\t\twinSize = window.size().cast<float>();\r\n\t\t\timagesInput = sibr::Input::subInput(sibr::Input::global(), imagesView.viewport);\r\n\t\t\tmeshInput = sibr::Input::subInput(sibr::Input::global(), meshView.viewport);\r\n\r\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tupdate(window, sibr::Input::global());\r\n\r\n\t\t\tonGui();\r\n\r\n\t\t\trender();\r\n\r\n\t\t\tf(this);\r\n\r\n\t\t\twindow.swapBuffer();\r\n\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::addCameras(const std::vector<InputCamera::Ptr>& input_cams)\r\n\t{\r\n\t\tcams = input_cams;\r\n\t}\r\n\r\n\tvoid MultiViewInterface::addMesh(const sibr::Mesh::Ptr &mesh)\r\n\t{\r\n\t\tcpuMesh = mesh;\r\n\t\tcpuMesh->generateNormals();\r\n\t}\r\n\r\n\tvoid MultiViewInterface::addMesh(const sibr::Mesh & mesh)\r\n\t{\r\n\t\tcpuMesh = std::make_shared<sibr::Mesh>();\r\n\t\tcpuMesh->vertices(mesh.vertices());\r\n\t\tcpuMesh->triangles(mesh.triangles());\r\n\t\tcpuMesh->generateNormals();\r\n\t}\r\n\r\n\tvoid MultiViewInterface::update(sibr::Window & window, const sibr::Input & input)\r\n\t{\r\n\t\tupdateImageView(imagesView.viewport, imagesInput);\r\n\t\tupdateMeshView(meshInput, window);\t\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateImageView(const sibr::Viewport & viewport, const sibr::Input & input)\r\n\t{\r\n\t\tsibr::Vector2f winSize = viewport.finalSize();\r\n\t\tcurrentActivePos = pixFromScreenPos(input.mousePosition(), winSize);\r\n\t\timagesViewBase->currentActivePos = currentActivePos;\r\n\r\n\t\timgPixelScreenSize = screenPosPixelsFloat({ 0,{ 1,1 } }, winSize) - screenPosPixelsFloat({ 0,{ 0,0 } }, winSize);\r\n\r\n\t\tupdateCurrentLayer(input);\r\n\r\n\t\tif (input.key().isActivated(sibr::Key::LeftShift)) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tupdateZoomBox(input, winSize);\r\n\t\t//updateCenter(imagesInput, imagesViewSize);\r\n\t\tupdateZoomScroll(input);\r\n\t\tupdateDrag(input, winSize);\r\n\t}\r\n\r\n\tvoid MultiViewInterface::render()\r\n\t{\r\n\t\trenderImageView(imagesView.viewport);\r\n\r\n\r\n\t\t////if (hightligthChanged) {\r\n\t\t//renderHighlightPixels();\r\n\t\t////}\r\n\t\t//displayHighlightedPixels(sibr::Vector3f(0, 1, 0), 0.15);\r\n\r\n\t\tdisplayMesh(meshView.viewport);\r\n\r\n\t\t//if (sibr::Input::global().key().isActivated(sibr::Key::C)) {\r\n\t\t//\tint r = 50;\r\n\t\t//\tutils.rectanglePixels(sibr::Vector3f(1, 0, 1), sibr::Input::global().mousePosition().cast<float>(), sibr::Vector2f(r, r), true, 0.15f, imagesViewSize);\r\n\t\t//\tutils.circlePixels(sibr::Vector3f(0, 1, 1), sibr::Input::global().mousePosition().cast<float>(), r, true, 0.15f, imagesViewSize);\r\n\t\t//\t\r\n\t\t//}\r\n\r\n\t}\r\n\r\n\tvoid MultiViewInterface::renderImageView(const sibr::Viewport & viewport)\r\n\t{\r\n\t\tsibr::Vector2f imagesViewSize = viewport.finalSize();\r\n\t\tdisplayImages(viewport);\r\n\r\n\t\tdisplayZoom(viewport);\r\n\r\n\t\tif (currentActivePos.isDefined) {\r\n\t\t\thighlightPixel(currentActivePos, viewport);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tsibr::ViewBase::Ptr MultiViewInterface::getViewBase(MultiViewInterfaceView::ViewType type)\r\n\t{\r\n\t\tif (type == MultiViewInterfaceView::ViewType::IMAGES) {\r\n\t\t\treturn sibr::ViewBase::Ptr(imagesViewBase.get());\r\n\t\t} else {\r\n\t\t\treturn sibr::ViewBase::Ptr(meshViewBase.get());\r\n\t\t}\r\n\t\t\r\n\t}\r\n\r\n\tvoid MultiViewInterface::onGui()\r\n\t{\r\n\t\tImGui::Separator();\r\n\t\tif (imagesLayers.size() != 1) {\r\n\t\t\tImGui::SliderInt(\"Laplacian scale\", &currentScale, 0, (int)imagesLayers.size() - 1);\r\n\t\t\tImGui::Separator();\r\n\t\t}\r\n\t\tconst size_t nLayers = layersData.size();\r\n\t\tif (nLayers > 1) {\r\n\t\t\tImGui::Text(\"Image Layers : \");\r\n\t\t\tImGui::Separator();\r\n\r\n\t\t\tfor (size_t n = 0; n < nLayers; ++n) {\r\n\t\t\t\tif (ImGui::Selectable(layersData[n].name.c_str(), currentLayer == n)) {\r\n\t\t\t\t\tcurrentLayer = (int)n;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tImGui::Separator();\r\n\t\t}\r\n\t\tif (currentScale == 0 && currentActivePos.isDefined) {\r\n\t\t\tstd::stringstream ss;\r\n\t\t\tss << \"Image : \" << currentActivePos.im << \", pixel : \" << currentActivePos.pos << std::endl;\r\n\t\t\tImGui::Text(ss.str().c_str());\r\n\t\t\tif (currentActivePos.isDefined) {\r\n\t\t\t\t//std::cout << imagesPtr[currentLayer][currentActivePos.im] << std::endl;\r\n\t\t\t\t//std::cout << imagesPtr[currentLayer][currentActivePos.im]->size() << std::endl;\r\n/*\t\t\t\tif (imagesPtr[currentLayer][currentActivePos.im]) {\r\n\t\t\t\t\tImGui::Text(imagesPtr[currentLayer][currentActivePos.im]->pixelStr(currentActivePos.pos).c_str());\r\n\t\t\t\t}\t*/\t\t\r\n\t\t\t}\r\n\t\t\tImGui::Separator();\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t//MultiViewInterface::~MultiViewInterface()\r\n\t//{\r\n\t//\tCHECK_GL_ERROR;\r\n\r\n\t//\tCHECK_GL_ERROR;\r\n\t//}\r\n\r\n\tPixPos MultiViewInterface::pixFromScreenPos(const sibr::Vector2i & posScreen, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\tUV01 uvScreen = UV10::from((posScreen.cast<float>()+0.5f*sibr::Vector2f(1,1)).cwiseQuotient(winSize));\r\n\t\t\r\n\t\t//std::cout << uvScreen.transpose() << std::endl;\r\n\r\n\t\tsibr::Vector2f posF = viewRectangle.tl() + (viewRectangle.br() - viewRectangle.tl()).cwiseProduct(uvScreen);\r\n\t\tposF.y() = 1.0f - posF.y();\r\n\t\t\r\n\t\tposF = posF.cwiseProduct(grid.cast<float>());\r\n\r\n\t\t//std::cout << posF.transpose() << \" \" << numImgs << std::endl;\r\n\r\n\t\tif (posF.x() < 0 || posF.y() < 0 || posF.x() >= grid.x() /* || posF.y() >= grid.y()  */ ) {\r\n\t\t\treturn PixPos();\r\n\t\t}\r\n\r\n\t\tint x = (int)std::floor(posF.x());\r\n\t\tint y = (int)std::floor(posF.y());\r\n\t\tsibr::Vector2f frac = posF - sibr::Vector2f(x, y);\r\n\r\n\t\tint n = x + grid.x() * y;\r\n\t\tint j = (int)std::floor(frac.x()*scalesData[currentScale].imSize.x());\r\n\t\tint i = (int)std::floor(frac.y()*scalesData[currentScale].imSize.y());\r\n\r\n\t\tif (n >= numImgs) {\r\n\t\t\treturn PixPos();\r\n\t\t}\r\n\r\n\t\treturn PixPos(n, sibr::Vector2i(j, i));\r\n\t}\r\n\r\n\tUV01 MultiViewInterface::screenPos(const PixPos & pix)\r\n\t{\r\n\t\tsibr::Vector2f pos = (pix.pos.cast<float>().cwiseQuotient(scalesData[currentScale].imSize) +\r\n\t\t\tsibr::Vector2f(pix.im % grid.x(), pix.im / grid.x())).cwiseQuotient(grid.cast<float>());\r\n\t\tpos.y() = 1.0f - pos.y();\r\n\t\treturn UV01::from((pos - viewRectangle.tl()).cwiseQuotient(viewRectangle.br() - viewRectangle.tl()));\r\n\t}\r\n\r\n\tUV01 MultiViewInterface::screenPosPixelCenter(const PixPos & pix)\r\n\t{\r\n\t\tsibr::Vector2f pos = ((pix.pos.cast<float>()+sibr::Vector2f(0.5,0.5)).cwiseQuotient(scalesData[currentScale].imSize) +\r\n\t\t\tsibr::Vector2f(pix.im % grid.x(), pix.im / grid.x())).cwiseQuotient(grid.cast<float>());\r\n\t\tpos.y() = 1.0f - pos.y();\r\n\t\treturn UV01::from((pos - viewRectangle.tl()).cwiseQuotient(viewRectangle.br() - viewRectangle.tl()));\r\n\t}\r\n\r\n\tsibr::Vector2i MultiViewInterface::screenPosPixels(const PixPos & pix, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\treturn (screenPos(pix).cwiseProduct(winSize)).cast<int>();\r\n\t}\r\n\r\n\tsibr::Vector2f MultiViewInterface::screenPosPixelsFloat(const PixPos & pix, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\treturn screenPosPixelCenter(pix).cwiseProduct(winSize);\r\n\t}\r\n\r\n\t//void MultiViewInterface::setupFromImSizeAndNumIm(LayerData & layerData, const sibr::Vector2i & imSize)\r\n\t//{\r\n\t//\timRatio = imSize[0] / (float)imSize[1];\r\n\t//\timSizeF = imSize.cast<float>();\r\n\t//\tnumImgs = numIms;\r\n\t//}\r\n\r\n\tvoid MultiViewInterface::addHighlightPixel(const PixPos & pix, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\thighlightedPixels.push_back(pix);\r\n\t\t//hightligthChanged = true;\r\n\t}\r\n\r\n\tvoid MultiViewInterface::renderHighlightPixels()\r\n\t{\r\n\t\tint pixsSize = (int)highlightedPixels.size();\r\n\r\n\t\tif (pixsSize == 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (!highlightedPixelsMesh.get()) {\r\n\t\t\thighlightedPixelsMesh = std::make_shared<sibr::Mesh>();\r\n\t\t}\r\n\r\n\t\tsibr::Mesh::Vertices vs(4 * pixsSize);\r\n\t\tsibr::Mesh::Triangles ts(2 * pixsSize);\r\n\r\n\t\tunsigned pixId = 0;\r\n\t\tfor (const auto & pix : highlightedPixels) {\r\n\t\t\tUV11 tl = screenPos(pix);\r\n\t\t\tPixPos otherCorner(pix.im, pix.pos + sibr::Vector2i(1, 1));\r\n\t\t\tUV11 br = screenPos(otherCorner);\r\n\r\n\t\t\tvs[4 * pixId + 0] = { tl.x(), tl.y() , 0 };\r\n\t\t\tvs[4 * pixId + 1] = { tl.x(), br.y() , 0 };\r\n\t\t\tvs[4 * pixId + 2] = { br.x(), br.y() , 0 };\r\n\t\t\tvs[4 * pixId + 3] = { br.x(), tl.y() , 0 };\r\n\r\n\t\t\tts[2 * pixId + 0] = { 4 * pixId + 0,4 * pixId + 1,4 * pixId + 2 };\r\n\t\t\tts[2 * pixId + 1] = { 4 * pixId + 0,4 * pixId + 2,4 * pixId + 3 };\r\n\r\n\t\t\t++pixId;\r\n\t\t}\r\n\r\n\r\n\r\n\t\thighlightedPixelsMesh->vertices(vs);\r\n\t\thighlightedPixelsMesh->triangles(ts);\r\n\r\n\t\t//hightligthChanged = false;\r\n\t}\r\n\r\n\tvoid MultiViewInterface::highlightPixel(const PixPos & pix, const sibr::Viewport & viewport, const sibr::Vector3f & color, const sibr::Vector2f & pixScreenSize)\r\n\t{\r\n\t\tUV01 pixTl = screenPos(pix);\r\n\t\tPixPos otherCorner(pix.im, pix.pos + sibr::Vector2i(1, 1));\r\n\t\tUV01 pixBR = screenPos(otherCorner);\r\n\r\n\t\tviewport.bind();\r\n\t\t\t\t\r\n\t\tif ((pixBR-pixTl).cwiseProduct(viewport.finalSize()).cwiseAbs().minCoeff() < 2.0f) {\r\n\t\t\t//if pixel size in screen space is tinier than 2 screen pix\r\n\t\t\tutils.rectanglePixels(color, 0.5f*(pixTl+pixBR).cwiseProduct(viewport.finalSize()), pixScreenSize, true, 0.15f, viewport.finalSize());\r\n\t\t} else {\t\r\n\t\t\t//otherwise hightligh pixel intirely\r\n\t\t\tutils.rectangle(color, pixTl, pixBR, true, 0.15f);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tvoid MultiViewInterface::displayImages(const sibr::Viewport & viewport)\r\n\t{\r\n\t\t\r\n\t\t\t//const sibr::Vector2f & winSize;\r\n\t\t//std::cout << imagesView.viewport.left() << \" \" << imagesView.viewport.right() << \" \" << imagesView.viewport.top() << \" \" << imagesView.viewport.bottom() << std::endl;\r\n\t\tviewport.bind();\r\n\r\n\t\tviewport.clear(sibr::Vector3f(0.7f, 0.7f, 0.7f));\r\n\t\t//glClear(GL_COLOR_BUFFER_BIT);\r\n\r\n\t\tutils.multiViewShader.begin();\r\n\r\n\t\tutils.numImgsGL.set((int)imagesLayers[currentScale][currentLayer]->depth() - 1);\r\n\t\tsibr::Vector2f gridF = grid.cast<float>();\r\n\t\tutils.gridGL.set(gridF);\r\n\r\n\t\tutils.multiViewTopLeftGL.set(viewRectangle.tl());\r\n\t\tutils.multiViewBottomRightGL.set(viewRectangle.br());\r\n\r\n\t\tglActiveTexture(GL_TEXTURE0);\r\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, imagesLayers[currentScale][currentLayer]->handle());\r\n\t\tsibr::RenderUtility::renderScreenQuad();\r\n\r\n\t\tutils.multiViewShader.end();\r\n\r\n\t\tfor (int i = 0; i <(int)imagesLayers[currentScale][currentLayer]->depth(); ++i) {\r\n\t\t\tUV01 imTl = screenPos(PixPos(i, sibr::Vector2i(0, 0)));\r\n\t\t\tUV01 imBR = screenPos(PixPos(i, scalesData[currentScale].imSize.cast<int>()));\r\n\t\t\tutils.rectangle(sibr::Vector3f(0, 0, 0), imTl, imBR, false, 1.0);\r\n\t\t}\r\n\r\n\t\t//utils.rectangle(sibr::Vector3f(0, 1, 0), UV01(0.25,0.25), UV01(0.75, 0.75), true, 0.15);\r\n\t\t//utils.circle(sibr::Vector3f(0, 0, 1), UV01(0.33, 0.33), 0.1, true, 0.25);\r\n\r\n\t\tif (reproMesh.get()) {\r\n\t\t\tutils.meshViewShader.begin();\r\n\t\t\tutils.alphaMeshGL.set(0.25f);\r\n\t\t\tutils.colorMeshGL.set(sibr::Vector3f(1, 0, 1));\r\n\r\n\t\t\tsibr::Vector2i viewPortSize(imagesView.viewport.finalWidth(), imagesView.viewport.finalHeight());\r\n\r\n\t\t\tEigen::AlignedBox2d winBox;\r\n\t\t\twinBox.extend(sibr::Vector2d(0, 0));\r\n\r\n\t\t\twinBox.extend(viewPortSize.cast<double>());\r\n\t\t\t//std::cout << std::endl;\r\n\r\n\t\t\t//std::cout << \" winbox \" << (winBox.center()-0.5*winBox.diagonal()).transpose() << \" \" << (winBox.center() + 0.5*winBox.diagonal()).transpose() << std::endl;\r\n\t\t\tfor (int i = 0; i <(int)cams.size(); ++i) {\r\n\t\t\t\tutils.mvp.set(cams[i]->viewproj());\r\n\r\n\t\t\t\t//std::cout << i << std::endl;\r\n\t\t\t\tglClearDepth(1.0);\r\n\t\t\t\tglClear(GL_DEPTH_BUFFER_BIT);\r\n\r\n\t\t\t\tsibr::Vector2i tlImgPix = screenPosPixels(PixPos(i, sibr::Vector2i(0, cams[i]->h() - 1)), viewPortSize.cast<float>());\r\n\t\t\t\tsibr::Vector2i brImgPix = screenPosPixels(PixPos(i, sibr::Vector2i(cams[i]->w() - 1, 0)), viewPortSize.cast<float>());\r\n\r\n\t\t\t\tEigen::AlignedBox2d box;\r\n\t\t\t\tbox.extend(tlImgPix.cast<double>());\r\n\t\t\t\tbox.extend(brImgPix.cast<double>());\r\n\t\t\t\t//std::cout << \"\\t box \" << (box.center() - 0.5*box.diagonal()).transpose() << \" \" << (box.center() + 0.5*box.diagonal()).transpose() << std::endl;\r\n\r\n\t\t\t\tEigen::AlignedBox2d renderBox = winBox.intersection(box);\r\n\r\n\t\t\t\tif (renderBox.isEmpty()) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//tlImgPix = renderBox.corner(Eigen::AlignedBox2d::CornerType::BottomLeft).cast<int>();\r\n\t\t\t\t//\tbrImgPix = renderBox.corner(Eigen::AlignedBox2d::CornerType::TopRight).cast<int>();\r\n\t\t\t\t//std::cout << \"\\t renderBox \" << (renderBox.center() - 0.5*renderBox.diagonal()).transpose() << \" \" << (renderBox.center() + 0.5*renderBox.diagonal()).transpose() << std::endl;\r\n\r\n\t\t\t\t//std::cout << tlImgPix.x() << \" \" << tlImgPix.y() << \" \" << (brImgPix - tlImgPix).x() << \" \" << (brImgPix - tlImgPix).y() << std::endl;\r\n\t\t\t\tglViewport(tlImgPix.x(), tlImgPix.y(), std::abs((brImgPix - tlImgPix).x()), std::abs((brImgPix - tlImgPix).y()));\r\n\r\n\t\t\t\treproMesh->render(true, reproMeshBackFace, reproMeshMode);\r\n\t\t\t}\r\n\r\n\t\t\tutils.meshViewShader.end();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::displayMesh(const sibr::Viewport & viewport)\r\n\t{\r\n\t\tif (meshView.isActive) {\t\t\r\n\t\t\tmeshViewer.render(viewport);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::displayZoom(const sibr::Viewport & viewport)\r\n\t{\r\n\t\tif (zoomSelection.isActive) {\r\n\t\t\tviewport.bind();\r\n\t\t\tUV01 tl = UV01::from(zoomSelection.first.cast<float>().cwiseQuotient(viewport.finalSize()));\r\n\t\t\tUV01 br = UV01::from(zoomSelection.second.cast<float>().cwiseQuotient(viewport.finalSize()));\r\n\t\t\tutils.rectangle(sibr::Vector3f(1, 0, 0), tl, br, true, 0.15f);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::displayHighlightedPixels(const sibr::Vector3f & color, float alpha)\r\n\t{\r\n\t\tif (!highlightedPixelsMesh.get()) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tutils.baseShader.begin();\r\n\r\n\t\tutils.scalingGL.set(1.0f);\r\n\t\tutils.translationGL.set(sibr::Vector2f(0, 0));\r\n\t\tutils.colorGL.set(color);\r\n\t\tutils.alphaGL.set(alpha);\r\n\t\tglEnable(GL_BLEND);\r\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r\n\t\tglBlendEquation(GL_FUNC_ADD);\r\n\r\n\t\thighlightedPixelsMesh->render(false, false);\r\n\r\n\t\tutils.alphaGL.set(1.0f);\r\n\t\thighlightedPixelsMesh->render(false, false, sibr::Mesh::LineRenderMode);\r\n\r\n\t\tutils.baseShader.end();\r\n\r\n\t\thighlightedPixelsMesh.reset();\r\n\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateMeshView(const sibr::Input & input, sibr::Window & window)\r\n\t{\r\n\r\n\t\tif (meshView.isActive) {\r\n\r\n\t\t\t//meshViewer.trackBall->updateAspectWithViewport(meshView.viewport);\r\n\t\t\tmeshViewer.interactCam->update(input, 1 / 60.f, meshView.viewport);\r\n\r\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Left)) {\r\n\t\t\t\tsibr::Vector2i oldWinSize = window.size();\r\n\t\t\t\twindow.size(oldWinSize.x() /2, oldWinSize.y());\r\n\t\t\t\timagesView.viewport = sibr::Viewport(&window.viewport(), 0, 0, 1, 1);\r\n\t\t\t\t\r\n\t\t\t\tmeshView.isActive = false;\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Right)) {\r\n\t\t\t\tsibr::Vector2i oldWinSize = window.size();\r\n\t\t\t\twindow.size(oldWinSize.x() * 2, oldWinSize.y());\r\n\t\t\t\tmeshView.viewport = sibr::Viewport(&window.viewport(), 0.5, 0, 1, 1);\r\n\t\t\t\timagesView.viewport = sibr::Viewport(&window.viewport(), 0, 0, 0.5, 1);\r\n\t\t\t\tmeshViewer.interactCam->setup(cpuMesh, meshView.viewport);\r\n\t\t\t\tmeshView.isActive = true;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateMeshView(const sibr::Input & input, const sibr::Viewport & viewport)\r\n\t{\r\n\t\tif (!meshView.isActive && cpuMesh.get() ) {\r\n\t\t\tmeshViewer.setMainMesh(*cpuMesh, sibr::Mesh::FillRenderMode, false, true);\r\n\t\t\tmeshViewer.interactCam->setup(cpuMesh, viewport);\r\n\t\t\tmeshView.isActive = true;\r\n\t\t}\r\n\t\tif (meshView.isActive) {\r\n\t\t\tmeshViewer.interactCam->update(input,1/60.0f, viewport);\r\n\t\t}\t\t\t\t\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateZoomBox(const sibr::Input & input, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\tif (input.key().isPressed(sibr::Key::Q)) {\r\n\t\t\tviewRectangle.center = sibr::Vector2f(0.5, 0.5);\r\n\t\t\tviewRectangle.diagonal = sibr::Vector2f(0.5, 0.5);\r\n\t\t}\r\n\r\n\t\tif (input.mouseButton().isPressed(sibr::Mouse::Code::Right) && !zoomSelection.isActive) {\r\n\t\t\tzoomSelection.isActive = true;\r\n\t\t\tzoomSelection.first = input.mousePosition();\r\n\t\t\tzoomSelection.first.y() = (int)winSize.y() - zoomSelection.first.y() - 1;\r\n\t\t}\r\n\t\tif (input.mouseButton().isActivated(sibr::Mouse::Code::Right) && zoomSelection.isActive) {\r\n\t\t\tzoomSelection.second = input.mousePosition();\r\n\t\t\tzoomSelection.second.y() = (int)winSize.y() - zoomSelection.second.y() - 1;\r\n\t\t}\r\n\t\tif (input.mouseButton().isReleased(sibr::Mouse::Code::Right) && zoomSelection.isActive) {\t\r\n\t\t\tsibr::Vector2f currentTL = (zoomSelection.first.cwiseMin(zoomSelection.second)).cast<float>();\r\n\t\t\tsibr::Vector2f currentBR = (zoomSelection.first.cwiseMax(zoomSelection.second)).cast<float>();\r\n\r\n\t\t\tif (((currentBR - currentTL).array() > sibr::Vector2f(10, 10).array()).all()) {\r\n\t\t\t\t\r\n\r\n\t\t\t\tsibr::Vector2f tlPix = viewRectangle.tl().cwiseProduct(winSize) + (viewRectangle.br() - viewRectangle.tl()).cwiseProduct(currentTL);\r\n\t\t\t\tsibr::Vector2f brPix = viewRectangle.tl().cwiseProduct(winSize) + (viewRectangle.br() - viewRectangle.tl()).cwiseProduct(currentBR);\r\n\r\n\t\t\t\tsibr::Vector2f center = 0.5f*(brPix + tlPix);\r\n\t\t\t\tsibr::Vector2f diag = 0.5f*(brPix - tlPix);\r\n\r\n\t\t\t\tfloat new_ratio = diag.x() / diag.y();\r\n\t\t\t\tfloat target_ratio = scalesData[currentScale].imRatio;\r\n\t\t\t\tif (new_ratio > target_ratio) {\r\n\t\t\t\t\tdiag.y() = diag.x() / target_ratio;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tdiag.x() = diag.y() * target_ratio;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tviewRectangle.center = center.cwiseQuotient(winSize);\r\n\t\t\t\tviewRectangle.diagonal = diag.cwiseQuotient(winSize);\r\n\r\n\t\t\t\tzoomSelection.isActive = false;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateCurrentLayer(const sibr::Input & input)\r\n\t{\r\n\t\tint i = -1;\r\n\r\n\t\tstd::vector<sibr::Key::Code> keys = {\r\n\t\t\tsibr::Key::Num1, sibr::Key::Num2, sibr::Key::Num3, sibr::Key::Num4, sibr::Key::Num5,\r\n\t\t\tsibr::Key::Num6, sibr::Key::Num7, sibr::Key::Num8, sibr::Key::Num9\r\n\t\t};\r\n\r\n\t\tfor (int k = 0; k < (int)keys.size(); ++k) {\r\n\t\t\tif (input.key().isPressed(keys[k])) {\r\n\t\t\t\ti = k;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (i >= 0 && i < (int)imagesLayers.size()) {\r\n\t\t\tcurrentLayer = i;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateZoomScroll(const sibr::Input & input)\r\n\t{\r\n\t\tdouble scroll = input.mouseScroll();\r\n\r\n\t\tif (scroll  != 0) {\t\r\n\t\t\tfloat ratio = (scroll > 0 ? 0.75f : 1.33f);\r\n\t\t\tif (input.key().isActivated(sibr::Key::LeftControl)) {\r\n\t\t\t\tratio *= ratio;\r\n\t\t\t}\r\n\t\t\tviewRectangle.diagonal *= ratio;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateCenter(const sibr::Input & input, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\tif (dclick.detected(input)) {\r\n\t\t\t//std::cout << \"dclick : \" << std::endl;\r\n\t\t\tsibr::Vector2f translation = (dclick.firstPosition.cast<float>().cwiseQuotient(winSize)-sibr::Vector2f(0.5,0.5)).cwiseProduct(viewRectangle.br() - viewRectangle.tl());\r\n\t\t\ttranslation.y() = -translation.y();\r\n\t\t\tviewRectangle.center += translation;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterface::updateDrag(const sibr::Input & input, const sibr::Vector2f & winSize)\r\n\t{\r\n\t\tif (input.mouseButton().isPressed(sibr::Mouse::Left)) {\r\n\t\t\tdrag.isActive = true;\r\n\t\t\tdrag.position = input.mousePosition();\r\n\t\t\tdrag.center = viewRectangle.center;\r\n\t\t} else if (drag.isActive && input.mouseButton().isReleased(sibr::Mouse::Left)) {\r\n\t\t\tdrag.isActive = false;\r\n\t\t}\r\n\t\tif (drag.isActive && input.mouseButton().isActivated(sibr::Mouse::Left)) {\r\n\t\t\tsibr::Vector2f translation = (input.mousePosition() - drag.position).cast<float>().cwiseQuotient(winSize).cwiseProduct(viewRectangle.br() - viewRectangle.tl());\r\n\t\t\ttranslation.y() = -translation.y();\r\n\t\t\tviewRectangle.center = drag.center - translation;\r\n\t\t}\r\n\t}\r\n\r\n\t\r\n\tvoid MultiViewInterfaceView::onUpdate(Input & input, const sibr::Viewport & viewport)\r\n\t{\r\n\t\tif (viewType == ViewType::IMAGES) {\r\n\t\t\t//i.imagesInput = input;\r\n\t\t\t//i.imagesView.viewport = viewport;\r\n\t\t\tinterfacePtr->updateImageView(viewport, input);\r\n\t\t} else if (viewType == ViewType::MESH) {\r\n\t\t\tinterfacePtr->updateMeshView(input, viewport);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid MultiViewInterfaceView::onRender(const sibr::Viewport & viewport)\r\n\t{\r\n\t\tif (viewType == ViewType::IMAGES) {\r\n\t\t\tinterfacePtr->renderImageView(viewport);\r\n\t\t\tinterfacePtr->onGui();\r\n\t\t} else if (viewType == ViewType::MESH) {\r\n\t\t\tinterfacePtr->displayMesh(viewport);\r\n\t\t}\r\n\t}\r\n\r\n} //namespace sibr\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/interface/Interface.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"../Config.hpp\"\n#include <core/graphics/Texture.hpp>\n#include <core/graphics/Input.hpp>\n#include <core/graphics/Window.hpp>\n#include <core/assets/InputCamera.hpp>\n#include <map>\n#include \"InterfaceUtils.h\"\n#include \"MeshViewer.h\"\n#include <core/view/ViewBase.hpp>\n\n#include <imgui/imgui.h>\n\n//typedef void (*CallBackFunction)(int event, int x, int y, int flags, void* userdata);\n\nnamespace sibr {\n\tstruct PixPos {\n\t\tPixPos() : im(-1), isDefined(false) {}\n\t\tPixPos(int i, const sibr::Vector2i & px) : im(i), pos(px), isDefined(true) {}\n\t\tvoid cout() const { std::cout << im << \" : \" << pos.transpose() << std::endl; }\n\t\tsibr::Vector2i pos;\n\t\tint im;\n\t\tbool isDefined;\n\t};\n\n\tstruct SubView {\n\t\tSubView() : isActive(false) {}\n\n\t\tsibr::Viewport viewport;\n\t\tbool isActive;\n\t\tsibr::Vector2i getViewportPosition(const sibr::Vector2i & winPos) {\n\t\t\treturn winPos - sibr::Vector2f(viewport.finalLeft(), viewport.finalTop()).cast<int>();\n\t\t}\n\t};\n\n\tstruct ScalingOptions {\n\t\tScalingOptions() : numScale(1), interpolation_method_cv(cv::INTER_CUBIC) {};\n\t\tint numScale;\n\t\tint interpolation_method_cv;\n\t};\n\n\tstruct LayerData {\n\t\tLayerData(const std::string & name) : name(name) { }\n\t\tstd::string name;\n\t};\n\n\tstruct ScaleData {\n\t\tScaleData(const sibr::Vector2i & imSizeI) {\n\t\t\timSize = imSizeI.cast<float>();\n\t\t\timRatio = imSize[0] / (float)imSize[1];\n\t\t}\n\t\tsibr::Vector2f imSize;\n\t\tfloat imRatio;\n\t};\n\n\tclass MultiViewInterface;\n\n\tclass SIBR_VIEW_EXPORT MultiViewInterfaceView : public sibr::ViewBase {\n\t\n\tpublic:\n\t\tenum class ViewType { IMAGES, MESH };\n\n\t\tSIBR_CLASS_PTR(MultiViewInterfaceView);\n\n\t\tMultiViewInterfaceView() {}\n\t\tMultiViewInterfaceView(MultiViewInterface * interfacePtr, ViewType type) : interfacePtr(interfacePtr), viewType(type) {}\n\n\t\tvirtual void\tonRenderIBR(IRenderTarget& /*dst*/, const sibr::Camera& /*eye*/) {}\n\t\t\n\t\tvirtual void\tonUpdate(Input& /*input*/, const sibr::Viewport & viewport);\n\t\tvirtual void\tonRender(const sibr::Viewport & viewport);\n\t\t\n\t\tPixPos currentActivePos;\n\n\tprotected:\n\t\tMultiViewInterface * interfacePtr;\n\t\tViewType viewType;\n\t};\n\n\t/**\n\t\tThis class provides basic rendering utilities for a list of images + a mesh.\n\t\t\\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT MultiViewInterface {\n\n\t\tSIBR_CLASS_PTR(MultiViewInterface);\n\n\tpublic:\n\n\t\tMultiViewInterface();\n\n\t\t/**\n\t\t Execute the main display loop, with an optional callback.\n\t\t \\param window the sibr::Window displayed into\n\t\t \\param f an optional callback called at the end of each frame.\n\t\t*/\n\t\tvoid displayLoop(sibr::Window & window, std::function<void(MultiViewInterface*)> = [](MultiViewInterface*){});\n\t\tvoid addCameras(const std::vector<InputCamera::Ptr> & input_cams);\n\t\tvoid addMesh(const sibr::Mesh::Ptr & mesh);\n\t\tvoid addMesh(const sibr::Mesh & mesh);\n\n\t\tvirtual void update(sibr::Window & window, const sibr::Input & input);\n\n\t\tvirtual void updateImageView(const sibr::Viewport & viewport, const sibr::Input & input);\n\n\t\tvirtual void render();\n\n\t\tvoid renderImageView(const sibr::Viewport & viewport);\n\n\t\tsibr::ViewBase::Ptr getViewBase(MultiViewInterfaceView::ViewType type = MultiViewInterfaceView::ViewType::IMAGES);\n\t\tvoid onGui();\n\n\t\t//~MultiViewInterface();\n\n\t\t//virtual void loop();\n\n\t\tsibr::Mesh::Ptr cpuMesh;\n\t\tsibr::Mesh::Ptr reproMesh;\n\t\tsibr::Mesh::RenderMode reproMeshMode;\n\t\tbool reproMeshBackFace;\n\n\t\tsibr::Mesh::Ptr highlightedPixelsMesh;\n\t\tstd::vector<sibr::PixPos> highlightedPixels;\n\t\tbool hightligthChanged;\n\n\t\tsibr::Vector2f imgPixelScreenSize;\n\n\t\tSubView imagesView;\n\t\tSubView meshView;\n\t\t\n\t\tsibr::Input imagesInput;\n\t\tsibr::Input meshInput;\n\n\n\tpublic:\n\t\t//std::unique_ptr<Window> window;\n\n\t\tMultiViewInterfaceView::Ptr imagesViewBase;\n\t\tMultiViewInterfaceView::Ptr meshViewBase;\n\n\t\tInterfaceUtilities utils;\n\n\t\tScalingOptions scalingOptions;\n\t\tstd::map<std::string, int> name_to_layer_map;\n\n\t\tRectangleData viewRectangle;\n\t\tDragData drag;\n\t\tDoubleClick<sibr::Mouse::Left> dclick;\n\t\tSelectionData zoomSelection;\n\t\tsibr::MeshViewer meshViewer;\n\n\t\tstd::vector<InputCamera::Ptr> cams;\n\n\t\tsibr::PixPos currentActivePos;\n\n\t\tsibr::Vector2i grid;\n\t\tsibr::Vector2f imSizeF;\n\t\tsibr::Vector2f winSize;\n\t\tfloat imRatio;\n\n\t\tint currentScale;\n\t\tint currentLayer;\n\t\tint numImgs;\n\t\tstd::vector<std::vector<sibr::ITexture2DArray::Ptr>> imagesLayers; //for each scale, each image layer\n\t\tstd::vector<std::vector<const sibr::IImage*>> imagesPtr;\n\t\tstd::vector<std::vector<sibr::IImage::Ptr> > imagesFromLambdasPtr;\n\t\tstd::vector<LayerData> layersData;\n\t\tstd::vector<ScaleData> scalesData;\n\n\t\tPixPos pixFromScreenPos(const sibr::Vector2i & pos, const sibr::Vector2f & winSize);\n\n\t\tUV01 screenPos(const PixPos & pix);\n\t\tUV01 screenPosPixelCenter(const PixPos & pix);\n\n\t\tsibr::Vector2i screenPosPixels(const PixPos & pix, const sibr::Vector2f & winSize);\n\t\tsibr::Vector2f screenPosPixelsFloat(const PixPos & pix, const sibr::Vector2f & winSize);\n\n\t\tvoid addHighlightPixel(const PixPos & pix, const sibr::Vector2f & winSize);\n\t\tvoid renderHighlightPixels();\n\n\t\tvoid highlightPixel(const PixPos & pix, const sibr::Viewport & viewport, const sibr::Vector3f & color = { 0, 1, 0 }, const sibr::Vector2f & pixScreenSize = { 5.0f,5.0f } );\n\n\t\tvirtual void displayImages(const sibr::Viewport & viewport);\n\t\tvirtual void displayMesh(const sibr::Viewport & viewport);\n\n\t\tvoid displayZoom(const sibr::Viewport & viewport);\n\t\tvoid displayHighlightedPixels(const sibr::Vector3f & color, float alpha);\n\n\t\tvirtual void updateMeshView(const sibr::Input & input, sibr::Window & window);\n\t\tvirtual void updateMeshView(const sibr::Input & input, const sibr::Viewport & viewport);\n\t\tvoid updateZoomBox(const sibr::Input & input, const sibr::Vector2f & winSize);\n\t\tvoid updateCurrentLayer(const sibr::Input & input);\n\t\tvoid updateZoomScroll(const sibr::Input & input);\n\t\tvoid updateCenter(const sibr::Input & input, const sibr::Vector2f & winSize);\n\t\tvoid updateDrag(const sibr::Input & input, const sibr::Vector2f & winSize);\n\n\t\ttemplate<typename T_Type, unsigned int T_NumComp>\n\t\tbool checkNewLayer(const std::vector<sibr::Image<T_Type, T_NumComp> > & images) \n\t\t{\n\t\t\tif (imagesLayers.size() == 0) {\n\t\t\t\timagesLayers.resize(scalingOptions.numScale);\n\t\t\t\tfor (int scale = 0; scale < scalingOptions.numScale; ++scale) {\n\t\t\t\t\tint w_s = static_cast<int>(std::ceil(images[0].w()*pow(2.0f, -scale)));\n\t\t\t\t\tint h_s = static_cast<int>(std::ceil(images[0].h()*pow(2.0f, -scale)));\n\t\t\t\t\tscalesData.push_back(ScaleData(sibr::Vector2i(w_s, h_s)));\n\t\t\t\t}\n\t\t\t\tnumImgs = (int)images.size();\n\t\t\t}\n\n\t\t\tconst auto & baseScaleImageLayer = imagesLayers[0];\n\t\t\tif (baseScaleImageLayer.size() > 0) {\n\t\t\t\tif ((uint)images.size() != baseScaleImageLayer[0]->depth()) {\n\t\t\t\t\tSIBR_ERR << \"not enough images\" << std::endl;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (images.size() == 0) {\n\t\t\t\tSIBR_ERR << \"empty image vector\" << std::endl;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\tpublic:\n\n\t\ttemplate <typename T_Type, unsigned int T_NumComp>\n\t\tvoid addImageLayer(const std::vector<sibr::ImagePtr<T_Type, T_NumComp> > & images, const std::string & name = \"\") {\n\t\t\tstd::vector<sibr::Image<T_Type, T_NumComp> > imgs(images.size());\n\t\t\tfor (int i = 0; i < (int)images.size(); ++i) {\n\t\t\t\timgs[i] = images[i]->clone();\n \t\t\t}\n\t\t\taddImageLayer(imgs, name);\n\t\t}\n\n\t\ttemplate <typename T_Type, unsigned int T_NumComp>\n\t\tvoid addImageLayer(const std::vector<sibr::Image<T_Type, T_NumComp> > & images, const std::string & name = \"\")\n\t\t{\n\t\t\t\n\t\t\tcheckNewLayer(images);\n\n\t\t\tfor (int scale = 0; scale < scalingOptions.numScale; ++scale) {\n\t\t\t\tstd::vector<sibr::Image<T_Type, T_NumComp> > resized_imgs(images.size());\n\t\t\t\tif (scale != 0) {\n#pragma omp parallel for\n\t\t\t\t\tfor (int im = 0; im < (int)images.size(); ++im) {\n\t\t\t\t\t\tconst sibr::Vector2i scaleSize = scalesData[scale].imSize.cast<int>();\n\t\t\t\t\t\tresized_imgs[im] = images[im].resized(scaleSize[0], scaleSize[1], scalingOptions.interpolation_method_cv);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tstd::vector<const sibr::IImage*> layerPtrs(images.size()); \n\t\t\t\t\tfor (int im = 0; im < (int)images.size(); ++im) {\n\t\t\t\t\t\tlayerPtrs[im] = &images[im];\n\t\t\t\t\t}\n\t\t\t\t\timagesPtr.push_back(layerPtrs);\n\t\t\t\t}\n\t\t\t\tconst std::vector<sibr::Image<T_Type, T_NumComp> > & imgs = (scale == 0 ?  images : resized_imgs);\n\t\t\t\tauto layer = std::make_shared<sibr::Texture2DArray<T_Type, T_NumComp>>();\n\n\t\t\t\tlayer->createFromImages(imgs);\n\n\t\t\t\tsibr::ITexture2DArray::Ptr layerBase(layer);\n\t\t\t\timagesLayers[scale].push_back(layerBase);\n\t\t\t}\n\n\t\t\tstd::string layerName = (name == \"\" ? \"Layer\" + std::to_string(layersData.size()) : name);\n\t\t\tname_to_layer_map[layerName] = (int)layersData.size();\n\t\t\tlayersData.push_back(LayerData(layerName));\n\t\t\t\n\t\t}\n\n\t\ttemplate <typename T_Type, unsigned int T_NumComp, typename LambdaType>\n\t\tvoid addImageLayerWithLambda(const std::vector<sibr::Image<T_Type, T_NumComp> > & images, LambdaType lambda, const std::string & name = \"\") {\n\t\t\t\n\t\t\tcheckNewLayer(images);\n\n\t\t\tusing Lambda_Out_Image_Type = decltype(lambda(images[0]));\n\t\t\tusing Lambda_Out_Type = typename Lambda_Out_Image_Type::Type;\n\t\t\tconst int Lambda_Out_N = Lambda_Out_Image_Type::e_NumComp;\n\n\t\t\tfor (int scale = 0; scale < scalingOptions.numScale; ++scale) {\n\t\t\t\tstd::vector<sibr::Image<T_Type, T_NumComp> > resized_imgs(images.size());\n\t\t\t\tif (scale != 0) {\n#pragma omp parallel for\n\t\t\t\t\tfor (int im = 0; im < (int)images.size(); ++im) {\n\t\t\t\t\t\tconst sibr::Vector2i scaleSize = scalesData[scale].imSize.cast<int>();\n\t\t\t\t\t\tresized_imgs[im] = images[im].resized(scaleSize[0], scaleSize[1], scalingOptions.interpolation_method_cv);\n\t\t\t\t\t}\n\t\t\t\t} \n\t\t\t\tconst std::vector<sibr::Image<T_Type, T_NumComp> > & imgs = (scale == 0 ? images : resized_imgs);\n\n\t\t\t\tstd::vector<Lambda_Out_Image_Type> lambdaImgs(images.size());\n#pragma omp parallel for\n\t\t\t\tfor (int im = 0; im < (int)images.size(); ++im) {\n\t\t\t\t\tlambdaImgs[im] = lambda(imgs[im]);\n\t\t\t\t\t//sibr::show(lambdaImgs[im], \"test\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (scale == 0) {\n\t\t\t\t\tstd::vector<sibr::IImage::Ptr> firstLayerPtrs(images.size());\n\t\t\t\t\tstd::vector<const sibr::IImage*> layerPtrs(images.size());\n\t\t\t\t\tfor (int im = 0; im < (int)images.size(); ++im) {\n\t\t\t\t\t\tauto lambdaImgPtr = std::make_shared<Lambda_Out_Image_Type>(lambdaImgs[im].clone());\n\n\t\t\t\t\t\tfirstLayerPtrs[im] = std::static_pointer_cast<IImage>(lambdaImgPtr);\n\t\t\t\t\t\tlayerPtrs[im] = firstLayerPtrs[im].get();\n\t\t\t\t\t}\n\t\t\t\t\timagesFromLambdasPtr.push_back(firstLayerPtrs);\n\t\t\t\t\timagesPtr.push_back(layerPtrs);\n\t\t\t\t}\n\n\t\t\t\ttypename sibr::Texture2DArray<Lambda_Out_Type, Lambda_Out_N>::Ptr layer = std::make_shared<sibr::Texture2DArray<Lambda_Out_Type, Lambda_Out_N> >();\n\t\t\t\tlayer->createFromImages(lambdaImgs);\n\t\t\t\tsibr::ITexture2DArray::Ptr layerBase(layer);\n\t\t\t\timagesLayers[scale].push_back(layerBase);\n\t\t\t}\n\n\t\t\tstd::string layerName = (name == \"\" ? \"Layer\" + std::to_string(layersData.size()) : name);\n\t\t\tname_to_layer_map[layerName] = (int)layersData.size();\n\t\t\tlayersData.push_back(LayerData(layerName));\n\t\t}\n\t};\n\n\n} //namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/interface/InterfaceUtils.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"InterfaceUtils.h\"\n\n#include <core/graphics/Mesh.hpp>\n#include <core/graphics/Window.hpp>\n\nnamespace sibr {\n\n\tconst std::string InterfaceUtilities::translationScalingVertexShader =\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform vec2 translation;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform vec2 scaling;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"\tgl_Position = vec4(scaling*in_vertex.xy+translation,0.0, 1.0);\t\t\\n\"\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\tconst std::string InterfaceUtilities::colorAlphaFragmentShader =\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform vec3 color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform float alpha;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"\t\tout_color = vec4(color,alpha);\t\t\t\t\t\t\t\t\\n\"\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\tconst std::string InterfaceUtilities::meshVertexShader =\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform mat4 mvp;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"\tgl_Position = mvp*vec4(in_vertex, 1.0);\t\t\t\t\t\t\\n\"\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\tconst std::string InterfaceUtilities::multiViewVertexShader =\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\n\t\t\"out vec2 uv_coord;\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform vec2 zoomTL;\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform vec2 zoomBR;\t\t\t\t\t\t\t\t\\n\"\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"\tuv_coord = 0.5*in_vertex.xy + vec2(0.5);\t\t\\n\"\n\t\t\"\tuv_coord = zoomTL + (zoomBR-zoomTL)*uv_coord;\t\\n\"\n\t\t\"\tuv_coord.y = 1.0 - uv_coord.y;\t\t\t\t\t\\n\"\n\t\t\"\tgl_Position = vec4(in_vertex.xy,0.0, 1.0);\t\t\\n\"\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\n\tconst std::string InterfaceUtilities::multiViewFragmentShader =\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"layout(binding = 0) uniform sampler2DArray texArray;\t\t\t\t\\n\"\n\t\t\"uniform int numImgs;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"uniform vec2 grid;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"in vec2 uv_coord;\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"\tvec2 uvs = uv_coord;\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"\tuvs =  grid*uvs;\t\t\t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"  if( uvs.x < 0 || uvs.y < 0 ) { discard; } \t\t\t\t\t\t\\n\"\n\t\t\"   vec2 fracs = fract(uvs); \t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"   vec2 mods = uvs - fracs; \t\t\t\t\t\t\t\t\t\t\\n\"\n\t\t\"   int n = int(mods.x + grid.x*mods.y); \t\t\t\t\t\t\t\\n\"\n\t\t\" if ( n< 0 || n > numImgs || mods.x >= grid.x || mods.y >= (float(numImgs)/grid.x) + 1) { discard; } else { \\n\"\n\t\t\"\tout_color = texture(texArray,vec3(fracs.x,fracs.y,n));\t}\t\t\\n\"\n\t\t\"\t//out_color = vec4(n/64.0,0.0,0.0,1.0); }\t\t\t\t\t\t\\n\"\n\t\t\"\t//out_color = vec4(uv_coord.x,uv_coord.y,0.0,1.0);\t}\t\t\t\\n\"\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\n\n\t//sibr::GLShader InterfaceUtilities::baseShader;\n\n\t//sibr::GLParameter InterfaceUtilities::colorGL;\n\t//sibr::GLParameter InterfaceUtilities::alphaGL;\n\t//sibr::GLParameter InterfaceUtilities::scalingGL;\n\t//sibr::GLParameter InterfaceUtilities::translationGL;\n\n\t//sibr::GLShader InterfaceUtilities::multiViewShader;\n\n\t//sibr::GLParameter InterfaceUtilities::numImgsGL;\n\t//sibr::GLParameter InterfaceUtilities::gridGL;\n\t//sibr::GLParameter InterfaceUtilities::multiViewTopLeftGL;\n\t//sibr::GLParameter InterfaceUtilities::multiViewBottomRightGL;\n\n\t//const InterfaceUtilities::GLinitializer InterfaceUtilities::init;\n\n\tInterfaceUtilities::InterfaceUtilities()\n\t{\n\t\t\n\t}\n\n\tvoid InterfaceUtilities::initAllShaders()\n\t{\n\t\tinitBaseShader();\n\t\tinitMultiViewShader();\n\t\tinitMeshViewShader();\n\t\tCHECK_GL_ERROR;\n\n\t\tstd::cout << \" all shaders compiled\" << std::endl;\n\t}\n\n\tvoid InterfaceUtilities::freeAllShaders()\n\t{\n\t\tCHECK_GL_ERROR;\n\t\tbaseShader.terminate();\n\t\tCHECK_GL_ERROR;\n\t\tmultiViewShader.terminate();\n\t\tCHECK_GL_ERROR;\n\t}\n\n\tvoid InterfaceUtilities::initBaseShader()\n\t{\n\t\tbaseShader.init(\"InterfaceUtilitiesBaseShader\", translationScalingVertexShader, colorAlphaFragmentShader);\n\t\tcolorGL.init(baseShader, \"color\");\n\t\talphaGL.init(baseShader, \"alpha\");\n\t\tscalingGL.init(baseShader, \"scaling\");\n\t\ttranslationGL.init(baseShader, \"translation\");\n\t}\n\n\tvoid InterfaceUtilities::initMultiViewShader()\n\t{\n\t\tmultiViewShader.init(\"InterfaceUtilitiesMultiViewShader\", multiViewVertexShader, multiViewFragmentShader);\n\t\tmultiViewTopLeftGL.init(multiViewShader, \"zoomTL\"); \n\t\tmultiViewBottomRightGL.init(multiViewShader, \"zoomBR\");\n\t\tnumImgsGL.init(multiViewShader, \"numImgs\");\n\t\tgridGL.init(multiViewShader, \"grid\");\n\t}\n\n\tvoid InterfaceUtilities::initMeshViewShader()\n\t{\n\t\tmeshViewShader.init(\"InterfaceUtilitiesMeshViewShader\", meshVertexShader, colorAlphaFragmentShader);\n\t\tmvp.init(meshViewShader,\"mvp\");\n\t\tcolorMeshGL.init(meshViewShader,\"color\");\n\t\talphaMeshGL.init(meshViewShader,\"alpha\");\n\t}\n\n\tvoid InterfaceUtilities::rectangle(const sibr::Vector3f & color, const UV11 & tl, const UV11 & br, bool fill, float alpha)\n\t{\n\n\t\tstatic sibr::Mesh::Ptr rectangleMesh;\n\t\tstatic int lastContextId = -1;\n\n\t\tif (lastContextId != sibr::Window::contextId) {\n\t\t\trectangleMesh = std::make_shared<sibr::Mesh>(true);\n\t\t};\n\t\t\n\t\n\t\tbaseShader.begin();\n\n\t\tscalingGL.set(sibr::Vector2f(1.0f,1.0f));\n\t\ttranslationGL.set(sibr::Vector2f(0, 0));\n\t\tcolorGL.set(color);\n\n\t\trectangleMesh->vertices({\n\t\t\t{ tl.x(), tl.y() , 0 },\n\t\t\t{ tl.x(), br.y() , 0 },\n\t\t\t{ br.x(), br.y() , 0 },\n\t\t\t{ br.x(), tl.y() , 0 }\n\t\t});\n\n\t\tif (fill) {\n\t\t\trectangleMesh->triangles({\n\t\t\t\t{ 0,1,2 },\n\t\t\t\t{ 0,2,3 }\n\t\t\t});\n\n\t\t\talphaGL.set(alpha);\n\t\t\tglEnable(GL_BLEND);\n\t\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\t\tglBlendEquation(GL_FUNC_ADD);\n\t\t\trectangleMesh->render(false, false);\n\t\t}\n\t\t\n\t\trectangleMesh->triangles({\n\t\t\t{ 0,0,1 },{ 1,1,2 },{ 2,2,3 },{ 3,3,0 }\n\t\t});\n\n\t\talphaGL.set(1.0f);\n\t\trectangleMesh->render(false, false, sibr::Mesh::LineRenderMode);\n\t\t\n\t\tbaseShader.end();\n\t}\n\n\tvoid InterfaceUtilities::rectanglePixels(const sibr::Vector3f & color, const sibr::Vector2f & center, const sibr::Vector2f & diagonal, bool fill, float alpha, const sibr::Vector2f & winSize)\n\t{\n\t\tUV01 centerUV = UV01::from(center.cwiseQuotient(winSize));\n\t\tUV01 tl = UV01::from(centerUV - 0.5f*diagonal.cwiseQuotient(winSize));\n\t\tUV01 br = UV01::from(centerUV + 0.5f*diagonal.cwiseQuotient(winSize));\n\t\trectangle(color, tl, br, fill, alpha);\n\t}\n\n\tvoid InterfaceUtilities::circle(const sibr::Vector3f & color, const UV11 & center, float radius, bool fill, float alpha, const sibr::Vector2f & scaling, int precision)\n\t{\n\n\t\tstatic int n;\n\t\tstatic sibr::Mesh::Ptr circleMesh;\n\t\tstatic sibr::Mesh::Ptr circleFilledMesh;\n\t\tstatic sibr::Mesh::Triangles circleTriangles;\n\t\tstatic sibr::Mesh::Triangles circleFillTriangles;\n\t\tstatic int lastContextId = -1;\n\n\t\tbool updateMeshes = (lastContextId != sibr::Window::contextId) || (n != precision);\n\t\tif (updateMeshes) {\n\t\t\tlastContextId = sibr::Window::contextId;\n\t\t\tn = precision;\n\t\t\tcircleTriangles.resize(n);\n\t\t\tcircleFillTriangles.resize(n);\n\t\t\tfor (int i = 0; i < n; ++i) {\n\t\t\t\tint next = (i + 1) % n;\n\t\t\t\tcircleTriangles[i] = sibr::Vector3u(i, i, next);\n\t\t\t\tcircleFillTriangles[i] = sibr::Vector3u(i, next, n);\n\t\t\t}\n\n\t\t\tsibr::Mesh::Vertices vertices(n + 1);\n\t\t\tdouble base_angle = 2.0*M_PI / (double)n;\n\t\t\tfloat rho = 0.5f*radius*(float)(1.0 + cos(0.5*base_angle));\n\n\t\t\tfor (int i = 0; i < n; ++i) {\n\t\t\t\tdouble angle = i*base_angle;\n\t\t\t\tvertices[i] = sibr::Vector3f((float)cos(angle), (float)sin(angle), (float)0.0);\n\t\t\t}\n\t\t\tvertices[n] = sibr::Vector3f(0, 0, 0);\n\n\t\t\tcircleMesh = std::make_shared<sibr::Mesh>(true);\n\t\t\tcircleFilledMesh = std::make_shared<sibr::Mesh>(true);\n\t\t\tcircleMesh->vertices(vertices);\n\t\t\tcircleFilledMesh->vertices(vertices);\n\t\t\tcircleMesh->triangles(circleTriangles);\n\t\t\tcircleFilledMesh->triangles(circleFillTriangles);\n\t\t}\n\n\t\tbaseShader.begin();\n\n\t\ttranslationGL.set(sibr::Vector2f(0, 0));\n\n\t\tcolorGL.set(color);\n\t\tscalingGL.set(scaling);\n\t\ttranslationGL.set(center);\n\n\t\tif (fill) {\n\t\t\talphaGL.set(alpha);\n\t\t\tglEnable(GL_BLEND);\n\t\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t\t\tglBlendEquation(GL_FUNC_ADD);\n\t\t\tcircleFilledMesh->render(false, false);\n\t\t}\n\n\t\talphaGL.set(1.0f);\n\t\tcircleMesh->render(false, false, sibr::Mesh::LineRenderMode);\n\n\t\tbaseShader.end();\n\t}\n\n\tvoid InterfaceUtilities::circlePixels(const sibr::Vector3f & color, const sibr::Vector2f & center, float radius, bool fill, float alpha, const sibr::Vector2f & winSize, int precision)\n\t{\n\t\tUV10 centerUV = UV10::from(center.cwiseQuotient(winSize));\n\t\tsibr::Vector2f scaling = radius*sibr::Vector2f(1, 1).cwiseQuotient(winSize);\n\n\t\tcircle(color, centerUV, 1.0f, fill, alpha, scaling, precision);\n\t}\n\n\tvoid InterfaceUtilities::linePixels(const sibr::Vector3f & color, const sibr::Vector2f & ptA, const sibr::Vector2f & ptB, const sibr::Vector2f & winSize)\n\t{\n\t\tUV11 uvA = UV01::from(ptA.cwiseQuotient(winSize));\n\t\tUV11 uvB = UV01::from(ptB.cwiseQuotient(winSize));\n\t\t\t\n\t\tsibr::Mesh line(true);\n\t\tline.vertices({\n\t\t\t{ uvA.x(), uvA.y(), 0.0f },\n\t\t\t{ uvB.x(), uvB.y(), 0.0f }\n\t\t});\n\t\tline.triangles({\n\t\t\tsibr::Vector3u(0,0,1)\n\t\t});\n\n\t\tbaseShader.begin();\n\n\t\tscalingGL.set(sibr::Vector2f(1.0f, 1.0f));\n\t\ttranslationGL.set(sibr::Vector2f(0, 0));\n\t\tcolorGL.set(color);\n\t\talphaGL.set(1.0f);\n\n\t\tline.render(false, false, sibr::Mesh::LineRenderMode);\n\n\t\tbaseShader.end();\n\t}\n\n\n\n\n} //namespace sibr"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/interface/InterfaceUtils.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n#include \"../Config.hpp\"\n#include <core/graphics/Shader.hpp>\n#include <core/system/SimpleTimer.hpp>\n#include <core/graphics/Input.hpp>\n#include <core/graphics/Window.hpp>\n\n#include <string>\n\n#include <imgui/imgui.h>\n\nnamespace sibr {\n\n\t/**\n\t* \\ingroup sibr_view\n\t*/\n\tenum UVspace { ZERO_ONE, MINUS_ONE_ONE, ONE_ZERO };\n\n\t/**\n\t* \\ingroup sibr_view\n\t*/\n\ttemplate<UVspace space> struct UV : public sibr::Vector2f {\n\n\t\tstatic UV from(const sibr::Vector2f & v) { return UV(v.x(), v.y()); }\n\n\t\tUV(float u, float v) : Vector2f(u, v) {}\n\n\t\t//explicit UV(const sibr::Vector2f & v) : sibr::Vector2f(v) {}\n\n\t\ttemplate<UVspace otherSpace> operator UV<otherSpace>() const;\n\t};\n\n\ttypedef UV<sibr::MINUS_ONE_ONE> UV11;\n\ttypedef UV<sibr::ZERO_ONE> UV01;\n\ttypedef UV<sibr::ONE_ZERO> UV10;\n\n\ttemplate<> template<> inline\n\tUV11::operator UV01() const {\n\t\treturn UV01(0.5f*x() + 1, 0.5f*y() + 1);\n\t}\n\n\ttemplate<> template<> inline\n\tUV01::operator UV11() const {\n\t\treturn UV11(2.0f*x() - 1, 2.0f*y() - 1);\n\t}\n\n\ttemplate<> template<> inline\n\tUV01::operator UV10() const {\n\t\treturn UV10(x(), 1.0f - y());\n\t}\n\n\ttemplate<> template<> inline\n\tUV10::operator UV01() const {\n\t\treturn UV01(x(), 1.0f - y());\n\t}\n\n\ttemplate<> template<> inline\n\tUV10::operator UV11() const {\n\t\treturn UV11(UV01(*this));\n\t}\n\n\ttemplate<> template<> inline\n\tUV11::operator UV10() const {\n\t\treturn UV10(UV01(*this));\n\t}\n\n\t/**\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT InterfaceUtilities\n\t{\n\tpublic:\n\t\tInterfaceUtilities();\n\n\t\tstatic const std::string translationScalingVertexShader;\n\t\tstatic const std::string colorAlphaFragmentShader;\n\n\t\tsibr::GLShader baseShader;\n\n\t\tsibr::GLParameter colorGL;\n\t\tsibr::GLParameter alphaGL;\n\t\tsibr::GLParameter scalingGL;\n\t\tsibr::GLParameter translationGL;\n\n\t\tstatic const std::string multiViewVertexShader;\n\t\tstatic const std::string multiViewFragmentShader;\n\n\t\tstatic const std::string meshVertexShader;\n\t\tstatic const std::string meshAlphaViewFragmentShader;\n\t\tsibr::GLShader meshViewShader;\n\t\tsibr::GLParameter mvp;\n\t\tsibr::GLParameter colorMeshGL;\n\t\tsibr::GLParameter alphaMeshGL;\n\n\t\tsibr::GLShader multiViewShader;\n\n\t\tsibr::GLParameter numImgsGL;\n\t\tsibr::GLParameter gridGL;\n\t\tsibr::GLParameter multiViewTopLeftGL;\n\t\tsibr::GLParameter multiViewBottomRightGL;\n\n\t\tvoid rectangle(const sibr::Vector3f & color, const UV11 & tl, const UV11 & br, bool fill, float alpha); \n\t\tvoid rectanglePixels(const sibr::Vector3f & color, const sibr::Vector2f & center, const sibr::Vector2f & diagonal, bool fill, float alpha, const sibr::Vector2f & winSize);\n\t\tvoid circle(const sibr::Vector3f & color, const UV11 & center, float radius, bool fill, float alpha, const sibr::Vector2f & scaling = sibr::Vector3f(1,1), int precision = 50);\n\t\tvoid circlePixels(const sibr::Vector3f & color, const sibr::Vector2f & center, float radius, bool fill, float alpha, const sibr::Vector2f & winSize, int precision = 50);\n\t\tvoid linePixels(const sibr::Vector3f & color, const sibr::Vector2f & ptA, const sibr::Vector2f & ptB, const sibr::Vector2f & winSize);\n\n\t\tvoid initAllShaders();\n\t\tvoid freeAllShaders();\n\n\tprivate:\n\t\tstruct GLinitializer {\n\t\t\tGLinitializer() {\n\t\t\t\t//InterfaceUtilities::initBaseShader();\n\t\t\t}\n\t\t};\n\n\t\tvoid initBaseShader();\n\t\tvoid initMultiViewShader();\n\t\tvoid initMeshViewShader();\n\n\t\t//const static GLinitializer init;\n\t};\n\n\tstruct RectangleData\n\t{\n\t\tRectangleData() : center({ 0.5, 0.5 }), diagonal({ 0.5, 0.5 }) {}\n\t\tsibr::Vector2f center;\n\t\tsibr::Vector2f diagonal;\n\t\tsibr::Vector2f br() const { return center + diagonal; }\n\t\tsibr::Vector2f tl() const { return center - diagonal; }\n\t};\n\n\tstruct DragData\n\t{\n\t\tDragData() : isActive(false) {}\n\t\tsibr::Vector2f center;\n\t\tsibr::Vector2i position;\n\t\tbool isActive;\n\t};\n\n\tstruct SelectionData\n\t{\n\t\tSelectionData() : isActive(false) {}\n\t\tsibr::Vector2i first;\n\t\tsibr::Vector2i second;\n\t\tbool isActive;\n\n\t};\n\n\ttemplate<sibr::Mouse::Code mKey> struct DoubleClick\n\t{\n\t\tDoubleClick() : detection_timing_in_ms(500) {}\n\n\t\tbool detected(const sibr::Input & input, bool should_be_close = true) {\n\t\t\tif (input.mouseButton().isPressed(mKey)) {\n\t\t\t\t//std::cout << \"timer.deltaTimeFromLastTic<sibr::Timer::s>() : \" << timer.deltaTimeFromLastTic<>() << std::endl;\t\t\t\n\t\t\t\tif (timer.deltaTimeFromLastTic<>() < detection_timing_in_ms && (!should_be_close || (firstPosition - input.mousePosition()).cwiseAbs().maxCoeff() < 10 ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tfirstPosition = input.mousePosition();\n\t\t\t\ttimer.tic();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tdouble detection_timing_in_ms;\n\n\t\tsibr::Timer timer;\n\t\tsibr::Vector2i firstPosition;\n\t};\n\n\ttemplate <typename T_Type, unsigned int T_NumComp>\n\tstatic void show(\n\t\tconst sibr::Texture2DArray<T_Type, T_NumComp> & texArray,\n\t\tsibr::Window * inputWin = nullptr,\n\t\tint w = -1,\n\t\tint h = -1\n\t) {\n\t\tenum Mode { SLICE, GRID };\n\n\t\tsibr::Window * win = inputWin;\n\t\tconst bool inChargeOfWindow = (win == nullptr);\n\t\tconst bool useCustomSize = (w > 0 && h > 0);\n\t\tsibr::Vector2i previousSize;\n\n\t\tif (inChargeOfWindow) {\n\t\t\tsibr::Vector2i winSize = (useCustomSize ? sibr::Vector2i(w, h) : sibr::Vector2i (1600, 1200));\n\t\t\twin = new sibr::Window(winSize[0], winSize[1], \"showTexArray\");\n\t\t} else if (useCustomSize) {\n\t\t\tpreviousSize = win->size();\n\t\t\twin->size(w, h);\n\t\t}\n\t\tMode mode = GRID;\n\n\t\twin->makeContextCurrent();\n\n\t\tsibr::InterfaceUtilities utils;\n\t\tutils.initAllShaders();\n\n\t\tsibr::Vector2i grid(3, 3), previousGrid;\n\t\tsibr::Vector2f TL(0, 0), BR(1, 1);\n\t\tint slice = 1;\n\n\t\tbool renderLoop = true;\n\t\twhile (renderLoop) {\n\t\t\tsibr::Input::poll();\n\t\t\tsibr::Input & input = sibr::Input::global();\n\t\t\tif (input.key().isPressed(sibr::Key::Escape)) {\n\t\t\t\trenderLoop = false;\n\t\t\t\tif (inChargeOfWindow) {\n\t\t\t\t\twin->close();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tImGui::Begin(\"Show setting\");\n\t\t\tif (ImGui::RadioButton(\"Grid\", (int*)&mode, 1)) {\n\t\t\t\tgrid = previousGrid;\n\t\t\t}\n\t\t\tImGui::SameLine();\n\t\t\tImGui::RadioButton(\"Slice\", (int*)&mode, 0);\n\n\t\t\tif (mode == GRID) {\n\t\t\t\tImGui::SliderInt(\"GridX\", &grid[0], 1, texArray.depth());\n\t\t\t\tImGui::SliderInt(\"GridY\", &grid[1], 1, texArray.depth());\n\t\t\t\tpreviousGrid = grid;\n\t\t\t\tTL = { 0, 0 }, BR = { 1,1 };\n\t\t\t} else if (mode == SLICE) {\n\t\t\t\tgrid = sibr::Vector2i(1, 1); \n\t\t\t\tImGui::SliderInt(\"Slice\", &slice, 1, texArray.depth());\n\t\t\t\tTL[1] = -slice + 2;\n\t\t\t\tBR[1] = -slice + 1;\n\t\t\t\t//ImGui::SliderFloat(\"L\", &TL[0], -3, 3);\n\t\t\t\t//ImGui::SliderFloat(\"T\", &TL[1], -3, 3);\n\t\t\t\t//ImGui::SliderFloat(\"R\", &BR[0], -3, 3);\n\t\t\t\t//ImGui::SliderFloat(\"B\", &BR[1], -3, 3);\t\t\n\t\t\t}\n\n\t\t\tImGui::End();\n\n\t\t\tconst auto & viewport = win->viewport();\n\t\t\tviewport.bind();\n\t\t\tviewport.clear(sibr::Vector3f(0.7, 0.7, 0.7));\n\n\t\t\tutils.multiViewShader.begin();\n\n\t\t\tutils.numImgsGL.set((int)texArray.depth() - 1);\n\t\t\tsibr::Vector2f gridF = grid.cast<float>();\n\t\t\tutils.gridGL.set(gridF);\n\n\t\t\tutils.multiViewTopLeftGL.set(TL);\n\t\t\tutils.multiViewBottomRightGL.set(BR);\t\t\n\n\t\t\tglActiveTexture(GL_TEXTURE0);\n\t\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, texArray.handle());\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t\tutils.multiViewShader.end();\n\n\t\t\twin->swapBuffer();\n\t\t}\n\n\t\tif (inChargeOfWindow) {\n\t\t\tdelete win;\n\t\t} else if(useCustomSize) {\n\t\t\twin->size(previousSize[0], previousSize[1]);\n\t\t}\n\t}\n\n\t//class SIBR_VIEW_EXPORT Draw {\n\t//public:\n\t//\tstatic void rectangle(const sibr::Vector3f & color, const UV11 & tl, const UV11 & br, bool fill, float alpha);\n\t//\tstatic void circle(const sibr::Vector3f & color, const UV11 & center, float radius, bool fill, float alpha, int precision = 50);\n\t//};\n\n} // namespace sibr\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/interface/MeshViewer.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#include \"MeshViewer.h\"\r\n\r\n#include <core/graphics/Window.hpp>\r\n#include <core/assets/InputCamera.hpp>\r\n#include <core/graphics/Mesh.hpp>\r\n#include <core/raycaster/Raycaster.hpp>\r\n#include <core/view/InteractiveCameraHandler.hpp>\r\n\r\nconst std::string sibr::MeshRenderer::meshVertexShader =\r\n\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\"uniform mat4 MVP;\t\t\t\t\t\t\t\t\t\\n\"\r\n\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\r\n\"layout(location = 1) in vec3 in_color;\t\t\t\t\\n\"\r\n\"layout(location = 3) in vec3 in_normal;\t\t\t\\n\"\r\n\"out vec3 color;\t\t\t\t\t\t\t\t\t\\n\"\r\n\"out vec3 normal;\t\t\t\t\t\t\t\t\t\\n\"\r\n\"out vec3 vertex;\t\t\t\t\t\t\t\t\t\\n\"\r\n\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\"\tcolor = in_color;\t\t\t\t\t\t\t\t\\n\"\r\n\"\tnormal = in_normal;\t\t\t\t\t\t\t\t\\n\"\r\n\"\tvertex = in_vertex;\t\t\t\t\t\t\t\t\\n\"\r\n\"\tgl_Position = MVP * vec4(in_vertex,1.0);\t\t\\n\"\r\n\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\nconst std::string sibr::MeshRenderer::meshFragmentShader =\r\n\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\"uniform vec3 light_pos;\t\t\t\t\t\t\t\\n\"\r\n\"uniform vec3 forcedColor = vec3(0.7f,0.7f,0.7f);\t\\n\"\r\n\"in vec3 color;\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\"in vec3 normal;\t\t\t\t\t\t\t\t\t\\n\"\r\n\"in vec3 vertex;\t\t\t\t\t\t\t\t\t\\n\"\r\n\"out vec4 out_color;\t\t\t\t\t\t\t\t\\n\"\r\n\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\"\tfloat kd = 0.3;\t\t\t\t\t\t\t \t\t\\n\"\r\n\"\tfloat ks = 0.2;\t\t\t\t\t\t\t \t\t\\n\"\r\n\"\tvec3 L = normalize(light_pos - vertex);\t\t\t\t\t\t\t \t\t\\n\"\r\n\"\tvec3 N = normalize(normal);\t\t\t\t\t\t\t \t\t\t\\n\"\r\n\"\tvec3 R = 2.0*dot(L,N)*N - N;\t\t\t\t\t\t\t \t\t\\n\"\r\n\"\tvec3 V = L;\t\t//light pos = eye\t\t\t\t\t \t\t\t\\n\"\r\n\"\tvec3 diffuse = max(0.0, dot(L,N))*vec3(1, 1, 1);\t\t\t\t\\n\"\r\n\"\tvec3 specular = max(0.0, dot(R,V))*vec3(1, 1, 1);\t\t\t\t\\n\"\r\n\"\tout_color = vec4((1.0 - kd -ks)*forcedColor + kd*diffuse + ks*specular , 1.0);\t \t\\n\"\r\n\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\nsibr::MeshRenderer::MeshRenderer()\r\n{\r\n\tinitShaders();\r\n\r\n\tresetLinesAndPoints();\r\n\t\r\n}\r\n\r\nvoid sibr::MeshRenderer::render(const sibr::Camera & eye)\r\n{\r\n\tglLineWidth(1.0f);\r\n\tglEnable(GL_BLEND);\r\n\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r\n\r\n\tfor (auto & meshParam : listMeshes) {\r\n\t\tif (meshParam.mode == sibr::Mesh::LineRenderMode) {\r\n\t\t\tshaderLines.begin();\r\n\t\t\tmvpLines.set(eye.viewproj());\r\n\t\t\tlineColor.set(meshParam.color);\r\n\t\t\tmeshParam.mesh->render(meshParam.depthTest, false, sibr::Mesh::LineRenderMode);\r\n\t\t\tshaderLines.end();\r\n\t\t} else {\r\n\t\t\tshaderMesh.begin();\r\n\t\t\tconst sibr::Vector3f lightPos = eye.position();\r\n\t\t\tlight_pos.set(lightPos);\r\n\t\t\tmvpMesh.set(eye.viewproj());\r\n\t\t\tforcedColor.set(meshParam.color);\r\n\t\t\tmeshParam.mesh->render(meshParam.depthTest, meshParam.backFaceCulling, meshParam.mode);\r\n\t\t\tshaderMesh.end();\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tif (lines.dirty) {\r\n\t\tupdateMeshLines();\r\n\t}\r\n\tshaderLines.begin();\r\n\tmvpLines.set(eye.viewproj());\r\n\tlines.mesh->render(lines.depthTest, false, sibr::Mesh::LineRenderMode);\r\n\tshaderLines.end();\r\n\r\n\tfloat radiusW = 10.0f;\r\n\tglEnable(GL_VERTEX_PROGRAM_POINT_SIZE);\r\n\tglPointSize(radiusW);\r\n\r\n\tif (points.dirty) {\r\n\t\tupdateMeshPoints();\r\n\t}\r\n\tshaderPoints.begin();\r\n\tmvpPoints.set(eye.viewproj());\r\n\tradiusScreen.set(radiusW);\r\n\tpoints.mesh->render(points.depthTest, false, sibr::Mesh::PointRenderMode);\r\n\r\n\tif (specialPoints.get() != nullptr) {\r\n\t\tspecialPoints->render(false, false, sibr::Mesh::PointRenderMode);\r\n\t}\r\n\r\n\tshaderPoints.end();\r\n\r\n\r\n}\r\n\r\nvoid sibr::MeshRenderer::addMesh(std::shared_ptr<sibr::Mesh> meshPtr, sibr::Mesh::RenderMode mode)\r\n{\r\n\tMeshParams mesh;\r\n\tmesh.mesh = meshPtr;\r\n\tmesh.mode = mode;\r\n\tlistMeshes.push_back(mesh);\r\n}\r\n\r\nvoid sibr::MeshRenderer::addLines(const std::vector<sibr::Vector3f>& listPoints, const sibr::Vector3f & color)\r\n{\r\n\tint nLines = (int)listPoints.size() / 2;\r\n\tfor (int l = 0; l < nLines; ++l) {\r\n\t\tlines.points.push_back(listPoints[2 * l]);\r\n\t\tlines.points.push_back(listPoints[2 * l + 1]);\r\n\t\tlines.colors.push_back(color);\r\n\t}\r\n\r\n\tlines.dirty = true;\r\n}\r\n\r\nvoid sibr::MeshRenderer::addPoint(const sibr::Vector3f & point, const sibr::Vector3f & color)\r\n{\r\n\tpoints.points.push_back(point);\r\n\tpoints.colors.push_back(color);\r\n\tpoints.dirty = true;\r\n}\r\n\r\nvoid sibr::MeshRenderer::addPoints(const std::vector<sibr::Vector3f>& list_points, const sibr::Vector3f & color)\r\n{\r\n\tstd::vector<sibr::Vector3f> colors(list_points.size(), color);\r\n\t\r\n\tpoints.points.reserve(points.points.size() + list_points.size());\r\n\tpoints.points.insert(points.points.end(), list_points.begin(), list_points.end());\r\n\r\n\tpoints.colors.reserve(points.colors.size() + colors.size());\r\n\tpoints.colors.insert(points.colors.end(), colors.begin(), colors.end());\r\n\tpoints.dirty = true;\r\n}\r\n\r\nvoid sibr::MeshRenderer::cleanPoints()\r\n{\r\n\tpoints.points.resize(0);\r\n\tpoints.colors.resize(0);\r\n\tpoints.dirty = true;\r\n}\r\n\r\nvoid sibr::MeshRenderer::cleanLines()\r\n{\r\n\tlines.points.resize(0);\r\n\tlines.colors.resize(0);\r\n\tlines.dirty = true;\r\n}\r\n\r\nvoid sibr::MeshRenderer::resetLinesAndPoints()\r\n{\r\n\tlines.mesh = std::shared_ptr<sibr::Mesh>(new sibr::Mesh());\r\n\tpoints.mesh = std::shared_ptr<sibr::Mesh>(new sibr::Mesh());\r\n\tcleanLines();\r\n\tcleanPoints();\r\n}\r\n\r\nvoid sibr::MeshRenderer::resetMeshes()\r\n{\r\n\tlistMeshes.resize(0);\r\n}\r\n\r\nvoid sibr::MeshRenderer::initShaders()\r\n{\r\n\r\n\tshaderMesh.init(\"meshShader\", meshVertexShader, meshFragmentShader);\r\n\tmvpMesh.init(shaderMesh, \"MVP\");\r\n\tlight_pos.init(shaderMesh, \"light_pos\");\r\n\tforcedColor.init(shaderMesh, \"forcedColor\");\r\n\r\n\tstd::string lineVertexShader =\r\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"uniform mat4 MVP;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\r\n\t\t\"layout(location = 1) in vec3 in_color;\t\t\t\\n\"\r\n\t\t\"out vec3 color_vert;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"\tgl_Position = MVP * vec4(in_vertex,1.0);\t\t\\n\"\r\n\t\t\"\tcolor_vert = in_color;\t\t\t\t\t\t\t\\n\"\r\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\tstd::string lineFragmentShader =\r\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"in vec3 color_vert;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"uniform vec3 color;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"\tout_color = vec4( color_vert, 1.0 );\t \t\t\t\t\\n\"\r\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\tshaderLines.init(\"LineShader\", lineVertexShader, lineFragmentShader);\r\n\tmvpLines.init(shaderLines, \"MVP\");\r\n\tlineColor.init(shaderLines, \"color\");\r\n\r\n\tstd::string pointVertexShader =\r\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"uniform mat4 MVP;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"uniform float radiusScreen;\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"layout(location = 0) in vec3 in_vertex;\t\t\t\\n\"\r\n\t\t\"layout(location = 1) in vec3 in_color;\t\t\t\t\\n\"\r\n\t\t\"out vec3 color_vert;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"\tgl_Position = MVP * vec4(in_vertex,1.0);\t\t\\n\"\r\n\t\t\"\tgl_PointSize = radiusScreen;\t\t\t\t\t\t\t\\n\"\r\n\t\t\"\tcolor_vert = in_color;\t\t\t\t\t\t\t\\n\"\r\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\tstd::string pointFragmentShader =\r\n\t\t\"#version 420\t\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"in vec3 color_vert;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"out vec4 out_color;\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"void main(void) {\t\t\t\t\t\t\t\t\t\\n\"\r\n\t\t\"\tout_color = vec4( color_vert, 1.0 );\t \t\t\\n\"\r\n\t\t\"}\t\t\t\t\t\t\t\t\t\t\t\t\t\\n\";\r\n\r\n\tshaderPoints.init(\"PointShader\", pointVertexShader, pointFragmentShader);\r\n\tmvpPoints.init(shaderPoints, \"MVP\");\r\n\tradiusScreen.init(shaderPoints, \"radiusScreen\");\r\n}\r\n\r\nvoid sibr::MeshRenderer::updateMeshPoints(void)\r\n{\r\n\tstd::vector<float> vertexBuffer;\r\n\tfor (int vertex_id = 0; vertex_id<(int)points.points.size(); vertex_id++) {\r\n\t\tfor (int c = 0; c<3; ++c) {\r\n\t\t\tvertexBuffer.push_back(points.points.at(vertex_id)[c]);\r\n\t\t}\r\n\t}\r\n\r\n\tpoints.mesh->vertices(vertexBuffer);\r\n\tpoints.mesh->colors(points.colors);\r\n\r\n\tpoints.dirty = false;\r\n}\r\n\r\nvoid sibr::MeshRenderer::updateMeshLines(void)\r\n{\r\n\tstd::vector<float> vertexBuffer;\r\n\tstd::vector<uint> indicesBuffer(3 * (lines.points.size() / 2));\r\n\tstd::vector<sibr::Vector3f> colors(lines.points.size());\r\n\tfor (int vertex_id = 0; vertex_id<(int)lines.points.size(); vertex_id += 2) {\r\n\t\tfor (int c = 0; c<3; ++c) {\r\n\t\t\tvertexBuffer.push_back(lines.points.at(vertex_id)[c]);\r\n\t\t}\r\n\t\tfor (int c = 0; c<3; ++c) {\r\n\t\t\tvertexBuffer.push_back(lines.points.at(vertex_id + 1)[c]);\r\n\t\t}\r\n\r\n\t\tindicesBuffer.at(3 * (vertex_id / 2)) = vertex_id;\r\n\t\tindicesBuffer.at(3 * (vertex_id / 2) + 1) = vertex_id;\r\n\t\tindicesBuffer.at(3 * (vertex_id / 2) + 2) = vertex_id + 1;\r\n\r\n\t\tcolors.at(vertex_id) = lines.colors.at(vertex_id / 2);\r\n\t\tcolors.at(vertex_id + 1) = lines.colors.at(vertex_id / 2);\r\n\t}\r\n\r\n\tlines.mesh->vertices(vertexBuffer);\r\n\tlines.mesh->colors(colors);\r\n\tlines.mesh->triangles(indicesBuffer);\r\n\r\n\tlines.dirty = false;\r\n}\r\n\r\nsibr::MeshViewer::MeshViewer()\r\n{\r\n\trenderer = std::make_shared<MeshRenderer>();\r\n\tinteractCam = std::make_shared<sibr::InteractiveCameraHandler>(true);\r\n\tinteractCam->setFPSCameraSpeed(1);\r\n\tinteractCam->switchMode(sibr::InteractiveCameraHandler::InteractionMode::TRACKBALL);\r\n\tinChargeOfWindow = false;\r\n\tfpsCounter.init(sibr::Vector2f(10, 10));\r\n}\r\n\r\nsibr::MeshViewer::MeshViewer(const sibr::Vector2i & screenRes, const sibr::Mesh & mesh, bool launchRenderingLoop)\r\n{\r\n\twindow.reset(new Window(screenRes[0], screenRes[1], \"MeshViewer\" ));\r\n\trenderer = std::make_shared<MeshRenderer>();\r\n\tinteractCam = std::make_shared<sibr::InteractiveCameraHandler>(new sibr::InteractiveCameraHandler());\r\n\tinteractCam->setFPSCameraSpeed(1);\r\n\tinteractCam->switchMode(sibr::InteractiveCameraHandler::InteractionMode::TRACKBALL);\r\n\tinChargeOfWindow = true;\r\n\r\n\tsetMainMesh(mesh);\r\n\r\n\tif (launchRenderingLoop) {\r\n\t\trenderLoop();\r\n\t}\r\n}\r\n\r\nvoid sibr::MeshViewer::setMainMesh(const sibr::Mesh & mesh, sibr::Mesh::RenderMode mode, bool updateCam, bool setupRaycaster)\r\n{\r\n\tsetMainMesh(*window, mesh, mode, updateCam, setupRaycaster);\r\n}\r\n\r\nvoid sibr::MeshViewer::setMainMesh(sibr::Window & win, const sibr::Mesh & mesh, sibr::Mesh::RenderMode mode, bool updateCam, bool setupRaycaster)\r\n{\r\n\tsibr::Mesh::Ptr meshGL = std::make_shared<sibr::Mesh>(true);\r\n\tmeshGL->vertices(mesh.vertices());\r\n\tmeshGL->triangles(mesh.triangles());\r\n\tif (mesh.hasNormals()) {\r\n\t\tmeshGL->normals(mesh.normals());\r\n\t}\r\n\r\n\trenderer->resetMeshes();\r\n\trenderer->addMesh(meshGL, mode);\r\n\r\n\tif (updateCam) {\r\n\t\tinteractCam->setup(meshGL, win.viewport());\r\n\t\tinteractCam->getTrackball().fromMesh(*meshGL, win.viewport());\r\n\t}\r\n\r\n\tif (setupRaycaster) {\r\n\t\traycaster = std::make_shared<sibr::Raycaster>();\r\n\t\traycaster->init();\r\n\t\traycaster->addMesh(*meshGL);\r\n\t}\r\n\r\n\tfloat radius;\r\n\tsibr::Vector3f pos;\r\n\tmeshGL->getBoundingSphere(pos, radius);\r\n\tinteractCam->setFPSCameraSpeed(radius/10.0f);\r\n\r\n}\r\n\r\nvoid sibr::MeshViewer::render()\r\n{\r\n\tif (window.get()) {\r\n\t\trender(window->viewport(), interactCam->getCamera());\r\n\t\twindow->swapBuffer();\r\n\t}\r\n}\r\n\r\nvoid sibr::MeshViewer::renderLoop(sibr::Window & window)\r\n{\r\n\tbool doLoop = true;\r\n\r\n\twhile (doLoop && window.isOpened() ) {\r\n\t\tsibr::Input::poll();\r\n\r\n\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\r\n\t\t\tdoLoop = false;\r\n\t\t}\r\n\r\n\t\tinteractCam->update(sibr::Input::global(), 1 / 60.0f, window.viewport());\r\n\t\t\r\n\t\twindow.viewport().bind();\r\n\t\twindow.viewport().clear(sibr::Vector3f(0.9f, 0.9f, 0.9f));\r\n\t\trenderer->render(interactCam->getCamera());\r\n\t\tinteractCam->onRender(window.viewport());\r\n\r\n\t\twindow.swapBuffer();\r\n\t}\r\n\r\n\t\r\n}\r\n\r\nvoid sibr::MeshViewer::render(const sibr::Viewport & viewport, const sibr::Camera & eye )\r\n{\r\n\tviewport.bind();\r\n\tviewport.clear(sibr::Vector3f(0.9f, 0.9f, 0.9f));\r\n\trenderer->render(eye);\r\n\tinteractCam->onRender(viewport);\r\n\tfpsCounter.update(true);\r\n}\r\n\r\nvoid sibr::MeshViewer::render(const sibr::Viewport & viewport)\r\n{\r\n\trender(viewport, interactCam->getCamera());\r\n}\r\n\r\nvoid sibr::MeshViewer::render(const sibr::Camera & eye)\r\n{\r\n\tif (window.get()) {\r\n\t\trender(window->viewport(), eye);\r\n\t\twindow->swapBuffer();\r\n\t}\r\n}\r\n\r\nvoid sibr::MeshViewer::renderLoop(std::shared_ptr<sibr::Window> otherWindow)\r\n{\r\n\r\n\tif (!otherWindow.get() && !window->isOpened()) {\r\n\t\treturn;\r\n\t}\r\n\tif (otherWindow.get() && !window.get() ) {\r\n\t\twindow = otherWindow;\r\n\t}\r\n\r\n\twhile (window->isOpened()) {\r\n\t\tsibr::Input::poll();\r\n\r\n\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\r\n\t\t\twindow->close();\r\n\t\t}\r\n\r\n\t\tinteractCam->update(sibr::Input::global(), 1 / 60.0f, window->viewport());\r\n\t\trender();\r\n\t}\r\n\r\n\treset();\r\n}\r\n\r\nvoid sibr::MeshViewer::renderLoop(const std::function<void(MeshViewer*)> & f, bool customRendering, bool doReset)\r\n{\r\n\tbool doRender = true;\r\n\twhile (doRender && window->isOpened()) {\r\n\t\tsibr::Input::poll();\r\n\t\tinput = sibr::Input::global();\r\n\t\tif (input.key().isPressed(sibr::Key::Escape)) {\r\n\t\t\tdoRender = false; \r\n\t\t\tif (inChargeOfWindow) {\r\n\t\t\t\twindow->close();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tinteractCam->update(input,1/60.0f, window->viewport());\r\n\r\n\t\tf(this);\r\n\r\n\t\tif (!customRendering) {\r\n\t\t\trender();\r\n\t\t}\r\n\t}\r\n\tif(doReset) {\r\n\t\treset();\r\n\t}\r\n}\r\n\r\nvoid sibr::MeshViewer::reset()\r\n{\r\n\tif (inChargeOfWindow) {\r\n\t\tinteractCam.reset();\r\n\t\trenderer.reset();\r\n\t\traycaster.reset();\r\n\t\twindow.reset();\r\n\t}\r\n\t\r\n}\r\n\r\nvoid sibr::MeshViewer::demo()\r\n{\r\n\tsibr::Mesh::Ptr meshPtr = sibr::Mesh::getTestCube();\r\n\r\n\tsibr::MeshViewer meshViewer(sibr::Vector2i(1600, 1200), *meshPtr);\r\n\r\n\tmeshViewer.renderer->addPoints(meshPtr->vertices(), sibr::Vector3f(0, 1, 0));\r\n\t\r\n\tfor (const auto & tri : meshPtr->triangles()) {\r\n\t\tfor (int k = 0; k < 3; ++k) {\r\n\t\t\tmeshViewer.renderer->addLines( \r\n\t\t\t\t{ meshPtr->vertices()[tri[k]], meshPtr->vertices()[tri[(k + 1) % 3]] },\r\n\t\t\t\tsibr::Vector3f(1, 0, 0));\r\n\t\t}\r\n\t}\r\n\r\n\tmeshViewer.renderLoop();\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/interface/MeshViewer.h",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n#ifndef _DISABLE_EXTENDED_ALIGNED_STORAGE\n# define _DISABLE_EXTENDED_ALIGNED_STORAGE\n#endif\n#include \"../Config.hpp\"\n#include <core/system/Vector.hpp>\n#include <core/graphics/Shader.hpp>\n#include <core/graphics/Mesh.hpp>\n#include <core/graphics/Window.hpp>\n#include <functional>\n#include <core/graphics/Input.hpp>\n#include <core/view/FPSCounter.hpp>\n\nnamespace sibr {\n\n\tclass InteractiveCameraHandler;\n\tclass Raycaster;\n\tclass Camera;\n\n\t/**\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT MeshRenderer {\n\n\t\tSIBR_CLASS_PTR(MeshRenderer);\n\n\t\tstruct MeshData {\n\t\t\tMeshData() : dirty(false), depthTest(true) {}\n\n\t\t\tstd::shared_ptr<sibr::Mesh> mesh;\n\t\t\tstd::vector<sibr::Vector3f> points;\n\t\t\tstd::vector<sibr::Vector3f> colors;\n\t\t\tbool dirty;\n\t\t\tbool depthTest;\n\t\t};\n\n\t\tstruct MeshParams {\n\t\t\tMeshParams() : depthTest(true), backFaceCulling(true), color(sibr::Vector3f(0.7f,0.7f,0.7f)) {}\n\t\t\tstd::shared_ptr<sibr::Mesh> mesh;\n\t\t\tsibr::Mesh::RenderMode mode;\n\t\t\tsibr::Vector3f color;\n\t\t\tbool depthTest;\n\t\t\tbool backFaceCulling;\n\t\t};\n\n\tpublic:\n\t\tMeshRenderer();\n\t\tvoid render(const sibr::Camera& viewproj);\n\n\t\tvoid addMesh(std::shared_ptr<sibr::Mesh> meshPtr, sibr::Mesh::RenderMode mode = sibr::Mesh::FillRenderMode );\n\n\t\tvoid addLines(const std::vector<sibr::Vector3f> & listPoints, const sibr::Vector3f & color);\n\n\t\tvoid addPoint(const sibr::Vector3f & point, const sibr::Vector3f & color);\n\t\tvoid addPoints(const std::vector<sibr::Vector3f> & listPoints, const sibr::Vector3f & color);\n\t\tvoid cleanPoints();\n\t\tvoid cleanLines();\n\n\t\tvoid resetLinesAndPoints();\n\t\tvoid resetMeshes();\n\n\t\tstd::vector<MeshParams> & getMeshesParams() { return listMeshes; }\n\n\tpublic:\n\t\tstd::vector<MeshParams> listMeshes;\n\n\t\tMeshData lines;\n\t\tMeshData points;\n\t\tsibr::Mesh::Ptr specialPoints;\n\n\t\tstatic const std::string meshVertexShader;\n\t\tstatic const std::string meshFragmentShader;\n\n\t\tsibr::GLShader\t\t\t\tshaderLines;\n\n\tprivate:\n\n\t\tsibr::GLShader\t\t\t\tshaderMesh;\n\t\tsibr::GLShader\t\t\t\tshaderPoints;\n\n\t\tsibr::GLParameter\t\t\tmvpLines;\n\t\tsibr::GLuniform<sibr::Vector3f>\tlineColor = sibr::Vector3f(1,0,0);\n\t\tsibr::GLParameter\t\t\tmvpPoints;\n\t\tsibr::GLParameter\t\t\tmvpMesh;\n\t\tsibr::GLParameter\t\t\tforcedColor;\n\n\t\tsibr::GLParameter\t\t\tlight_pos;\n\t\tsibr::GLParameter\t\t\tradiusScreen;\n\n\t\n\n\t\tvoid initShaders();\n\t\tvoid updateMeshPoints(void);\n\t\tvoid updateMeshLines(void);\n\n\t};\n\n\t/**\n\t* \\ingroup sibr_view\n\t*/\n\tclass SIBR_VIEW_EXPORT MeshViewer {\n\t\t\n\t\tSIBR_CLASS_PTR(MeshViewer);\n\n\tpublic:\n\t\tMeshViewer();\n\n\t\tMeshViewer(\n\t\t\tconst sibr::Vector2i & screenRes,\n\t\t\tconst sibr::Mesh & mesh = sibr::Mesh(),\n\t\t\tbool launchRenderingLoop = false);\n\n    virtual void setMainMesh(\n      const sibr::Mesh & mesh,\n      sibr::Mesh::RenderMode mode = sibr::Mesh::FillRenderMode,\n      bool updateCam = true,\n      bool setupRaycaster = true\n    );\n\n    virtual void setMainMesh(\n      sibr::Window & win,\n      const sibr::Mesh & mesh,\n      sibr::Mesh::RenderMode mode = sibr::Mesh::FillRenderMode,\n      bool updateCam = true,\n      bool setupRaycaster = true\n    );\n\n    virtual void render(const sibr::Viewport & viewport, const sibr::Camera & eye);\n    virtual void render(const sibr::Viewport & viewport);\n    virtual void render(const sibr::Camera & eye);\n    virtual void render();\n\n    virtual void renderLoop(sibr::Window & window);\n\n\t\tvoid renderLoop(std::shared_ptr<sibr::Window> window);\n\t\tvoid renderLoop(const std::function<void(MeshViewer*)> & f = [](MeshViewer* m){} , bool customRendering = false, bool doReset = true);\n\n    virtual void reset();\n\n    static void demo();\n\n\n\n  public:\n    sibr::Input input;\n    sibr::FPSCounter fpsCounter;\n    std::shared_ptr<sibr::Window>\t\t\twindow;\n    std::shared_ptr<MeshRenderer>\t\t\trenderer;\n    std::shared_ptr<sibr::InteractiveCameraHandler>\t\tinteractCam;\n    std::shared_ptr<sibr::Raycaster>\t\traycaster;\n\n    bool inChargeOfWindow;\n  };\n\n} //namespace sibr\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_colored_mesh.frag",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\nlayout(location = 0) out vec4 out_color;\r\n  \r\nuniform vec3    light_position;\r\nuniform vec3    user_color;                    \r\nuniform float   alpha;\r\nuniform bool    phong_shading;\r\nuniform bool    use_mesh_color;\r\n\r\nin vec3 color;\r\nin vec3 normal;\r\nin vec3 position;            \r\n\r\nvoid main(void) {\r\n\r\n    vec3 col;\r\n    if(use_mesh_color){\r\n        col = color;\r\n    } else {\r\n        col = user_color;\r\n    }\r\n    \r\n    out_color = vec4(col, alpha);\r\n    \r\n    if(phong_shading){\r\n        float kd = 0.2;\r\n        float ks = 0.1;\r\n        vec3 L = normalize(light_position - position);\t\r\n        vec3 N = normalize(normal);\r\n        vec3 R = - reflect(L,N);\r\n        vec3 V = L;\t\t//light pos = eye\r\n        float diffuse = max(0.0, dot(L,N));\r\n        float specular = max(0.0, dot(R,V));\r\n        out_color.xyz = (1.0 - kd - ks)*col + (kd*diffuse + ks*specular)* vec3(1, 1, 1);\r\n    }  \r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_colored_mesh.vert",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\nlayout(location = 0) in vec3 in_vertex;   \r\nlayout(location = 1) in vec3 in_color; \r\nlayout(location = 3) in vec3 in_normal;  \r\n  \r\nuniform mat4 mvp;    \r\n          \r\nout vec3 color;\r\nout vec3 normal;\r\nout vec3 position;                      \r\n\r\nvoid main(void) {\r\n    gl_Position = mvp * vec4(in_vertex, 1.0);\r\n    color = in_color;\r\n    normal = in_normal;\r\n    position = in_vertex;\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_colored_per_triangle_normals.geom",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#version 420\r\n\r\nlayout(triangles) in;\r\nlayout(line_strip, max_vertices = 2) out;\r\n\r\nuniform mat4 mvp;\r\nuniform float normals_size;\r\n\r\nout vec3 color;\r\nout vec3 normal;\r\nout vec3 position;\r\n\r\nvoid main(void) {\r\n\tvec3 a = gl_in[0].gl_Position.xyz;\r\n\tvec3 b = gl_in[1].gl_Position.xyz;\r\n\tvec3 c = gl_in[2].gl_Position.xyz;\r\n\r\n\tvec3 tri_normal = normalize(cross(b-a,c-b));\r\n\tvec3 tri_center = (a+b+c)/3.0;\r\n\tgl_Position = mvp*vec4(tri_center,1.0);\r\n\tcolor = vec3(0.0);\r\n\tnormal = vec3(0.0);\r\n\tposition = vec3(0.0);\r\n\tEmitVertex();\r\n\tgl_Position = mvp*vec4(tri_center + normals_size*tri_normal, 1.0);\r\n\tcolor = vec3(0.0);\r\n\tnormal = vec3(0.0);\r\n\tposition = vec3(0.0);\r\n\tEmitVertex();\r\n\tEndPrimitive();\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_colored_per_triangle_normals.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec3 in_vertex;   \n\nvoid main(void) {\n    gl_Position = vec4(in_vertex, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_colored_per_vertex_normals.geom",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\nlayout(points) in;\r\nlayout(line_strip, max_vertices = 2) out;\r\n\r\nuniform mat4 mvp;\r\nuniform float normals_size;\r\n\r\nin vec3 normals[];\r\n\r\nout vec3 color;\r\nout vec3 normal;\r\nout vec3 position;\r\n\r\n\r\nvoid main(void) {\r\n\tgl_Position = mvp*(gl_in[0].gl_Position);\r\n\tcolor = vec3(0.0);\r\n\tnormal = vec3(0.0);\r\n\tposition = vec3(0.0);\r\n\tEmitVertex();\r\n\tgl_Position = mvp* vec4(gl_in[0].gl_Position.xyz + normals_size*normals[0],1.0);\r\n\tcolor = vec3(0.0);\r\n\tnormal = vec3(0.0);\r\n\tposition = vec3(0.0);\r\n\tEmitVertex();\r\n\tEndPrimitive();\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_colored_per_vertex_normals.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec3 in_vertex;   \nlayout(location = 3) in vec3 in_normal;  \n\nout vec3 normals;\n\nvoid main(void) {\n    gl_Position = vec4(in_vertex, 1.0);\n\tnormals = in_normal;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_points.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\n\nuniform vec3 user_color;                    \nuniform float alpha;\nin vec3 color;\n\nvoid main(void) {\n//    out_color = vec4(user_color, alpha);\n    out_color = vec4(color, alpha);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_points.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec3 in_vertex;   \nlayout(location = 1) in vec3 in_color; \n\nuniform mat4 mvp;    \nuniform int radius;\nout vec3 color;\n\nvoid main(void) {\n    gl_Position = mvp * vec4(in_vertex, 1.0);\n    gl_PointSize = radius;\n    color = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_uv_tex.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\nlayout(binding = 0) uniform sampler2D input_rgb;\n\nin vec2 out_uv;\n\nuniform float alpha;\n\nvoid main() {\n    out_color = vec4(texture(input_rgb, out_uv).xyz, alpha);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alpha_uv_tex_array.frag",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\nlayout(location = 0) out vec4 out_color;\r\nlayout(binding = 0) uniform sampler2DArray input_rgbs;\r\n\r\nin vec2 out_uv;\r\n\r\nuniform float alpha;\r\nuniform int slice;\r\n\r\nvoid main() {\r\n    vec3 uv_cam = vec3(out_uv, slice);\r\n    out_color = vec4(texture(input_rgbs, uv_cam).xyz, alpha);\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alphaimgview.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D \ttex;\nlayout(location= 0) out vec4 out_color;\n\nin vec2 tex_coord;\nuniform float \t\talpha;\n\nvoid main(void) {\n    vec2 texcoord = tex_coord ;\n    out_color = vec4(texture(tex,texcoord).rgb, alpha);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/alphaimgview.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec2 in_vertex;\nlayout(location = 1) in vec2 in_texcoord;\n\nout vec2 tex_coord;\n\nuniform vec4 imagefit;\n\nvec2\t\tfitTexcoord( vec2 tc ) {\n\ttc.x = tc.x*imagefit[0] + imagefit[2+0];\n\ttc.y = tc.y*imagefit[1] + imagefit[2+1];\n\treturn tc;\n}\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex, 0.0, 1.0);\n\ttex_coord = fitTexcoord(in_texcoord);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/anaglyph.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D left;\nlayout(binding = 1) uniform sampler2D right;\nlayout(location= 0) out vec4 out_color;\n\nin vec2 vertex_coord;\n\nvoid main(void) {\n    vec2 texcoord = (vertex_coord + vec2(1.0)) / 2.0;\n    vec4 cl = texture(left, texcoord);\n    vec4 cr = texture(right, texcoord);\n    out_color = vec4(cl.r, cr.g, cr.b, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/anaglyph.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec2 in_vertex;\n\nout vec2 vertex_coord;\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex, 0.0, 1.0);\n\tvertex_coord = in_vertex;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/axisgizmo.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nin vec3 axis_color;\nout vec4 out_color;\n\nvoid main(void) {\n    out_color = vec4(axis_color, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/axisgizmo.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 1) in vec3 in_color;\n\nout vec3 axis_color;\n\nvoid main(void) {\n\taxis_color = in_color;\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/camstub.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform vec3 color;\n\nout vec4 out_color;\n\nvoid main(void) {\n    out_color = vec4(color, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/camstub.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\n\nlayout(location = 0) in vec3 in_vertex;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/depth.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\nlayout(binding  = 0) uniform sampler2D image; /// \\todo TODO: remove\n\n//in vec3 vertex_coord;\n//uniform vec3 iCamPos;\n\nuniform vec2 size;\n\nvoid main(void) {\n  vec2 tC       = gl_FragCoord.xy / size;\n  out_color.xyz = vec3(0.0);//texture(image, tC.xy).xyz;\n  out_color.w   = gl_FragCoord.z;\n  //out_color.w   = distance(vertex_coord, iCamPos);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/depth.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 proj;\n\nlayout(location = 0) in vec3 in_vertex;\n\n//out vec2 texture_coord;\n//out vec3 normal_coord;\n\nvoid main(void) {\n\tgl_Position = proj * vec4(in_vertex,1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/depthonly.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out float out_color;\n\nvoid main(void) {\n  out_color   = gl_FragCoord.z;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/depthonly.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 proj;\n\nlayout(location = 0) in vec3 in_vertex;\n\nvoid main(void) {\n\tgl_Position = proj * vec4(in_vertex,1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/image_viewer.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nin vec2 texcoord;\n\nlayout(binding = 0) uniform sampler2D in_texture;\n\nuniform vec4 minVal = vec4(0.0);\nuniform vec4 maxVal = vec4(1.0);\nuniform vec4 channels = vec4(1.0);\n\nlayout(location = 0) out vec4 out_color;\n\nvoid main(void)\n{\n\tif(any(greaterThan(texcoord, vec2(1.0))) || any(lessThan(texcoord, vec2(0.0)))){\n\t\tdiscard;\n\t}\n\t\n\tvec4 col = texture(in_texture, texcoord);\n\t// Rescale.\n\tout_color = channels*(col - minVal)/(maxVal - minVal);\n\n\t// If only one channel is enabled, no alpha and B&W image.\n\tif(dot(channels, vec4(1.0)) == 1.0){\n\t\tfloat val = dot(out_color, channels);\n\t\tout_color.rgb = vec3(val);\n\t\tout_color.a = 1.0;\n\t}\n\n\t// Ensure visibility when alpha is disabled.\n\tif(channels[3] == 0.0f){\n\t\tout_color.a;\n\t}\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/image_viewer.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec3 in_vertex;\n\nuniform float scale;\nuniform vec2 pos;\nuniform vec2 size;\nuniform bool correctRatio;\n\nout vec2 texcoord;\n\nvoid main(void) {\n\tvec2 position = scale * vec2(1.0, correctRatio ? (size.y/size.x) : 1.0) * in_vertex.xy + pos; \n\tgl_Position = vec4(in_vertex.xy, 0.0, 1.0);\n\ttexcoord = position * 0.5 + 0.5;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/mesh_color.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform vec3 lightDir;\n\nout vec4 out_color;\n\nin vec3 color_vert;\nin vec3 vertexPos; \nin vec3 normalPos;\n\nvoid main(void) {\n\tfloat kd = 0.2;\n\tvec3 normal = normalize(normalPos);\n\tvec3 shading = max(0.0,dot(lightDir,normal))*color_vert;\n\t\n\tout_color = vec4( (1.0-kd)*color_vert + kd*shading, 1.0);\n\t//out_color = vec4( normal , 1.0 );\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/mesh_color.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\nuniform mat4 invMV;\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 1) in vec3 in_color;\nlayout(location = 3) in vec3 in_normal;\n\nout vec3 color_vert;\nout vec3 vertexPos; \nout vec3 normalPos;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\tvertexPos = vec3(MVP * vec4(in_vertex,1.0));\n\tnormalPos = vec3(invMV*vec4(in_normal,1.0));\n\t\n\tcolor_vert = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/mesh_debugview.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform vec3 lightDir;\n\nuniform bool hasNormal = true;\nout vec4 out_color;\n\nin vec3 color_vert;\nin vec3 vertexPos; \nin vec3 normalPos;\n\nvoid main(void) {\n\tfloat kd = 0.8;\n\tfloat ks = 0.15;\n\tfloat diffuse = 1.0;\t\n\tfloat specular = 0.0;\n\n\tif(hasNormal){\n\t\tvec3 L = normalize(lightDir);\t\t\t\t\n\t\tvec3 N = normalize(normalPos);\t\t\t\t\t\t\t\n\t\tvec3 R = reflect(L,N);//2.0*dot(L,N)*N - N;\t\t\t\t\t\t\n\t\tvec3 V = L;\t\t\t\t\n\t\tdiffuse = max(0.0, dot(L,N));\t\n\t\tspecular = max(0.0, dot(R,V));\n\t}\n\tout_color.rgb = (1.0-kd-ks)*color_vert + kd*diffuse*color_vert + ks*specular;\n\tout_color.a = 1.0;\n\t\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/mesh_debugview.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 MVP;\nuniform mat4 invMV;\nuniform bool hasColor = true;\nuniform vec3 defaultColor = vec3(0.9,0.9,0.9);\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 1) in vec3 in_color;\nlayout(location = 3) in vec3 in_normal;\n\nout vec3 color_vert;\nout vec3 vertexPos; \nout vec3 normalPos;\n\nvoid main(void) {\n\tgl_Position = MVP * vec4(in_vertex,1.0);\n\tvertexPos = vec3(MVP * vec4(in_vertex,1.0));\n\tnormalPos = vec3(vec4(in_normal,1.0));\n\t\n\tcolor_vert = hasColor ? in_color : defaultColor;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/mesh_normal.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\n\nin vec3 normal_coord;\n\nvoid main(void) {\n    vec3 color = vec3(normalize(normal_coord));\n\t   color = color * 0.5 + 0.5;\n    out_color = vec4(color, 1.0);\n    //out_color = vec4(dot(normal_coord, vec3(0.58,-0.58,0.08)));\n    if (length(normal_coord) == 0.0) { // no normal present\n        out_color = vec4(0.8);\n    }\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/mesh_normal.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 proj;\n\nlayout(location = 0) in vec3 in_vertex;\n//layout(location = 1) in vec2 in_texcoord;\nlayout(location = 1) in vec3 in_normal;\n\n//out vec2 texture_coord;\nout vec3 normal_coord;\n\nvoid main(void) {\n\tgl_Position = proj * vec4(in_vertex,1.0);\n\t//texture_coord = in_texcoord;\n  normal_coord  = in_normal;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/number.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nin vec2 uv_coord;\n\nuniform float value;\nuniform int count;\n\nout vec4 out_color;\n\nconst float digits[10] = float[](0x69996,0x26222,0x6924F,0x69396,0x99F11,0xF861E,0x68E96,0xF1248,0x69696,0x69716);\n\nfloat printDigit(int digit, vec2 position){\n\t// Margin scaling/shift\n\tposition *= 1.4;\n\tposition -= 0.2;\n\t// Early discard.\n\tif(position.x < 0.0 || position.x > 1.0 || position.y < 0.0 || position.y > 1.0){\n\t\treturn 0.0;\n\t}\n\t// [0,1] -> discrete[0,4]x[0,5]\n\tvec2 newPos = floor(vec2(4.0-4.0*position.x,5.0*position.y));\n\t// -> corresponding bit\n\tfloat index = 4*newPos.y + newPos.x;\n\t// -> get the index-th bit\n\tfloat isIn = mod(floor(digits[digit]/pow(2.0,index)),2.0);\n\treturn isIn;\n}\n\nfloat printPoint(vec2 position){\n\tposition *= 1.4;\n\tposition -= 0.02;\n\tif(position.x < 0.0 || position.x > 1.0 || position.y < 0.0 || position.y > 1.0){\n\t\treturn 0.0;\n\t}\n\treturn length(position - vec2(0.2, 0.4)) < 0.182 ? 1.0 : 0.0;\n\t\n}\n\nvoid main(void) {\n\tfloat deca = printDigit(int(mod(value/10,10)), uv_coord);\n\tfloat unit = printDigit(int(mod(value,10)), uv_coord-vec2(1.0,0.0));\n\tfloat deci = printDigit(int(mod(value*10,10)), uv_coord-vec2(2.5,0.0));\n\tfloat centi = printDigit(int(mod(value*100,10)), uv_coord-vec2(3.5,0.0));\n\tfloat point = printPoint(uv_coord-vec2(2.0,0.0));\n\tfloat color = clamp(deca+unit+deci+centi+point,0.0,1.0);\n  \tout_color = vec4(color,color, color, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/number.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform vec2 position;\nuniform vec2 scale;\nuniform int count;\n\nlayout(location = 0) in vec3 in_vertex;\nout vec2 uv_coord;\n\nvoid main(void) {\n\tuv_coord = vec2(count+0.5, 1.0) * (in_vertex.xy * 0.5 + 0.5);\n\tgl_Position = vec4(scale * vec2(count, 1.0) * (in_vertex.xy - position) + position,0.0, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/skybox.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#version 420\n\nlayout(binding = 0) uniform samplerCube in_CubeMap;\nlayout(location= 0) out vec4 out_Color;\n\nin VSOUT\n{\n  vec3 tc;\n} in_Frag;\n\nvoid main(void)\n{\n  out_Color = texture(in_CubeMap, in_Frag.tc);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/skybox.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#version 420\n\nout VSOUT\n{\n  vec3 tc;\n} out_Vert;\n\nuniform mat4 in_View;\nuniform vec2 in_Aspect;\n\n\nconst float fov = 70.0;\nconst float vecZ = in_Aspect.y / tan(radians(fov / 2.0));\n//const float vecZ = 0.8033332538;\n\nmat3 rotationMatrix(vec3 axis, float angle)\n{\n  axis = normalize(axis);\n  float s = sin(angle);\n  float c = cos(angle);\n  float oc = 1.0 - c;\n  return mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,\n\t      oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,\n\t      oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c);\n}\n\nvoid main(void)\n{\n\n  vec2[4] vertices = vec2[4](vec2(-1.0, -1.0),\n  \t\t\t     vec2( 1.0, -1.0),\n  \t\t\t     vec2(-1.0,  1.0),\n  \t\t\t     vec2( 1.0,  1.0));\n\n\n  vec3 vertex = vec3(vertices[gl_VertexID], -1.0);\n\n  // out gl_Position\n  gl_Position = vec4(vertex, 1.0);\n\n  vertex.y = vertex.y * in_Aspect.y;\n  vertex.z = -vecZ;\n\n  out_Vert.tc = mat3(in_View) * vertex;\n  //out_Vert.tc = rotationMatrix(vec3(1,0,0), -1.14) * out_Vert.tc;\n  out_Vert.tc.z = -out_Vert.tc.z;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/text-imgui.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout (location = 0) out vec4 fragColor;\n\nin INTERFACE {\n\tvec4 col;\n\tvec2 uv;\n} In ;\n\nuniform sampler2D tex;\n\nvoid main(){\n\tfragColor = In.col * texture(tex, In.uv);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/text-imgui.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n// Attributes\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 1) in vec3 in_color;\nlayout(location = 2) in vec2 in_uv;\n//layout(location = 3) in vec3 in_normal;\n\n// Uniforms\nuniform vec3 position = vec3(0.0,0.0,0.0); // Position in NDC space\nuniform float scale = 1.0;\nuniform vec2 viewport = vec2(1.0);\nuniform bool forceOpacity = true;\n\nout INTERFACE {\n\tvec4 col;\n\tvec2 uv;\n} Out ;\n\nvoid main(){\n\t// Should be in -1,1\n\t// Multiply by the w component to stay at a constant screen size.\n\tgl_Position = vec4(position.xy+scale*in_vertex.xy/viewport, 0.0, 1.0);\n\tOut.uv = in_uv;\n\tOut.col = vec4(in_color, forceOpacity ? 1.0 : in_vertex.z);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/texture.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D tex;\nlayout(location= 0) out vec4 out_color;\n\nin vec2 tex_coord;\n\nvoid main(void) {\n    vec2 texcoord = tex_coord ;\n    out_color = texture(tex,texcoord);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/texture.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec2 in_vertex;\nlayout(location = 1) in vec2 in_texcoord;\n\nout vec2 tex_coord;\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex, 0.0, 1.0);\n\ttex_coord = in_texcoord;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/topview.fp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/**\n * \\file topview.fp\n *\n * Basic shader to render textured/colored geometry\n */\n\n#version 420\n\nlayout(binding = 0) uniform sampler2D tex;/**< Input texture */\nlayout(location= 0) out vec4 out_color;   /**< Output texture map */\n\nuniform vec4 in_color;                    /**< Uniform color */\nin vec4 texcoord;                         /**< Texture coords at current pixel */\n\nvoid main(void) {\n  vec4 c1 = texture(tex,texcoord.xy);\n  vec4 c2 = in_color;\n  out_color = c2.a*in_color + clamp(1.0-c2.a,0.0,1.0)*c1;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/topview.vp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/** \\file topview.vp\n *\n * Vertex shader with projection and modelview transformations\n * for rendering top view\n */\n\n#version 420\n\nlayout(location = 0) in vec4 in_vertex;   /**< Input vertex coordinates */\nlayout(location = 1) in vec4 in_texcoord; /**< Input texture coordinates */\n\nuniform mat4 proj;                        /**< Projection matrix */\nout vec4 texcoord;                        /**< Output texture coordinates */\n\nvoid main(void) {\n  gl_Position = proj * in_vertex;\n  texcoord    = in_texcoord;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/shaders/uv_mesh.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 2) in vec2 in_uv;\n\nout vec2 out_uv;\n\nuniform mat4 mvp;\n\nvoid main() {\n    out_uv = in_uv;\n    gl_Position = mvp * vec4(in_vertex, 1.0);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/core/view/sibr_view.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*! \n\t\\defgroup sibr_view sibr_view\n\n\t\\brief View, camera, high-level rendering utilities. \n\t\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(sibr_basic_all)\n\nadd_subdirectory(apps)\nadd_subdirectory(renderer)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/basic\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/apps/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_basic_apps)\n\nadd_subdirectory(texturedMesh/)\nadd_subdirectory(pointBased/)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/apps/pointBased/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_PointBased_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tsibr_view\n\tsibr_assets\n\tsibr_basic\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/basic/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"basic\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/apps/pointBased/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <projects/basic/renderer/PointBasedView.hpp>\n#include <core/scene/BasicIBRScene.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n\n#define PROGRAM_NAME \"sibr_PointBased_app\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\t\"Usage: \" PROGRAM_NAME \" -path <dataset-path or mesh-path>\"    \t                                \"\\n\"\n\t;\n\n\nstruct PointBasedAppArgs :\n\tvirtual BasicIBRAppArgs {\n\tArg<std::string> meshPath = { \"mesh\", \"\", \"mesh path\" };\n};\n\n\nint main( int ac, char** av )\n{\n\t{\n\t\t// Parse Commad-line Args\n\t\tCommandLineArgs::parseMainArgs(ac, av);\n\t\tPointBasedAppArgs myArgs;\n\n\t\tconst bool doVSync = !myArgs.vsync;\n\t\t// rendering size\n\t\tuint rendering_width = myArgs.rendering_size.get()[0];\n\t\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\t// window size\n\t\tuint win_width = myArgs.win_width;\n\t\tuint win_height = myArgs.win_height;\n\n\t\t// Window setup\n\t\tsibr::Window        window(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/ulr/\" + PROGRAM_NAME + \".ini\");\n\n\t\t// Setup IBR\n\t\tBasicIBRScene::Ptr\t\tscene;\n\t\tstd::string meshPath;\n\n\t\t// Specify scene initlaization options\n\t\tscene = BasicIBRScene::Ptr(new BasicIBRScene(myArgs));\n\n\t\t// Load the texture image and provide it to the scene\n\t\tsibr::ImageRGB inputTextureImg;\n\n\t\t// check rendering size; if no rendering-size specified, use 1080p\n\t\trendering_width = (rendering_width <= 0) ? 1920 : rendering_width;\n\t\trendering_height = (rendering_height <= 0) ? 1080 : rendering_height;\n\t\tVector2u usedResolution(rendering_width, rendering_height);\n\n\t\tconst unsigned int sceneResWidth = usedResolution.x();\n\t\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\t\tPointBasedView::Ptr\tpointbasedView(new PointBasedView(scene, sceneResWidth, sceneResHeight));\n\n\t\t// Raycaster.\n\t\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\t\traycaster->init();\n\t\traycaster->addMesh(scene->proxies()->proxy());\n\n\t\t// Camera handler for main view.\n\t\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\t\tif( scene->cameras()->inputCameras().size() == 0 ) \n\t\t\tgeneralCamera->setup(scene->proxies()->proxyPtr(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()));\n\t\telse\n\t\t\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster);\n\n\t\t// Add views to mvm.\n\t\tMultiViewManager        multiViewManager(window, false);\n\t\tmultiViewManager.addIBRSubView(\"Point-Based View\", pointbasedView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\t\tmultiViewManager.addCameraForView(\"Point-Based View\", generalCamera);\n\n\t\t// Top view\n\t\tconst std::shared_ptr<sibr::SceneDebugView>    topView(new sibr::SceneDebugView(scene, multiViewManager.getViewport(), generalCamera, myArgs));\n\t\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\n\t\tif (myArgs.pathFile.get() !=  \"\" ) {\n\t\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"TM view\"), \"pointbasedmesh\");\n\t\t\tif( !myArgs.noExit )\n\t\t\t\texit(0);\n\t\t}\n\n\t\twhile (window.isOpened())\n\t\t{\n\t\t\tsibr::Input::poll();\n\t\t\twindow.makeContextCurrent();\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape))\n\t\t\t\twindow.close();\n\n\t\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\t\tmultiViewManager.onRender(window);\n\t\t\twindow.swapBuffer();\n\t\t\tCHECK_GL_ERROR\n\t\t}\n\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/apps/texturedMesh/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_texturedMesh_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tsibr_view\n\tsibr_assets\n\tsibr_basic\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/basic/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"basic\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/apps/texturedMesh/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <projects/basic/renderer/TexturedMeshView.hpp>\n#include <core/scene/BasicIBRScene.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n\n#define PROGRAM_NAME \"sibr_texturedMesh_app\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\t\"Usage: \" PROGRAM_NAME \" -path <dataset-path or mesh-path>\"    \t                                \"\\n\"\n\t;\n\n\nstruct TexturedMeshAppArgs :\n\tvirtual BasicIBRAppArgs {\n\tArg<std::string> textureImagePath = { \"texture\", \"\" ,\"texture path\"};\n\tArg<std::string> meshPath = { \"mesh\", \"\", \"mesh path\" };\n\tArg<bool> noScene = { \"noScene\" };\n};\n\n// order textured, mesh {obj} then mesh.ply\nstd::string findCaprealMesh(const std::string& caprealDir)\n{\n\tstd::string texImgPath, meshPath, m1, m2, m3;\n\tm1 = meshPath = caprealDir + \"/textured.obj\";\n\tif (!fileExists(meshPath)) \n\t\tm2 = meshPath = caprealDir + \"/mesh.obj\";\n\tif (!fileExists(meshPath)) \n\t\tm3 = meshPath = caprealDir + \"/mesh.ply\";\n\t\n\tif (!fileExists(meshPath))\n\t\tSIBR_ERR << \"Can't find mesh, tried: \" << m1 << \":\" << m2 << \":\" << m3 << std::endl;\n\n\treturn meshPath;\n}\n\n// order texture, texture_u1_v1, textured, textured_u1_v1, mesh_u1_v1\n\nstd::string findCaprealTexture(const std::string& caprealDir)\n{\n\tstd::string texImgPath, t1, t2, t3, t4;\n\tt1 = texImgPath = caprealDir + \"/texture.png\";\n\tif (!fileExists(texImgPath))\n\t\tt2 = texImgPath = caprealDir + \"/texture_u1_v1.png\";\n\tif (!fileExists(texImgPath)) \n\t\tt3 = texImgPath = caprealDir + \"/mesh.png\";\n\tif (!fileExists(texImgPath)) \n\t\tt4 = texImgPath = caprealDir + \"/mesh_u1_v1.png\";\n\tif (!fileExists(texImgPath))\n\t\tSIBR_ERR << \"Cant find texture, tried \" << t1 << \":\" << t2 << \":\" << t3 << \":\" << t4 << std::endl;\n\treturn texImgPath;\n}\n\n\nint main( int ac, char** av )\n{\n\t{\n\n\t\t// Parse Commad-line Args\n\t\tCommandLineArgs::parseMainArgs(ac, av);\n\t\tTexturedMeshAppArgs myArgs;\n\n\t\tconst bool doVSync = !myArgs.vsync;\n\t\t// rendering size\n\t\tuint rendering_width = myArgs.rendering_size.get()[0];\n\t\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\t// window size\n\t\tuint win_width = myArgs.win_width;\n\t\tuint win_height = myArgs.win_height;\n\n\t\t// Window setup\n\t\tsibr::Window        window(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/ulr/\" + PROGRAM_NAME + \".ini\");\n\n\t\t// Setup IBR\n\t\tBasicIBRScene::Ptr\t\tscene;\n\t\tstd::string texImgPath, meshPath, m1, m2, m3, t1, t2;\n\n\t\tif (myArgs.noScene) {\n\t\t\tscene = BasicIBRScene::Ptr(new BasicIBRScene());\n\n\t\t\tif (myArgs.textureImagePath.get() != \"\") {\n\t\t\t\tmeshPath = myArgs.dataset_path.get();\n\t\t\t\ttexImgPath = myArgs.textureImagePath;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Specify scene initlaization options\n\n/*\n\t\t\tBasicIBRScene::SceneOptions initOpts;\n\t\t\tinitOpts.cameras = false;\n\t\t\tinitOpts.images = false;\n\t\t\tinitOpts.mesh = false;\n\t\t\tinitOpts.renderTargets = false;\n*/\n\n\t\t\tscene = BasicIBRScene::Ptr(new BasicIBRScene(myArgs));\n\n\t\t\t// Cleanup; move the Scene ?\n\t\t\tstd::cerr << \"Reading \" << myArgs.dataset_path.get() + \"/capreal\" << std::endl;\n\t\t\tstd::string caprealDir = myArgs.dataset_path.get() + \"/capreal\";\n\t\t\tif (!directoryExists(caprealDir))\n\t\t\t\tcaprealDir = parentDirectory(myArgs.dataset_path.get()) + \"/capreal\";\n\n\t\t\tif (directoryExists(caprealDir)) {\n\t\t\t\tmeshPath = findCaprealMesh(caprealDir);\n\t\t\t\ttexImgPath = findCaprealTexture(caprealDir);\n\t\t\t}\n\t\t}\n\n\t\t// Load the texture image and provide it to the scene\n\t\tsibr::ImageRGB inputTextureImg;\n\t\tif (sibr::fileExists(texImgPath)) {\n\t\t\tinputTextureImg.load(texImgPath);\n\t\t\tscene->inputMeshTextures().reset(new sibr::Texture2DRGB(inputTextureImg, SIBR_GPU_LINEAR_SAMPLING));\n\t\t}\n\t\t/* HACK GD\n\t\telse {\n\t\t\tSIBR_ERR << \"No mesh and texture found! Please specify path to mesh using --path and path to the mesh texture using --texture!\" << std::endl;\n\t\t\treturn 0;\n\t\t}\n\t\t*/\n\n\t\tif (myArgs.noScene) {\n\t\t\tMesh::Ptr newMesh(new Mesh(true));\n\t\t\tnewMesh->load(meshPath);\n\t\t\tscene->proxies()->replaceProxyPtr(newMesh);\n\t\t}\n\n\t\t// check rendering size; if no rendering-size specified, use 1080p\n\t\trendering_width = (rendering_width <= 0) ? 1920 : rendering_width;\n\t\trendering_height = (rendering_height <= 0) ? 1080 : rendering_height;\n\t\tVector2u usedResolution(rendering_width, rendering_height);\n\n\t\tconst unsigned int sceneResWidth = usedResolution.x();\n\t\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\n\t\tTexturedMeshView::Ptr\ttexturedView(new TexturedMeshView(scene, sceneResWidth, sceneResHeight));\n\n\n\t\t// Raycaster.\n\t\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\t\traycaster->init();\n\t\traycaster->addMesh(scene->proxies()->proxy());\n\n\t\t// Camera handler for main view.\n\t\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\t\tif( scene->cameras()->inputCameras().size() == 0 ) \n\t\t\tgeneralCamera->setup(scene->proxies()->proxyPtr(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()));\n\t\telse\n\t\t\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster);\n\n\t\t// Add views to mvm.\n\t\tMultiViewManager        multiViewManager(window, false);\n\t\tmultiViewManager.addIBRSubView(\"TM View\", texturedView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\t\tmultiViewManager.addCameraForView(\"TM View\", generalCamera);\n\n\t\t// Top view\n\t\tconst std::shared_ptr<sibr::SceneDebugView>    topView(new sibr::SceneDebugView(scene, multiViewManager.getViewport(), generalCamera, myArgs));\n\t\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\n\t\tif (myArgs.pathFile.get() !=  \"\" ) {\n\t\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"TM view\"), \"texturedmesh\");\n\t\t\tif( !myArgs.noExit )\n\t\t\t\texit(0);\n\t\t}\n\n\t\twhile (window.isOpened())\n\t\t{\n\t\t\tsibr::Input::poll();\n\t\t\twindow.makeContextCurrent();\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape))\n\t\t\t\twindow.close();\n\n\t\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\t\tmultiViewManager.onRender(window);\n\t\t\twindow.swapBuffer();\n\t\t\tCHECK_GL_ERROR\n\t\t}\n\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/apps/texturedMesh/resources/sibr_texturedMesh_app.ini",
    "content": "\n[Window][Textured Mesh Renderer Settings]\nPos=50,50\nSize=350,300\nCollapsed=0\n\n[Window][Camera TM View]\nPos=400,50\nSize=550,300\nCollapsed=0\n\n[Window][Top view settings]\nPos=950,50\nSize=450,300\nCollapsed=0\n\n[Window][Metrics##0]\nPos=1400,50\nSize=450,300\nCollapsed=0\n\n[Window][TM View]\nPos=50,350\nSize=900,600\nCollapsed=0\n\n[Window][Top view]\nPos=950,350\nSize=900,600\nCollapsed=0\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/renderer/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nset(SIBR_PROJECT \"basic\")\nproject(sibr_${SIBR_PROJECT})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\n\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS} .)\nif (WIN32)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n)\nelse()\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n)\nendif()\n\nadd_definitions( -DSIBR_EXP_ULR_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/renderer\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS ${SHADERS}\n\tRSC_FOLDER ${SIBR_PROJECT}\n\n    #STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/renderer/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n# include <core/system/CommandLineArgs.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_EXP_ULR_EXPORT\n#      ifdef SIBR_EXP_ULR_EXPORTS\n/* We are building this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllexport)\n#      else\n/* We are using this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_EXP_ULR_EXPORT\n# endif\n\nnamespace sibr {\n\n\t/// Arguments for all ULR applications.\n\tstruct ULRAppArgs :\n\t\tvirtual BasicIBRAppArgs {\n\t\tArg<int> version = { \"v\", 3, \"ULR implementation version\" };\n\t\tArgSwitch softVisibility = { \"soft-visibility\", false, \"generate and use soft visibility masks\" };\n\t\tArg<bool> masks = { \"masks\" , \"use binary masks\" };\n\t\tArg<std::string> maskParams = { \"masks-param\" , \"\" };\n\t\tArg<std::string> maskParamsExtra = { \"masks-param-extra\" , \"\" };\n\t\tArg<bool> invert = { \"invert\", \"invert the masks\" };\n\t\tArg<bool> alphas = { \"alphas\", \"\" };\n\t\tArg<bool> poisson = { \"poisson-blend\", \"apply Poisson-filling to the ULR result\" };\n\t};\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/renderer/PointBasedView.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <projects/basic/renderer/PointBasedView.hpp>\n#include <core/graphics/GUI.hpp>\n\nsibr::PointBasedView::PointBasedView(const sibr::BasicIBRScene::Ptr & ibrScene, uint render_w, uint render_h) :\n\t_scene(ibrScene),\n\tsibr::ViewBase(render_w, render_h)\n{\n\tconst uint w = render_w;\n\tconst uint h = render_h;\n\n\t//  Renderers.\n\t_pointBasedRenderer.reset(new PointBasedRenderer());\n}\n\nvoid sibr::PointBasedView::setScene(const sibr::BasicIBRScene::Ptr & newScene) {\n\t_scene = newScene;\n\tconst uint w = getResolution().x();\n\tconst uint h = getResolution().y();\n\n\t_pointBasedRenderer.reset(new PointBasedRenderer());\n}\n\nvoid sibr::PointBasedView::onRenderIBR(sibr::IRenderTarget & dst, const sibr::Camera & eye)\n{\n\t// Perform ULR rendering, either directly to the destination RT, or to the intermediate RT when poisson blending is enabled.\n\tglViewport(0, 0, dst.w(), dst.h());\n\tdst.clear();\n\t_pointBasedRenderer->process( _scene->proxies()->proxy(), eye, dst, false);\n}\n\nvoid sibr::PointBasedView::onUpdate(Input & input)\n{\n}\n\nvoid sibr::PointBasedView::onGUI()\n{\n\tif (ImGui::Begin(\"Point Based Mesh Renderer Settings\")) {\n\n\t\t// Poisson settings.\n\t\t//ImGui::Checkbox(\"Poisson fix\", &_poissonRenderer->enableFix());\n\n\t}\n\tImGui::End();\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/renderer/PointBasedView.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/system/Config.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/view/ViewBase.hpp>\n# include <core/renderer/CopyRenderer.hpp>\n# include <core/renderer/PointBasedRenderer.hpp>\n# include <core/scene/BasicIBRScene.hpp>\n# include <core/renderer/PoissonRenderer.hpp>\n\nnamespace sibr { \n\n\t/**\n\t * \\class PointBasedView\n\t * \\brief Wrap a Textured Mesh renderer with additional parameters and information.\n\t */\n\tclass SIBR_EXP_ULR_EXPORT PointBasedView : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(PointBasedView);\n\n\tpublic:\n\n\t\t/**\n\t\t * Constructor\n\t\t * \\param ibrScene The scene to use for rendering.\n\t\t * \\param render_w rendering width\n\t\t * \\param render_h rendering height\n\t\t */\n\t\tPointBasedView(const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h);\n\n\t\t/** Replace the current scene.\n\t\t *\\param newScene the new scene\n\t\t **/\n\t\tvoid setScene(const sibr::BasicIBRScene::Ptr & newScene);\n\n\t\t/**\n\t\t * Perform rendering. Called by the view manager or rendering mode.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param eye The novel viewpoint.\n\t\t */\n\t\tvoid onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye) override;\n\n\t\t/**\n\t\t * Update inputs (do nothing).\n\t\t * \\param input The input state.\n\t\t */\n\t\tvoid onUpdate(Input& input) override;\n\n\t\t/**\n\t\t * Update the GUI.\n\t\t */\n\t\tvoid onGUI() override;\n\n\t\t/// \\return a reference to the renderer.\n\t\tconst PointBasedRenderer::Ptr & getPointBasedRenderer() const { return _pointBasedRenderer; }\n\n\t\t/// \\return a reference to the scene\n\t\tconst BasicIBRScene::Ptr & getScene() const { return _scene; }\n\n\tprotected:\n\n\t\t\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene;\n\t\tPointBasedRenderer::Ptr\t\t\t _pointBasedRenderer;\n\t\t\t\t\t\t\t\t\t\t\t \n\t\t\t\t\t\t\t\t\t\t\t \n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/renderer/TexturedMeshView.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <projects/basic/renderer/TexturedMeshView.hpp>\n#include <core/graphics/GUI.hpp>\n\nsibr::TexturedMeshView::TexturedMeshView(const sibr::BasicIBRScene::Ptr & ibrScene, uint render_w, uint render_h) :\n\t_scene(ibrScene),\n\tsibr::ViewBase(render_w, render_h)\n{\n\tconst uint w = render_w;\n\tconst uint h = render_h;\n\n\t//  Renderers.\n\t_textureRenderer.reset(new TexturedMeshRenderer());\n\t_poissonRenderer.reset(new PoissonRenderer(w, h));\n\t_poissonRenderer->enableFix() = true;\n\n\t// Rendertargets.\n\t_poissonRT.reset(new RenderTargetRGBA(w, h, SIBR_CLAMP_UVS));\n\t_blendRT.reset(new RenderTargetRGBA(w, h, SIBR_CLAMP_UVS));\n}\n\nvoid sibr::TexturedMeshView::setScene(const sibr::BasicIBRScene::Ptr & newScene) {\n\t_scene = newScene;\n\tconst uint w = getResolution().x();\n\tconst uint h = getResolution().y();\n\n\t_textureRenderer.reset(new TexturedMeshRenderer());\n}\n\nvoid sibr::TexturedMeshView::onRenderIBR(sibr::IRenderTarget & dst, const sibr::Camera & eye)\n{\n\t// Perform ULR rendering, either directly to the destination RT, or to the intermediate RT when poisson blending is enabled.\n\tglViewport(0, 0, dst.w(), dst.h());\n\tdst.clear();\n\t_textureRenderer->process(\n\t\t\t_scene->proxies()->proxy(),\n\t\t\teye, _scene->inputMeshTextures()->handle(), \n\t\t_poissonBlend ? *_blendRT : dst, false);\n\n\t// Perform Poisson blending if enabled and copy to the destination RT.\n\tif (_poissonBlend) {\n\t\t_poissonRenderer->process(_blendRT, _poissonRT);\n\t\tblit(*_poissonRT, dst);\n\t}\n\n}\n\nvoid sibr::TexturedMeshView::onUpdate(Input & input)\n{\n}\n\nvoid sibr::TexturedMeshView::onGUI()\n{\n\tif (ImGui::Begin(\"Textured Mesh Renderer Settings\")) {\n\n\t\t// Poisson settings.\n\t\tImGui::Checkbox(\"Poisson \", &_poissonBlend); ImGui::SameLine();\n\t\tImGui::Checkbox(\"Poisson fix\", &_poissonRenderer->enableFix());\n\n\t}\n\tImGui::End();\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/basic/renderer/TexturedMeshView.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/system/Config.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/view/ViewBase.hpp>\n# include <core/renderer/CopyRenderer.hpp>\n# include <core/renderer/TexturedMeshRenderer.hpp>\n# include <core/scene/BasicIBRScene.hpp>\n# include <core/renderer/PoissonRenderer.hpp>\n\nnamespace sibr { \n\n\t/**\n\t * \\class TexturedMeshView\n\t * \\brief Wrap a Textured Mesh renderer with additional parameters and information.\n\t */\n\tclass SIBR_EXP_ULR_EXPORT TexturedMeshView : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(TexturedMeshView);\n\n\n\tpublic:\n\n\t\t/**\n\t\t * Constructor\n\t\t * \\param ibrScene The scene to use for rendering.\n\t\t * \\param render_w rendering width\n\t\t * \\param render_h rendering height\n\t\t */\n\t\tTexturedMeshView(const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h);\n\n\t\t/** Replace the current scene.\n\t\t *\\param newScene the new scene\n\t\t **/\n\t\tvoid setScene(const sibr::BasicIBRScene::Ptr & newScene);\n\n\t\t/**\n\t\t * Perform rendering. Called by the view manager or rendering mode.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param eye The novel viewpoint.\n\t\t */\n\t\tvoid onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye) override;\n\n\t\t/**\n\t\t * Update inputs (do nothing).\n\t\t * \\param input The input state.\n\t\t */\n\t\tvoid onUpdate(Input& input) override;\n\n\t\t/**\n\t\t * Update the GUI.\n\t\t */\n\t\tvoid onGUI() override;\n\n\t\t/// \\return a reference to the renderer.\n\t\tconst TexturedMeshRenderer::Ptr & getTexturedRenderer() const { return _textureRenderer; }\n\n\t\t/// \\return a reference to the scene\n\t\tconst BasicIBRScene::Ptr & getScene() const { return _scene; }\n\n\tprotected:\n\n\t\t\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene;\n\t\tTexturedMeshRenderer::Ptr\t\t\t _textureRenderer;\n\t\tPoissonRenderer::Ptr\t\t\t\t _poissonRenderer;\n\t\t\t\t\t\t\t\t\t\t\t \n\t\tRenderTargetRGBA::Ptr\t\t\t\t _blendRT;\n\t\tRenderTargetRGBA::Ptr\t\t\t\t _poissonRT;\n\t\t\t\t\t\t\t\t\t\t\t \n\t\tbool\t\t\t\t\t\t\t\t _poissonBlend = false;\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(sibr_dataset_tools_all)\n\nadd_subdirectory(preprocess)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/dataset_tools\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/documentation/dataset_tools.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page sibr_projects_dataset_tools Dataset Preprocessing Tools\n\nThis page contains the documentation for various tools developed for treating multi-view datasets used for image-based rendering. These deal with calibrated cameras (typically with Structure-from-Motion / SfM), 3D meshes reconstructed with SfM and Multi-View Stereo (MVS) and various other utilities.\n\nFor information on datasets, see the @ref howto_generate_dataset.\n\nWe next present a set preprocessing tools used in the various toolchains to prepare data for IBR *Projects*.\n\n\\subsection sibr_projects_dataset_tools_preprocess_tools Preprocessing tools\n\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_cameraConverter cameraConverter\n\nUtility to convert between camera path formats (blender: .lookAt, bundler: .out, colmap: .txt, internal binary format: .bin,...). This is useful for comparisons (see the \\ref comparisonsPage)\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_clippingPlanes clippingPlanes\n\nCalculates near and far planes for each image and writes to file clipping_planes.txt. This is used while creating the dataset. In some cases (e.g., [Chaurasia 13] and [Ortiz-Cayon 15] we need to have the same clipping planes for all images).\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_converters converters\n\nConverters include python scripts to generate various files. In *install/scripts*, run\n```\npython generate_list_images.py --imagesPath IMAGESPATH\n\n[--outputPath OUTPUTPATH ]\n[--filename FILENAME ]\n```\nThat generates \"list_images.txt\" file in a directory IMAGESPATH containing images, optional arguments are the outputpath and filename.\n\n```\nibr_preprocess_rc_to_sibr.py\n```\nSee \\ref howto_generate_dataset \n\n```\nsimplify_mesh.py\n```\nUsed in *fullcolmapProcess* (see below), and uses *meshlabServer* to simplify a mesh.\n\n```\nwedge_to_vertices_uvs.py\n```\nconverts a mesh from wedge uvs to vertex uvs, again using *meshlabServer*.\n\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_cropFromCenter cropFromCenter\n\nUtility to crop images so they are centered and have the same size. Used for preprocessing in [Chaurasia 13] and [Ortiz-Cayon 15].\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_distordCrop distordCrop\n\nUndistort images and then send to *cropFromCenter* above.\n\n\\subpage sibr_projects_dataset_tools_preprocess_tools_fullColmapProcess fullColmapProcess: from images to a colmap dataset\n\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_nvmToSIBR nvmToSIBR\n\nConvert from VisualSFM .nvm format for calibrated cameras to SIBR format\n\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_unwrapMesh unwrapMesh\n\n```\nunwrapMesh_rwdi.exe or\nunwrapMesh.exe\n        --appPath      define a custom app path (default: \"./\")\n        --help         display this help message (default: disabled)\n        --output       path to the output mesh (default: \"\")\n        --path         path to the mesh [required]\n        --size         target UV map width (approx.) (default: 4096)\n        --texture-name name of the texture to reference in the output mesh (Meshlab compatible) (default: \"TEXTURE_NAME_TO_PUT_IN_THE_FILE\")\n        --visu         save visualisation (default: disabled)\n```\n\nCalls xatlas to compute UV coordinates of a mesh (not adapted to complex meshes, works but really long); typical use involves calling simplify mesh first.\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_textureMesh textureMesh\n\n```\ntextureMesh_rwdi.exe or\ntextureMesh.exe\n\t\t--path PATH_TO_DATASET [required]\n\t\t--output PATH_TO_OUTPUT_FILE.png [required]\n\t    --size [default=8192]\n\t\t--flood \n\t\t--poisson\n```\n\nGiven a mesh with UV coordinates (typically using unwrapMesh) and calibrated cameras, produces a texture atlas, with optional arguments for texture resolution, flood or poisson filling.\n\n\\subsection Deprecated\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_tonemapper tonemapper\n\n\\subsubsection sibr_projects_dataset_tools_preprocess_tools_meshroomPythonScripts meshroomPythonScripts\nUtilities for Meshroom use (untested)\n\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/documentation/dataset_tools_doc.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nset(PROJECT_PAGE \"sibr_projects_dataset_tools\")\nset(PROJECT_LINK \"https://gitlab.inria.fr/sibr/sibr_core\")\nset(PROJECT_TYPE \"SAMPLES\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/documentation/fullColmapProcess.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page sibr_projects_dataset_tools_preprocess_tools_fullColmapProcess fullColmapProcess : running the full Colmap pipeline for SIBR\n\n\\section ColmapInstallRequirements Install requirements\n\n- Colmap 3.6: https://demuc.de/colmap/\n\nYou can choose the Pre-Release of Release Version for Windows. The .bat file corresponds\nto the application. Download and install it in any folder.\n\n- Dataset tools projects (available in sibr core)\n\nInstall the SIBR Core on your computer : https://gitlab.inria.fr/sibr/sibr_core\n\nThe page contains all the steps to install it.\n\nChoose the BUILD_IBR_DATASET_TOOLS option in CMAKE, BUILD and INSTALL the dataset_tools project (Apps and Preprocess).\n\n\n\\subsection ColmapHToPrepareDataset How to prepare the dataset\n- Create the directory which will contain the future dataset\n- In the dataset directory, create a new directory with the name images\n- In the images directory, place your images that you want to use to create \nthe SIBR dataset\n\nYour dataset architecture should correspond to this:\n\n\\verbatim\n dataset/                              # your dataset directory\n         images/                       # your images directory\n                 im001.jpeg\n                 im002.jpeg\n                 im003.jpeg\n                 im004.jpeg\n                 ...\n\\endverbatim\n\n\n\\subsection ColmapHToRunPipeline How to run the pipeline \n\nThis section shows the different steps that you need to run to create the dataset.\nThe SIBR ULR project contains some applications which allows to run Colmap, generate UVs\nand create a textured mesh. All the steps can be directly done through the \nfullColmapProcess python script.\n\nYou can run the script as an executable in bash-like command, or call it through python on cmd :\n\n\\code\n# from cmd\nλ python .\\install\\scripts\\fullColmapProcess.py --help\n# from git bash / cygwin / msys2\n❯ ./install/scripts/fullColmapProcess.py --help\n\nusage: fullColmapProcess.py [-h] --path PATH --colmapPath COLMAPPATH [--sibrBinariesPath SIBRBINARIESPATH] [--quality {default,low,medium,average,high,extreme}] [--with_texture] [--numGPUs NUMGPUS]\n                            [--SiftExtraction.max_image_size SIFTEXTRACTION_IMAGESIZE] [--SiftExtraction.estimate_affine_shape SIFTEXTRACTION_ESTIMATEAFFINESHAPE] [--SiftExtraction.domain_size_pooling SIFTEXTRACTION_DOMAINSIZEPOOLING]\n                            [--SiftExtraction.max_num_features SIFTEXTRACTION_MAXNUMFEATURES] [--ImageReader.single_camera IMAGEREADER_SINGLECAMERA] [--ExhaustiveMatching.block_size EXHAUSTIVEMATCHER_EXHAUSTIVEMATCHINGBLOCKSIZE]\n                            [--Mapper.ba_local_max_num_iterations MAPPER_MAPPERDOTBALOCALMAXNUMITERATIONS] [--Mapper.ba_global_max_num_iterations MAPPER_MAPPERDOTBAGLOBALMAXNUMITERATIONS]\n                            [--Mapper.ba_global_images_ratio MAPPER_MAPPERDOTBAGLOBALIMAGESRATIO] [--Mapper.ba_global_points_ratio MAPPER_MAPPERDOTBAGLOBALPOINTSRATIO]\n                            [--Mapper.ba_global_max_refinements MAPPER_MAPPERDOTBAGLOBALMAXREFINEMENTS] [--Mapper.ba_local_max_refinements MAPPER_MAPPERDOTBALOCALMAXREFINEMENTS]\n                            [--PatchMatchStereo.max_image_size PATCHMATCHSTEREO_PATCHMATCHSTEREODOTMAXIMAGESIZE] [--PatchMatchStereo.window_radius PATCHMATCHSTEREO_PATCHMATCHSTEREODOTWINDOWRADIUS]\n                            [--PatchMatchStereo.window_step PATCHMATCHSTEREO_PATCHMATCHSTEREODOTWINDOWSTEP] [--PatchMatchStereo.num_samples PATCHMATCHSTEREO_PATCHMATCHSTEREODOTNUMSAMPLES]\n                            [--PatchMatchStereo.num_iterations PATCHMATCHSTEREO_PATCHMATCHSTEREODOTNUMITERATIONS] [--PatchMatchStereo.geom_consistency PATCHMATCHSTEREO_PATCHMATCHSTEREODOTGEOMCONSISTENCY]\n                            [--StereoFusion.check_num_images STEREOFUSION_CHECKNUMIMAGES] [--StereoFusion.max_image_size STEREOFUSION_MAXIMAGESIZE]\n\noptional arguments:\n  -h, --help            show this help message and exit\n  --path PATH           path to your dataset folder\n  --colmapPath COLMAPPATH\n                        colmap path directory which contains colmap.bat / colmap.bin\n  --sibrBinariesPath SIBRBINARIESPATH\n                        binaries directory of SIBR\n  --quality {default,low,medium,average,high,extreme}\n                        quality of the reconstruction\n  --with_texture        Add texture steps\n  --numGPUs NUMGPUS     number of GPUs allocated to Colmap\n  --SiftExtraction.max_image_size SIFTEXTRACTION_IMAGESIZE\n  --SiftExtraction.estimate_affine_shape SIFTEXTRACTION_ESTIMATEAFFINESHAPE\n  --SiftExtraction.domain_size_pooling SIFTEXTRACTION_DOMAINSIZEPOOLING\n  --SiftExtraction.max_num_features SIFTEXTRACTION_MAXNUMFEATURES\n  --ImageReader.single_camera IMAGEREADER_SINGLECAMERA\n  --ExhaustiveMatching.block_size EXHAUSTIVEMATCHER_EXHAUSTIVEMATCHINGBLOCKSIZE\n  --Mapper.ba_local_max_num_iterations MAPPER_MAPPERDOTBALOCALMAXNUMITERATIONS\n  --Mapper.ba_global_max_num_iterations MAPPER_MAPPERDOTBAGLOBALMAXNUMITERATIONS\n  --Mapper.ba_global_images_ratio MAPPER_MAPPERDOTBAGLOBALIMAGESRATIO\n  --Mapper.ba_global_points_ratio MAPPER_MAPPERDOTBAGLOBALPOINTSRATIO\n  --Mapper.ba_global_max_refinements MAPPER_MAPPERDOTBAGLOBALMAXREFINEMENTS\n  --Mapper.ba_local_max_refinements MAPPER_MAPPERDOTBALOCALMAXREFINEMENTS\n  --PatchMatchStereo.max_image_size PATCHMATCHSTEREO_PATCHMATCHSTEREODOTMAXIMAGESIZE\n  --PatchMatchStereo.window_radius PATCHMATCHSTEREO_PATCHMATCHSTEREODOTWINDOWRADIUS\n  --PatchMatchStereo.window_step PATCHMATCHSTEREO_PATCHMATCHSTEREODOTWINDOWSTEP\n  --PatchMatchStereo.num_samples PATCHMATCHSTEREO_PATCHMATCHSTEREODOTNUMSAMPLES\n  --PatchMatchStereo.num_iterations PATCHMATCHSTEREO_PATCHMATCHSTEREODOTNUMITERATIONS\n  --PatchMatchStereo.geom_consistency PATCHMATCHSTEREO_PATCHMATCHSTEREODOTGEOMCONSISTENCY\n  --StereoFusion.check_num_images STEREOFUSION_CHECKNUMIMAGES\n  --StereoFusion.max_image_size STEREOFUSION_MAXIMAGESIZE\n\\endcode\n\n\\image HTML colmapfullpipeline.png\n\n- Colmap creates a reconstruction from your images\n- UnwrapMesh program gens UV coordinates on the mesh\n- colmapToSibr creates the architecture and files required by a SIBR scene\n- TextureMesh create a texture and bind it to the reconstruction.\n\n\\subsubsection ColmapInputArgs Input arguments\n\nRequired arguments:\n\n\\code\n--path YOUR_DATA_PATH\n\\endcode\n\nThe path to your dataset folder. It must contain an images folder with images of your captured scene\n\nOptional arguments:\n\n\\code\n--colmapPath COLMAP_DIR\n\\endcode\n\nThe directory containing the colmap.bat executable (if not provided, it will look for a `COLMAP_PATH` environment variable, or use `C:\\Program Files\\Colmap`)\n\n\\code\n--meshlabPath MESHLAB_DIR\n\\endcode\n\nThe directory containing the meshlabserver executable (if not provided, it will look for a `MESHLAB_PATH` environment variable, or use `C:\\Program Files\\VCG\\Meshlab`)\n\n\\code\n--sibrBinariesPath YOUR_SIBR_DIR\\install\\bin\n\\endcode\n\nThat is the directory which contains the binaries of SIBR\n\nThose optional arguments are about the Colmap parametrization. You have several ways to\nset the colmap parameters\n\n- Use a pre-defined configuration. You have 4 configurations : low, medium, high, extreme.\nIf you don't use a pre-defined configuration, all the parameters are set to the default\nvalue ( usually it is a mix between high and extreme ). To apply it, use the \n\\code\n--quality\n\\endcode option\n\n- Specify the parameters separately. You can set each parameters by yourself. Here is \na tab contains the default values and the values for each pre-defined configuration:\n\n | parameters                                           | default       | low   | medium        | average       | high          | extreme       |\n | ---------------------------------------------------- | ------------- | ----- | ------------- | ------------- | ------------- | ------------- |\n | **colmap feature_extractor**                         |||||||\n | siftExtraction_ImageSize                             | 3200          | 1000  | 1600          | 3200          | 2400          | 3200          |\n | siftExtraction_EstimateAffineShape                   | false         | false | false         | false         | true          | true          |\n | siftExtraction_DomainSizePooling                     | false         | false | false         | false         | false         | true          |\n | siftExtraction_MaxNumFeatures                        | 16000         | 8192  | 8192          | 8192          | 8192          | 8192          |\n | imageReader_SingleCamera                             | false         | true  | true          | true          | true          | true          |\n | **colmap exhaustive_matcher**                        |||||||\n | exhaustiveMatcher_ExhaustiveMatchingBlockSize        | 50            | 50    | 50            | 50            | 50            | 50            |\n | **colmap mapper**                                    |||||||\n | mapper_MapperDotbaLocalMaxNumIterations              | 25            | 12    | 16            | 25            | 30            | 40            |\n | mapper_MapperDotbaGlobalMaxNumIterations             | 50            | 25    | 33            | 50            | 75            | 100           |\n | mapper_MapperDotbaGlobalImagesRatio                  | 1.100001      | 1.32  | 1.21          | 1.100001      | 1.100001      | 1.100001      |\n | mapper_MapperDotbaGlobalPointsRatio                  | 1.100001      | 1.32  | 1.21          | 1.100001      | 1.100001      | 1.100001      |\n | mapper_MapperDotbaGlobalMaxRefinements               | 5             | 2     | 2             | 5             | 5             | 5             |\n | mapper_MapperDotbaLocalMaxRefinements                | 2             | 2     | 2             | 2             | 3             | 3             |\n | **colmap patch_match_stereo**                        |||||||\n | patchMatchStereo_PatchMatchStereoDotMaxImageSize     | -1            | 1000  | 1600          | -1            | 2400          | -1            |\n | patchMatchStereo_PatchMatchStereoDotWindowRadius     | 5             | 4     | 4             | 5             | 5             | 5             |\n | patchMatchStereo_PatchMatchStereoDotWindowStep       | 1             | 2     | 2             | 1             | 1             | 1             |\n | patchMatchStereo_PatchMatchStereoDotNumSamples       | 15            | 7     | 10            | 15            | 15            | 15            |\n | patchMatchStereo_PatchMatchStereoDotNumIterations    | 5             | 3     | 5             | 5             | 5             | 5             |\n | patchMatchStereo_PatchMatchStereoDotGeomConsistency  | 1             | 0     | 0             | 1             | 1             | 1             |\n | **colmap stereo_fusion**                             |||||||\n | stereoFusion_CheckNumImages                          | 50            | 25    | 33            | 50            | 50            | 50            |\n | stereoFusion_MaxImageSize                            | -1            | 1000  | 1600          | -1            | 2400          | -1            |\n\n- Mix a pre-defined configuration and your own parameters. First, the parameters\nof the pre-defined configuration are applied. Then, your parameters are applied over them.\n\n\n\n\\subsubsection ColmapInputArgsExamples Input arguments examples\n\nThe most basic version looks like that\n\n\n\\code\n--path E:\\USERNAME\\dataset --sibrBinariesPath E:\\USERNAME\\dev\\sibr_basic2\\install\\bin --colmapPath D:\\colmap\n\\endcode\n\n\\note Do not forget that your dataset path has to contain an image directory with the images inside it.\n\nNow an example using the pre-defined configuration\n\n\\code\n--path E:\\USERNAME\\dataset --sibrBinariesPath E:\\YOU\\dev\\sibr_basic2\\install\\bin --colmapPath D:\\colmap --quality low\n\\endcode\n\nFinally, an example with the mix of the two ways\n\n\\code\n--path E:\\USERNAME\\dataset --sibrBinariesPath E:\\YOU\\dev\\sibr_basic2\\install\\bin --colmapPath D:\\colmap --quality medium --SiftExtraction.max_num_features 4096\n\\endcode\n\nAll the parameters will be set to the medium configuration except the max_num_features that\nwill be setted to 4096.\n\n*/"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_dataset_tools_preprocess)\n\nadd_subdirectory(alignMeshes)\nadd_subdirectory(cameraConverter)\nadd_subdirectory(clippingPlanes)\nadd_subdirectory(converters)\nadd_subdirectory(cropFromCenter)\nadd_subdirectory(distordCrop)\nadd_subdirectory(fullColmapProcess)\nadd_subdirectory(meshroomPythonScripts)\nadd_subdirectory(nvmToSIBR)\nadd_subdirectory(textureMesh)\nadd_subdirectory(tonemapper)\nadd_subdirectory(unwrapMesh)\nadd_subdirectory(utils)\nadd_subdirectory(prepareColmap4Sibr)\nadd_subdirectory(realityCaptureTools)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/alignMeshes/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n# project name\nproject(alignMeshes)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n    OpenMP::OpenMP_CXX\n    sibr_assets\n    sibr_system\n    sibr_graphics\n    sibr_renderer\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/alignMeshes/main.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n\r\n#include <fstream>\r\n#include <boost/filesystem.hpp>\r\n#include <core/system/Utils.hpp>\r\n#include <opencv2/features2d/features2d.hpp>\r\n#include <opencv2/flann/flann.hpp>\r\n#include <core/renderer/DepthRenderer.hpp>\r\n#include <core/graphics/Window.hpp>\r\n#include <core/scene/BasicIBRScene.hpp>\r\n#include <core/scene/ParseData.hpp>\r\n\r\n#define PROGRAM_NAME \"sibr_chunk2sibr\"\r\nusing namespace sibr;\r\n\r\nconst char* usage = \"\"\r\n\"Usage: \" PROGRAM_NAME \" -path <reference scene path> -path2 <scene to align path> -outPath <mesh output path>\" \"\\n\"\r\n;\r\n\r\ndouble distPatch(sibr::ImageRGB& im1, sibr::Vector2i& tpos, sibr::ImageRGB& im2, sibr::Vector2i& spos, int size) {\r\n\t//only need to check boundaries for target\r\n\r\n\r\n\tdouble dist = 0;\r\n\tfor (int i = -size; i <= size; i++) {\r\n\t\tfor (int j = -size; j <= size; j++) {\r\n\t\t\tsibr::Vector2i debug(spos.x() + i, spos.y() + j);\r\n\t\t\tif (!im2.isInRange(debug.x(), debug.y()))\r\n\t\t\t\tstd::cout << \"Pos patch is : \" << sibr::Vector2i(spos.x() + i, spos.y() + j) << std::endl;\r\n\t\t\tdist += (im1(tpos.x() + i, tpos.y() + j).cast<double>() - im2(spos.x() + i, spos.y() + j).cast<double>()).squaredNorm();\r\n\t\t}\r\n\t}\r\n\treturn dist;\r\n}\r\n\r\n\r\n// Convienience function to find the Median Absolute Deviation from a vector of deviations.\r\n// Note that it isn't a strict median( just a quick approximation. )\r\nfloat findMAD(const Eigen::VectorXf& vec) {\r\n\tEigen::VectorXf vec_ = vec;\r\n\t// Sort the data in increasing order.\r\n\tstd::sort(vec_.data(), vec_.data() + vec_.size());\r\n\t// Return the 'middle' element.\r\n\treturn vec_[((vec_.size() + 1) / 2)];\r\n}\r\n\r\n// Weight function.( Takes a list of standardized adjusted residuals and returns the square-root-weights for each one )\r\n// Currently, the Bisquares estimator is used.\r\n// Note that this function should return the square root of the actual weight value since both X and Y are multiplied by this vector.\r\nEigen::VectorXf weight(Eigen::VectorXf v) {\r\n\tEigen::VectorXf vout = v;\r\n\r\n\tfor (int i = 0; i < v.size(); i++) {\r\n\t\tfloat r = v[i];\r\n\t\tvout[i] = ((abs(r) < 1) ? (1 - (r * r)) : 0);\r\n\t}\r\n\r\n\treturn vout;\r\n}\r\n\r\n#define MAX_ITERS 100\r\n// Procedure for IRLS( Iterative Reweighted Least Squares ).\r\nvoid irls(Eigen::MatrixX4f mX, Eigen::VectorXf vY, Eigen::Vector4f& mCoeffs, float tune) {\r\n\r\n\tEigen::MatrixXf mX_ = mX;\r\n\tEigen::VectorXf vY_ = vY;\r\n\t// Find the least squares coefficients.\r\n\tEigen::Vector4f vC = mX_.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(vY);\r\n\t//Log(EInfo, \"Finished solving for LS solution.\");\r\n\r\n\t// Form the leverage value matrix as H = X . ( X_T . X ) . X_T\r\n\t// Form the leverage factor matrix as 1/sqrt(1 - diag(H))\r\n\tEigen::VectorXf mH = (mX_ * (((mX_.transpose() * mX_).inverse()) * mX_.transpose())).diagonal();\r\n\tEigen::MatrixXf mH_ = (Eigen::VectorXf::Constant(mH.rows(), 1, 1) - mH).cwiseSqrt().cwiseInverse().asDiagonal();\r\n\r\n\tstd::cout << vC << std::endl;\r\n\tfor (int i = 0; ; i++) {\r\n\t\tstd::cout << \"IRLS: Iteration \" << i << \":\";\r\n\r\n\t\t// Find residuals:\r\n\t\tEigen::VectorXf resid = vY - mX * vC;\r\n\r\n\t\tfloat mad = findMAD(resid.cwiseAbs());\r\n\r\n\t\t// Calcualte Standardized Adjusted Residuals.\r\n\t\tEigen::VectorXf r = (mH_ * resid * 0.6745) / (mad * tune);\r\n\r\n\t\t// Find the root weight of residuals.\r\n\t\tEigen::VectorXf wt = weight(r);\r\n\r\n\t\t// Multiply X and Y with the root of the weights.\r\n\t\tmX_ = wt.asDiagonal() * mX;\r\n\t\tvY_ = wt.asDiagonal() * vY;\r\n\r\n\t\tstd::cout << \"MAD= \" << mad << \", \";\r\n\r\n\t\t// Regress the weighted X and Y to find weighted least squares optimisation.\r\n\t\tEigen::Vector4f vC_ = mX_.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(vY_);\r\n\r\n\t\t// Find mean deviation in coefficients.\r\n\t\tfloat meanDiff = (vC - vC_).cwiseAbs().mean();\r\n\t\tstd::cout << \"MD=\" << meanDiff << \",\";\r\n\t\t// Terminate if the deviation is too small or number of iterations has been exceeded.\r\n\t\tif (meanDiff < 0.01f || i > MAX_ITERS) {\r\n\t\t\tmCoeffs = vC_;\r\n\t\t\tstd::cout << \"\\n\";\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tvC = vC_;\r\n\t\tstd::cout << \"\\n\";\r\n\t}\r\n\tstd::cout << vC << std::endl;\r\n}\r\n\r\nstatic bool isRawRC(std::string pathRC)\r\n{\r\n\t// do we have bundle, mesh and list images ?\r\n\tsibr::Mesh mesh2Align;\r\n\tif (!mesh2Align.load(pathRC + \"/recon.ply\")) {\r\n\t\tSIBR_WRG << \"***** No file \" << pathRC + \"/recon.ply ; make sure your mesh has the correct name !!\";\r\n\t\treturn false;\r\n\t}\r\n\tif (!fileExists(pathRC + \"/bundle.out\")) {\r\n\t\tSIBR_WRG << \"***** No file \" << pathRC + \"/bundle.out ; make sure your bundle file has the correct name !!\";\r\n\t\treturn false;\r\n\t}\r\n\tif (!fileExists(pathRC + \"/list_images.txt\")) {\r\n\t\tSIBR_WRG << \"***** No file \" << pathRC + \"/list_images.txt ; make sure you generate the list_images.txt file \";\r\n\t\treturn false;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic void loadRawRC(std::string pathRC, std::vector<sibr::InputCamera::Ptr>& cams2Align,\r\n\tstd::vector<sibr::ImageRGB::Ptr>& imgs2Align, sibr::Mesh& mesh2Align)\r\n{\r\n\tcams2Align = sibr::InputCamera::loadBundle(pathRC + \"/bundle.out\", 0.01f, 1000.0f, pathRC + \"/list_images.txt\");\r\n\tmesh2Align.load(pathRC + \"/recon.ply\");\r\n\timgs2Align.resize(cams2Align.size());\r\n\tfor (int c = 0; c < cams2Align.size(); c++) {\r\n\t\tsibr::ImageRGB::Ptr imgPtr;\r\n\t\tsibr::ImageRGB img;\r\n\t\tif (!img.load(pathRC + \"/\" + cams2Align[c]->name()))\r\n\t\t\tif (!img.load(pathRC + \"/\" + cams2Align[c]->name() + \".png\"))\r\n\t\t\t\tif (!img.load(pathRC + \"/\" + cams2Align[c]->name() + \".jpg\")) {\r\n\t\t\t\t\tSIBR_ERR << \"Error loading dataset to align from \" << pathRC;\r\n\t\t\t\t\tSIBR_ERR << \"Problem loading images from raw RC, exiting \";\r\n\t\t\t\t}\r\n\r\n\t\timgs2Align[c] = img.clonePtr();\r\n\t}\r\n}\r\n\r\nstatic bool isRawSynthetic(std::string pathSynthetic)\r\n{\r\n\t// do we have bundle, mesh and list images ?\r\n\tsibr::Mesh mesh2Align;\r\n\tif (!fileExists(pathSynthetic + \"/scene.obj\")) {\r\n\t\tSIBR_WRG << \"***** No file \" << pathSynthetic + \"/scene.obj ; make sure your mesh has the correct name !!\";\r\n\t\treturn false;\r\n\t}\r\n\tif (!fileExists(pathSynthetic + \"/cameras.lookat\")) {\r\n\t\tSIBR_WRG << \"***** No file \" << pathSynthetic + \"/cameras.lookat ; make sure your bundle file has the correct name !!\";\r\n\t\treturn false;\r\n\t}\r\n\tif (!directoryExists(pathSynthetic + \"/images\")) {\r\n\t\tSIBR_WRG << \"***** No file \" << pathSynthetic + \"/images ; make sure you have images folder inside the scene \";\r\n\t\treturn false;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic void loadRawSynthetic(std::string pathSynthetic, std::vector<sibr::InputCamera::Ptr>& cams2Align,\r\n\tstd::vector<sibr::ImageRGB::Ptr>& imgs2Align, sibr::Mesh& mesh2Align)\r\n{\r\n\tcams2Align = sibr::InputCamera::loadLookat(pathSynthetic + \"/cameras.lookat\", std::vector<sibr::Vector2u>{sibr::Vector2u(1920, 1080)}, 0.01f, 1000.0f);\r\n\tSIBR_WRG << \"We assume a size of the synthetic images of 1920*1080. If it is not your case, this loading will not work properly\";\r\n\tmesh2Align.load(pathSynthetic + \"/scene.obj\");\r\n\timgs2Align.resize(cams2Align.size());\r\n\tfor (int c = 0; c < cams2Align.size(); c++) {\r\n\t\tsibr::ImageRGB::Ptr imgPtr;\r\n\t\tsibr::ImageRGB img;\r\n\t\tif (!img.load(pathSynthetic + \"/images/\" + cams2Align[c]->name()))\r\n\t\t\tif (!img.load(pathSynthetic + \"/images/\" + cams2Align[c]->name() + \".png\"))\r\n\t\t\t\tif (!img.load(pathSynthetic + \"/images/\" + cams2Align[c]->name() + \".jpg\")) {\r\n\t\t\t\t\tSIBR_ERR << \"Error loading dataset to align from \" << pathSynthetic;\r\n\t\t\t\t\tSIBR_ERR << \"Problem loading images from raw RC, exiting \";\r\n\t\t\t\t}\r\n\r\n\t\timgs2Align[c] = img.clonePtr();\r\n\t}\r\n}\r\n\r\n\r\nint assignImages(\r\n\tstd::vector<sibr::ImageRGB::Ptr>& imgs2Align, std::vector<sibr::ImageRGB>& imgs2AlignSmall,\r\n\tstd::vector<sibr::ImageRGB::Ptr>& imgsRef, std::vector<sibr::ImageRGB>& imgsRefSmall,\r\n\tstd::map<int, int>& alignCamToRef, std::vector<sibr::InputCamera::Ptr>& camsRef, std::vector<sibr::InputCamera::Ptr> cams2Align,\r\n\tint resizeW, std::set<int>& assignedCam, float threshold)\r\n{\r\n\tint assignCnt = 0;\r\n\tstd::cout << \"Assigning \" << imgs2Align.size() << \" cameras from the set to align to the fixed one: \";\r\n\t//We then look for closest match and assign it only if the distance between the images is half the median distance\r\n\t//This prevent issues in the case were a camera is missing from one set\r\n\tfor (int i = 0; i < imgs2Align.size(); i++) {\r\n\r\n\t\tstd::cout << \"Assigning camera \" << i << \", \";\r\n\r\n\t\tsibr::ImageRGB& im2Align = imgs2AlignSmall[i];\r\n\t\tsibr::Vector2i pos2Align(resizeW / 2, im2Align.h() / 2);\r\n\r\n\t\tdouble minImDist = DBL_MAX;\r\n\t\tint bestIm = -1;\r\n\r\n\t\tstd::vector<double> dists;\r\n\t\tstd::cerr << \"2 ALIGN TESTING \" << std::endl;\r\n\r\n\t\tcv::Rect centerROI(im2Align.w() / 8, im2Align.h() / 8, 6 * im2Align.w() / 8, 6 * im2Align.h() / 8);\r\n\r\n\t\tfor (int j = 0; j < imgsRef.size(); j++) {\r\n\t\t\tif (assignedCam.find(j) != assignedCam.end())\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tsibr::ImageRGB& imRef = imgsRefSmall[j];\r\n\t\t\tdouble minDist = DBL_MAX;\r\n\t\t\tint wIm = imRef.w();\r\n\t\t\tint hIm = imRef.h();\r\n\r\n\t\t\tfor (int dx = -wIm / 8; dx <= wIm / 8; dx += 4) {\r\n\t\t\t\tfor (int dy = -hIm / 8; dy <= hIm / 8; dy += 4) {\r\n\t\t\t\t\tcv::Rect shiftROI(dx + wIm / 8, dy + hIm / 8, 6 * wIm / 8, 6 * hIm / 8);\r\n\t\t\t\t\tdouble d = cv::norm(imRef.toOpenCV()(shiftROI), im2Align.toOpenCV()(centerROI));\r\n\t\t\t\t\tif (d < minDist)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tminDist = d;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tdists.push_back(minDist);\r\n\r\n\t\t\tif (minDist < minImDist) {\r\n\t\t\t\tminImDist = minDist;\r\n\t\t\t\tbestIm = j;\r\n\r\n\t\t\t\t//show(imRef);\r\n\t\t\t\t//show(im2Align);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstd::sort(dists.begin(), dists.end());\r\n\t\tstd::cerr << \" SIZe \" << dists.size() << \" min \" << minImDist << \" half \" << threshold * dists[dists.size() / 2] << std::endl;\r\n\t\tif (dists.size() > 5 && minImDist < threshold * dists[dists.size() / 2]) {\r\n\t\t\talignCamToRef[i] = bestIm;\r\n\t\t\tassignedCam.emplace(bestIm);\r\n\t\t\tstd::wcout << i << \" -> \" << bestIm << \" -- \" << cams2Align[i]->name().c_str() << \" -> \" << camsRef[bestIm]->name().c_str() << std::endl;\r\n\t\t\tassignCnt++;\r\n\t\t}\r\n\t\telse {\r\n\t\t\talignCamToRef[i] = -1;\r\n\t\t\tstd::wcout << i << \" -> \" << \"Not assigned \" << std::endl;\r\n\t\t\tstd::wcout << i << \" BEST MATCH -> \" << bestIm << \" -- \" << cams2Align[i]->name().c_str() << \" -> \" << camsRef[bestIm]->name().c_str() << std::endl;\r\n\t\t}\r\n\r\n\t\t//show(imgs2Align[i]);\r\n\t\t//show(imgsRef[bestIm]);\r\n\r\n\t}\r\n\treturn assignCnt;\r\n}\r\n\r\nInputCamera rot90CC(InputCamera::Ptr& in)\r\n{\r\n\tInputCamera rotCam = *in;\r\n\trotCam.size(rotCam.h(), rotCam.w());\r\n\trotCam.aspect(1.0f / rotCam.aspect());\r\n\trotCam.fovy(2.0 * atan(0.5 * rotCam.h() / rotCam.focal()));\r\n\trotCam.setLookAt(rotCam.position(), rotCam.position() + rotCam.dir(), rotCam.right());\r\n\treturn rotCam;\r\n}\r\n\r\n\r\nstruct AlignMeshesArgs :\r\n\tvirtual BasicIBRAppArgs {\r\n\tRequiredArg<std::string> pathRef = { \"pathRef\", \"Path to the fixed scene\" };\r\n\tRequiredArg<std::string> pathToAlign = { \"path2Align\", \"Path to the scene to align\" };\r\n\tRequiredArg<std::string> outPath = { \"out\", \"Path to the folder where to write the transformed mesh and the matrix\" };\r\n\tArg<bool> forceLandscape = { \"forceLandscape\", \"Option to force all images to be in landscape orientation before image assignation and correspondances computation\" };\r\n\tArg<bool> saveScene = { \"saveScene\", \"If true saves entire scene, else only save the transformed mesh and transform.txt file in out dir\"\r\n\t};\r\n};\r\n\r\nint main(int ac, char** av)\r\n{\r\n\t// Parse Commad-line Args\r\n\tCommandLineArgs::parseMainArgs(ac, av);\r\n\tAlignMeshesArgs myArgs;\r\n\r\n\t//sibr::Window window(100, 100, \"Window\");\r\n\tsibr::Window\t\twindow(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs);\r\n\tint wRender, hRender;\r\n\r\n\tstd::cout << \"This method relies on images, cameras and meshes of both scenes.\" << std::endl;\r\n\r\n\t//\tHere is the data strctures that we will use for this program to make it as generic as possible\r\n\tstd::vector<sibr::ImageRGB::Ptr> imgsRef;\r\n\tstd::vector<sibr::ImageRGB::Ptr> imgs2AlignOriginal;\r\n\tstd::vector<sibr::ImageRGB::Ptr> imgs2Align;\r\n\r\n\tstd::vector<sibr::ImageRGB> imgsRefSmall;\r\n\tstd::vector<sibr::ImageRGB> imgs2AlignSmall;\r\n\r\n\tstd::vector<sibr::InputCamera::Ptr> camsRef;\r\n\tstd::vector<sibr::InputCamera::Ptr> cams2Align;\r\n\r\n\tsibr::Mesh meshRef;\r\n\tsibr::Mesh mesh2Align;\r\n\t//Create the two scenes\r\n\r\n\t//Load the reference data\r\n\tIParseData::Type refSceneType;\r\n\tif (myArgs.pathRef.get() == \"\")\r\n\t\tSIBR_ERR << \"Reference path empty\";\r\n\tBasicIBRAppArgs argsRefScene;\r\n\targsRefScene.dataset_path = myArgs.pathRef.get();\r\n\r\n\ttry {\r\n\t\tBasicIBRScene::Ptr\t\tsceneRef(new BasicIBRScene(argsRefScene, true));\r\n\r\n\t\tif ((refSceneType = sceneRef->data()->datasetType()) != IParseData::Type::EMPTY) {\r\n\t\t\tmeshRef = sceneRef->proxies()->proxy();\r\n\t\t\timgsRef = sceneRef->images()->inputImages();\r\n\t\t\tcamsRef = sceneRef->cameras()->inputCameras();\r\n\t\t}\r\n\t\telse \r\n\t\t\tSIBR_ERR << \"Error loading reference dataset from \" << myArgs.pathRef.get();\r\n\t}\r\n\r\n   \tcatch(...) {\r\n\t\tstd::cout << \"Trying to load Raw RealityCapture or Synthetic data\" << std::endl;\r\n\t\tif (isRawRC(argsRefScene.dataset_path)) {  // try \"raw RC\" option\r\n\t\t\tloadRawRC(argsRefScene.dataset_path, camsRef, imgsRef, meshRef);\r\n\t\t}\r\n\t\telse if (isRawSynthetic(argsRefScene.dataset_path)) {\r\n\t\t\tloadRawSynthetic(argsRefScene.dataset_path, camsRef, imgsRef, meshRef);\r\n\t\t}\r\n\t\telse \r\n\t\t\tSIBR_ERR << \"Error loading reference dataset from \" << myArgs.pathRef.get();\r\n   \t}\r\n\r\n\r\n\t//Load the data for the scene to align\t\r\n\tif (myArgs.pathToAlign.get() == \"\")\r\n\t\tSIBR_ERR << \"Path to mesh to align empty\";\r\n\tBasicIBRAppArgs argsAlignScene;\r\n\targsAlignScene.dataset_path = myArgs.pathToAlign.get();\r\n\t// \r\n\ttry {\r\n\t\tBasicIBRScene::Ptr\t\tsceneAlign(new BasicIBRScene(argsAlignScene, true));\r\n\r\n\t\tif (sceneAlign->data()->datasetType() != IParseData::Type::EMPTY) {\r\n\t\t\tmesh2Align = sceneAlign->proxies()->proxy();\r\n\t\t\timgs2AlignOriginal = sceneAlign->images()->inputImages();\r\n\t\t\tcams2Align = sceneAlign->cameras()->inputCameras();\r\n\t\t}\r\n\t\telse {\r\n\t\t\tSIBR_ERR << \"Error loading dataset to align from \" << myArgs.pathToAlign.get();\r\n\t\t}\r\n\t}\r\n\r\n\r\n   \tcatch(...) {\r\n\t\tstd::cout << \"Trying to load Raw RealityCapture or Synthetic data\" << std::endl;\r\n\t\tif (isRawRC(argsAlignScene.dataset_path)){ // try \"raw RC\" option\r\n\t\t\tloadRawRC(argsAlignScene.dataset_path, cams2Align, imgs2AlignOriginal, mesh2Align);\r\n\t\t}\r\n\t\telse if (isRawSynthetic(argsAlignScene.dataset_path)) {\r\n\t\t\tloadRawSynthetic(argsAlignScene.dataset_path, cams2Align, imgs2AlignOriginal, mesh2Align);\r\n\t\t}\r\n\t\telse\r\n\t\t\tSIBR_ERR << \"Error loading dataset to align from \" << myArgs.pathToAlign.get();\r\n   \t}\r\n\t\r\n\tif (myArgs.forceLandscape) {\r\n\t\tfor (int c = 0; c < camsRef.size(); c++) {\r\n\t\t\tif (imgsRef[c]->h() > imgsRef[c]->w()) {\r\n\t\t\t\t//rotate the image\r\n\t\t\t\tcv::rotate(imgsRef[c]->toOpenCV(), imgsRef[c]->toOpenCVnonConst(), cv::ROTATE_90_COUNTERCLOCKWISE);\r\n\t\t\t\t//rotate the camera\r\n\t\t\t\t*camsRef[c] = rot90CC(camsRef[c]);\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\tfor (int c = 0; c < cams2Align.size(); c++) {\r\n\t\t\tif (imgs2AlignOriginal[c]->h() > imgs2AlignOriginal[c]->w()) {\r\n\t\t\t\tcv::rotate(imgs2AlignOriginal[c]->toOpenCV(), imgs2AlignOriginal[c]->toOpenCVnonConst(), cv::ROTATE_90_COUNTERCLOCKWISE);\r\n\t\t\t\t//rotate the camera\r\n\t\t\t\t*cams2Align[c] = rot90CC(cams2Align[c]);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\r\n\t//We resize the input images to the same width to account for possible rescale between the two scenes\r\n#pragma omp parallel for\r\n\tfor (int c = 0; c < camsRef.size(); c++) {\r\n\t\t*imgsRef[c] = imgsRef[c]->resized(1024, 1024.0f * imgsRef[c]->h() / imgsRef[c]->w(), cv::INTER_LINEAR);\r\n\t}\r\n\r\n\tfor (int c = 0; c < cams2Align.size(); c++) {\r\n\t\timgs2Align.push_back(sibr::ImageRGB::Ptr(new sibr::ImageRGB()));\r\n\t}\r\n#pragma omp parallel for\r\n\tfor (int c = 0; c < cams2Align.size(); c++) {\r\n\t\t*imgs2Align[c] = imgs2AlignOriginal[c]->resized(1024, 1024.0f * imgs2AlignOriginal[c]->h() / imgs2AlignOriginal[c]->w(), cv::INTER_LINEAR);\r\n\t}\r\n\r\n\t//Create output dir\r\n\tstd::string outPath;\r\n\toutPath = myArgs.outPath.get();\r\n\t//First create all the needed directories\r\n\tsibr::makeDirectory(outPath);\r\n\r\n\t//We now match images between the two scenes as we cannot rely on correspondance between cameras\r\n\t//First we make all image 512*512\r\n\tint resizeW = 512;\r\n\tcv::Rect centerROI(resizeW / 4, resizeW / 4, resizeW / 2, resizeW / 2);\r\n\tstd::map<int, int> alignCamToRef;\r\n\tstd::set<int> assignedCam;\r\n\r\n\tstd::cout << \"Resizing images\" << std::endl;\r\n\r\n\timgs2AlignSmall.resize(imgs2Align.size());\r\n#pragma omp parallel for\r\n\tfor (int i = 0; i < imgs2Align.size(); i++) {\r\n\t\timgs2AlignSmall[i] = imgs2Align[i]->resized(resizeW, resizeW, cv::INTER_AREA);\r\n\t\timgs2AlignSmall[i].fromOpenCV(imgs2AlignSmall[i].toOpenCV()(centerROI));\r\n\t}\r\n\timgsRefSmall.resize(imgsRef.size());\r\n#pragma omp parallel for\r\n\tfor (int i = 0; i < imgsRef.size(); i++) {\r\n\t\timgsRefSmall[i] = imgsRef[i]->resized(resizeW, resizeW, cv::INTER_AREA);\r\n\t\timgsRefSmall[i].fromOpenCV(imgsRefSmall[i].toOpenCV()(centerROI));\r\n\t}\r\n\r\n\tint cnt = assignImages(imgs2Align, imgs2AlignSmall, imgsRef, imgsRefSmall, alignCamToRef, camsRef, cams2Align, resizeW, assignedCam, 0.7);\r\n\tstd::cout << \"Assigned \" << cnt << std::endl;\r\n\r\n\t/////////////////\r\n\t/////////////////\r\n\t// Now we will compute closely matched feature between pair of images\r\n\t/////////////////\r\n\t/////////////////\r\n\r\n\tstd::vector<sibr::Vector3f> listFeatPRef;\r\n\tstd::vector<sibr::Vector3f> listFeatP2Align;\r\n\r\n\tstd::vector<float> dist2CamRef;\r\n\tstd::vector<float> dist2Cam2Align;\r\n\r\n\t//Maximum shift for patch alignement\r\n\tint shiftMax = 16;\r\n\tconst int patchRadius = 8;\r\n\r\n\tfor (int im = 0; im < imgs2Align.size(); im++) {\r\n\r\n\t\tif (alignCamToRef[im] < 0)\r\n\t\t\tcontinue;\r\n\r\n\t\tstd::cout << \"IM \" << im << std::endl;\r\n\r\n\t\tsibr::ImageRGB& imRef = *imgsRef[alignCamToRef[im]];\r\n\t\tsibr::ImageRGB& im2Align = *imgs2Align[im];\r\n\r\n\t\t// To see feature match in images\r\n\t\tsibr::ImageRGB imRefCopy = imRef.clone();\r\n\t\tsibr::ImageRGB im2AlignCopy = im2Align.clone();\r\n\r\n\t\t//Compute im center to transpose position from imref to im2Align. The center should stay aligned with the crop.\r\n\t\t//Small errors are absorbed by the shift estimation.\r\n\t\tsibr::Vector2i imRefCenter(imRef.w() / 2, imRef.h() / 2);\r\n\t\tsibr::Vector2i im2AlignCenter(im2Align.w() / 2, im2Align.h() / 2);\r\n\r\n\t\t//Get the two cameras\r\n\t\tsibr::InputCamera::Ptr camRef = camsRef[alignCamToRef[im]];\r\n\t\tsibr::InputCamera::Ptr cam2Align = cams2Align[im];\r\n\r\n\t\t//Depth map for 3D position estimation\r\n\t\tsibr::ImageL32F depthMapRef, depthMap2Align;\r\n\r\n\t\t//We render the two depth map of the two meshes to be able to recover 3D position from pixel position\r\n\t\tint wCamRef, hCamRef, wCam2Align, hCam2Align;\r\n\t\twCamRef = camRef->w();\r\n\t\thCamRef = camRef->h();\r\n\t\twCam2Align = cam2Align->w();\r\n\t\thCam2Align = cam2Align->h();\r\n\r\n\t\tstd::cout << \"Rendering reference DepthMap ...\" << std::endl;\r\n\t\tsibr::DepthRenderer rendererDepthRef(wCamRef, hCamRef);\r\n\t\tglViewport(0, 0, wCamRef, hCamRef);\r\n\t\trendererDepthRef.render(*camRef, meshRef);\r\n\t\trendererDepthRef._depth_RT->readBack(depthMapRef);\r\n\r\n\t\t//showFloat(depthMapRef.resized(1024, 1024 * hCamRef / wCamRef));\r\n\t\tstd::cout << \"Rendering recon DepthMap ...\" << std::endl;\r\n\r\n\t\tsibr::DepthRenderer rendererDepth2Align(wCam2Align, hCam2Align);\r\n\t\tglViewport(0, 0, wCam2Align, hCam2Align);\r\n\t\trendererDepth2Align.render(*cam2Align, mesh2Align);\r\n\t\trendererDepth2Align._depth_RT->readBack(depthMap2Align);\r\n\r\n\t\t//showFloat(depthMap2Align.resized(1024, 1024 * hCam2Align / wCam2Align));\r\n\t\tconst int stride = std::max(16.0, sqrt(imRef.w() * imRef.h() * imgs2Align.size() / 50000.0));\r\n\t\tstd::wcout << \"   Stride:\" << stride << std::endl;\r\n\r\n\t\tfloat ratioRefW = (float)depthMapRef.w() / imRefCopy.w();\r\n\t\tfloat ratioRefH = (float)depthMapRef.h() / imRefCopy.h();\r\n\t\tfloat ratio2AlignW = (float)depthMap2Align.w() / im2AlignCopy.w();\r\n\t\tfloat ratio2AlignH = (float)depthMap2Align.h() / im2AlignCopy.h();\r\n\r\n#pragma omp parallel for\r\n\t\tfor (int i = patchRadius; i < imRef.w(); i += stride) {\r\n\t\t\tfor (int j = patchRadius; j < imRef.h(); j += stride) {\r\n\r\n\t\t\t\tsibr::Vector2i posRef(i, j);\r\n\t\t\t\t//Corresponding position is estimated from the center because the two center of the images should be aligned\r\n\t\t\t\tsibr::Vector2i pos2Align = im2AlignCenter + posRef - imRefCenter;\r\n\r\n\r\n\t\t\t\tif (imRef.isInRange(i, j)\r\n\t\t\t\t\t&& imRef(i, j) != Vector3ub(0, 0, 0)\r\n\t\t\t\t\t&& im2Align.isInRange(pos2Align.x(), pos2Align.y())\r\n\t\t\t\t\t&& im2Align.isInRange(pos2Align.x() - (shiftMax + patchRadius), pos2Align.y() - (shiftMax + patchRadius))\r\n\t\t\t\t\t&& im2Align.isInRange(pos2Align.x() + shiftMax + patchRadius + 1, pos2Align.y() + shiftMax + patchRadius + 1)) {\r\n\r\n\t\t\t\t\t// To visualize feature match in images\r\n\t\t\t\t\timRefCopy(i, j) = sibr::Vector3ub(255, 0, 0);\r\n\t\t\t\t\tim2AlignCopy(pos2Align.x(), pos2Align.y()) = sibr::Vector3ub(255, 0, 0);\r\n\r\n\t\t\t\t\tdouble minDist = DBL_MAX;\r\n\t\t\t\t\tsibr::Vector2i bestShift(0, 0);\r\n\r\n\t\t\t\t\t//We find the best shift to refine our point matching\r\n\t\t\t\t\tfor (int k = -shiftMax; k <= shiftMax; k++) {\r\n\t\t\t\t\t\tfor (int l = -shiftMax; l <= shiftMax; l++) {\r\n\r\n\t\t\t\t\t\t\tsibr::Vector2i shift(k, l);\r\n\t\t\t\t\t\t\tsibr::Vector2i pos2AlignShifted = pos2Align + shift;\r\n\r\n\t\t\t\t\t\t\tdouble dist = distPatch(imRef, posRef, im2Align, pos2AlignShifted, patchRadius);\r\n\r\n\t\t\t\t\t\t\tif (dist < minDist || (dist == minDist && shift.norm() < bestShift.norm())) {\r\n\t\t\t\t\t\t\t\tbestShift = shift;\r\n\t\t\t\t\t\t\t\tminDist = dist;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tsibr::Vector2i pos2AlignShifted = pos2Align + bestShift;\r\n\t\t\t\t\tsibr::Vector2i posImFullRef(posRef.x() * ratioRefW, posRef.y() * ratioRefH);\r\n\t\t\t\t\tsibr::Vector2i posImFull2Align(pos2AlignShifted.x() * ratio2AlignW, pos2AlignShifted.y() * ratio2AlignH);\r\n\r\n\t\t\t\t\tif (depthMapRef(posImFullRef.x(), posImFullRef.y()).x() != 1 &&\r\n\t\t\t\t\t\tdepthMap2Align(posImFull2Align.x(), posImFull2Align.y()).x() != 1) {\r\n\t\t\t\t\t\t// To see feature match in images\r\n\t\t\t\t\t\tim2AlignCopy(pos2Align.x() + bestShift.x(), pos2Align.y() + bestShift.y()) = sibr::Vector3ub(0, 255, 0);\r\n\r\n\t\t\t\t\t\tfloat dRef = depthMapRef(posImFullRef.x(), posImFullRef.y()).x();\r\n\t\t\t\t\t\tsibr::Vector3f pos3DRef = camRef->unprojectImgSpaceInvertY(\r\n\t\t\t\t\t\t\tposImFullRef, dRef);\r\n\r\n\t\t\t\t\t\tfloat d2Align = depthMap2Align(posImFull2Align.x(), posImFull2Align.y()).x();\r\n\t\t\t\t\t\tsibr::Vector3f pos3D2Align = cam2Align->unprojectImgSpaceInvertY(\r\n\t\t\t\t\t\t\tposImFull2Align, d2Align);\r\n\r\n\t\t\t\t\t\t//Add those feature to the list\r\n#pragma omp critical\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (pos3DRef == pos3DRef && pos3D2Align == pos3D2Align) {\r\n\t\t\t\t\t\t\t\tlistFeatPRef.push_back(pos3DRef);\r\n\t\t\t\t\t\t\t\tlistFeatP2Align.push_back(pos3D2Align);\r\n\r\n\t\t\t\t\t\t\t\tdist2CamRef.push_back((pos3DRef - camRef->position()).norm());\r\n\t\t\t\t\t\t\t\tdist2Cam2Align.push_back((pos3D2Align - cam2Align->position()).norm());\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\t\tstd::cout << \"Skipping bad point\" << std::endl;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// To see feature match in images\r\n\t\t//show(imRefCopy);\r\n\t\t//show(im2AlignCopy);\r\n\r\n\t}\r\n\r\n\t//Now we will remove outliers that appears for several reason, one of them is obviously the part of the images where the content changed.\r\n\t//We compute the median scale factor using the distances to cameras, and we only keep matches that respect this median\r\n\tstd::vector<float> scalesfromCam;\r\n\tfor (int i = 0; i < dist2CamRef.size(); i++) {\r\n\t\tscalesfromCam.push_back(dist2CamRef[i] / dist2Cam2Align[i]);\r\n\t}\r\n\tstd::sort(scalesfromCam.begin(), scalesfromCam.end());\r\n\tfloat medianScale = scalesfromCam[scalesfromCam.size() / 2];\r\n\tstd::cout << std::endl << \"Median is \" << medianScale << std::endl;\r\n\r\n\tstd::vector<sibr::Vector3f> listStrongFeatPRef;\r\n\tstd::vector<sibr::Vector3f> listStrongFeatP2Align;\r\n\r\n\t//5% above and under the median\r\n\tfor (int i = 0; i < dist2CamRef.size(); i++) {\r\n\t\tif (dist2CamRef[i] / dist2Cam2Align[i] > 0.95 * medianScale &&\r\n\t\t\tdist2CamRef[i] / dist2Cam2Align[i] < 1.05 * medianScale) {\r\n\t\t\tlistStrongFeatPRef.push_back(listFeatPRef[i]);\r\n\t\t\tlistStrongFeatP2Align.push_back(listFeatP2Align[i]);\r\n\t\t}\r\n\t}\r\n\r\n\tstd::cout << \"Cleaned matches: \" << listFeatPRef.size() << \" to \" << listStrongFeatPRef.size() << std::endl;\r\n\r\n\t/////////\r\n\t/////////\r\n\t//Now we will estimate the transformation using irls.\r\n\t/////////\r\n\t/////////\r\n\r\n\t//Format the data\r\n\tstd::vector<float> XData;\r\n\tstd::vector<float> YData0;\r\n\tstd::vector<float> YData1;\r\n\tstd::vector<float> YData2;\r\n\r\n\tfor (int i = 0; i < listStrongFeatPRef.size(); i++) {\r\n\t\tXData.push_back(listStrongFeatP2Align[i].x());\r\n\t\tXData.push_back(listStrongFeatP2Align[i].y());\r\n\t\tXData.push_back(listStrongFeatP2Align[i].z());\r\n\r\n\t\tYData0.push_back(listStrongFeatPRef[i].x());\r\n\t\tYData1.push_back(listStrongFeatPRef[i].y());\r\n\t\tYData2.push_back(listStrongFeatPRef[i].z());\r\n\t}\r\n\r\n\t///////////////\r\n\t//SOLVING WITH IRLS\r\n\tint numX = listStrongFeatP2Align.size();\r\n\t// Convert std::vector to Eigen::VectorXf\r\n\tEigen::Map<Eigen::VectorXf> evY0(YData0.data(), numX);\r\n\tEigen::Map<Eigen::VectorXf> evY1(YData1.data(), numX);\r\n\tEigen::Map<Eigen::VectorXf> evY2(YData2.data(), numX);\r\n\r\n\tEigen::Map<Eigen::Matrix<float, -1, 3, Eigen::RowMajor> > mX3(XData.data(), numX, 3);\r\n\r\n\t// Convert 3-column matrix into a 4-column one using all 1s for the last column.\r\n\tEigen::MatrixX4f mX4(mX3.rows(), 4);\r\n\tmX4 << mX3, Eigen::ArrayXXf::Ones(mX3.rows(), 1);\r\n\r\n#define TUNING_CONSTANT 4.685\r\n\tEigen::Vector4f vCoeffs0;\r\n\tEigen::Vector4f vCoeffs1;\r\n\tEigen::Vector4f vCoeffs2;\r\n\r\n\t// Run IRLS considering each of the Y-matrix's calumns as the target Y-vector individually.\r\n\t// We get one row of the solution matrix at each step which is then put together to form the \r\n\t// complete solution.\r\n\t//Log(EInfo, \"Running IRLS on row 0\");\r\n\tirls(mX4, evY0, vCoeffs0, TUNING_CONSTANT);\r\n\t//Log(EInfo, \"Running IRLS on row 1\");\r\n\tirls(mX4, evY1, vCoeffs1, TUNING_CONSTANT);\r\n\t//Log(EInfo, \"Running IRLS on row 2\");\r\n\tirls(mX4, evY2, vCoeffs2, TUNING_CONSTANT);\r\n\t//Log(EInfo, \"Finished running IRLS\");\r\n\r\n\t// Put all the rows together.\r\n\tEigen::Matrix4f mFinal;\r\n\tmFinal << vCoeffs0.x(), vCoeffs0.y(), vCoeffs0.z(), vCoeffs0.w(),\r\n\t\tvCoeffs1.x(), vCoeffs1.y(), vCoeffs1.z(), vCoeffs1.w(),\r\n\t\tvCoeffs2.x(), vCoeffs2.y(), vCoeffs2.z(), vCoeffs2.w(),\r\n\t\t0, 0, 0, 1;\r\n\r\n\tstd::cout << \"Matrix is:\" << std::endl;\r\n\tstd::cout << vCoeffs0.x() << \" \" << vCoeffs0.y() << \" \" << vCoeffs0.z() << \" \" << vCoeffs0.w() << std::endl;\r\n\tstd::cout << vCoeffs1.x() << \" \" << vCoeffs1.y() << \" \" << vCoeffs1.z() << \" \" << vCoeffs1.w() << std::endl;\r\n\tstd::cout << vCoeffs2.x() << \" \" << vCoeffs2.y() << \" \" << vCoeffs2.z() << \" \" << vCoeffs2.w() << std::endl;\r\n\tstd::cout << 0 << \" \" << 0 << \" \" << 0 << \" \" << 1 << std::endl;\r\n\tstd::cout << medianScale << std::endl; // for xFormScene scale factor of 1\r\n\r\n\tstd::ofstream myfile;\r\n\tmyfile.open(outPath + \"/transform.txt\");\r\n\tmyfile << vCoeffs0.x() << \" \" << vCoeffs0.y() << \" \" << vCoeffs0.z() << \" \" << vCoeffs0.w() << std::endl;\r\n\tmyfile << vCoeffs1.x() << \" \" << vCoeffs1.y() << \" \" << vCoeffs1.z() << \" \" << vCoeffs1.w() << std::endl;\r\n\tmyfile << vCoeffs2.x() << \" \" << vCoeffs2.y() << \" \" << vCoeffs2.z() << \" \" << vCoeffs2.w() << std::endl;\r\n\tmyfile << 0 << \" \" << 0 << \" \" << 0 << \" \" << 1 << std::endl;\r\n\tmyfile << 1 << std::endl; // for xFormScene scale factor of 1\r\n\tmyfile.close();\r\n\r\n\t//create new mesh :\r\n\tMesh alignedMesh = mesh2Align;\r\n\tstd::cout << \"Input vertices num : \" << mesh2Align.vertices().size();\r\n\tMesh::Vertices newVertices;\r\n\tfor (sibr::Vector3f v : alignedMesh.vertices()) {\r\n\t\tsibr::Vector4f v4(v.x(), v.y(), v.z(), 1.0);\r\n\t\tnewVertices.push_back((mFinal * v4).xyz());\r\n\t}\r\n\talignedMesh.vertices(newVertices);\r\n\tstd::cout << \" Output vertices num : \" << alignedMesh.vertices().size() << std::endl;\r\n\r\n\tif (myArgs.saveScene) {\r\n\t\tsibr::makeDirectory(outPath + \"/meshes\");\r\n\t\tsibr::makeDirectory(outPath + \"/cameras\");\r\n\t\tsibr::makeDirectory(outPath + \"/images\");\r\n\r\n\t\t//Save the meshes\r\n\t\talignedMesh.save(outPath + \"/meshes/recon.ply\", true);\r\n\t\talignedMesh.save(outPath + \"/meshes/recon.obj\", true);\r\n\t\r\n\t\t//Save the cameras\r\n\t\t//transform first\r\n\t\tfor (auto& cam : cams2Align) {\r\n\t\t\tsibr::Vector3f pos = cam->position();\r\n\t\t\tsibr::Vector3f center = cam->position() + cam->dir();\r\n\t\t\tsibr::Vector3f up = cam->position() + cam->up();\r\n\t\t\tpos = (mFinal * pos.homogeneous()).xyz();\r\n\t\t\tcenter = (mFinal * center.homogeneous()).xyz();\r\n\t\t\tup = (mFinal * up.homogeneous()).xyz();\r\n\t\t\tcam->setLookAt(pos, center, (up - pos).normalized());\r\n\t\t}\r\n\t\tstd::vector<InputCamera::Ptr> outCams;\r\n\t\tfor (const auto& cam : cams2Align) {\r\n\t\t\toutCams.push_back(std::make_shared<InputCamera>(*cam));\r\n\t\t}\r\n\r\n\t\tsibr::InputCamera::saveAsBundle(outCams, outPath + \"/cameras/bundle.out\");\r\n\r\n\t\t//Save the images and metadata\r\n\t\tstd::ofstream outputSceneMetadata;\r\n\t\toutputSceneMetadata.open(outPath + \"/scene_metadata.txt\");\r\n\t\toutputSceneMetadata << \"Scene Metadata File\\n\" << std::endl;\r\n\t\toutputSceneMetadata << \"[list_images]\\n<filename> <image_width> <image_height> <near_clipping_plane> <far_clipping_plane>\" << std::endl;\r\n\r\n\t\tint im = 0;\r\n\t\tfor (const auto& camIm : cams2Align) {\r\n\r\n\t\t\t//std::string extensionFile = boost::filesystem::extension(camIm->name());\r\n\t\t\tstd::ostringstream ssZeroPad;\r\n\t\t\tssZeroPad << std::setw(8) << std::setfill('0') << camIm->id();\r\n\t\t\tstd::string newFileName = ssZeroPad.str() + \".jpg\";\r\n\t\t\timgs2AlignOriginal[im]->save(outPath + \"/images/\" + newFileName);\r\n\t\t\toutputSceneMetadata << newFileName << \" \" << camIm->w() << \" \" << camIm->h() << \" \" << camIm->znear() << \" \" << camIm->zfar() << std::endl;\r\n\t\t\tim++;\r\n\t\t}\r\n\t\toutputSceneMetadata << \"\\n// Always specify active/exclude images after list images\\n\\n[exclude_images]\\n<image1_idx> <image2_idx> ... <image3_idx>\" << std::endl;\r\n\t\toutputSceneMetadata << \"\\n\\n\\n[other parameters]\" << std::endl;\r\n\t\toutputSceneMetadata.close();\r\n\t}\r\n\telse { // just save meshes (transform.txt saved above)\r\n\t\tstd::string textureFileName;\r\n\t\t// could preserve texture name, but is probably cleaner to have standard name\r\n\t\ttextureFileName = \"textured_u1_v1.png\";\r\n\r\n\t\talignedMesh.save(outPath + \"/mesh.ply\", true, textureFileName);\r\n\t\talignedMesh.save(outPath + \"/mesh.obj\", true);\r\n\t\t// save the mtl file\r\n\t\tstd::string mtlFileName = outPath + \"/mesh.mtl\";\r\n\t\tstd::ofstream mtlFile;\r\n\t\tmtlFile.open(mtlFileName);\r\n\t\tmtlFile << \"# File produced by SIBR\\n\\nnewmtl $Material_0\\nKa 1 1 1\\nKd 1 1 1\\nd 1\\nNs 0\\nillum 1\\nmap_Kd \" << textureFileName;\r\n\t\tmtlFile.close();\r\n\t}\r\n\r\n\treturn EXIT_SUCCESS;\r\n}\r\n\r\n\r\n\r\ntemplate <typename T> std::vector<size_t> sort_indexes(const std::vector<T>& v) {\r\n\r\n\t// initialize original index locations\r\n\tstd::vector<size_t> idx(v.size());\r\n\tiota(idx.begin(), idx.end(), 0);\r\n\r\n\t// sort indexes based on comparing values in v\r\n\tsort(idx.begin(), idx.end(),\r\n\t\t[&v](size_t i1, size_t i2) {return v[i1] < v[i2]; });\r\n\r\n\treturn idx;\r\n}\r\n\r\n\r\n\r\nvoid computeRT(std::vector<sibr::Vector3f> A, std::vector<sibr::Vector3f>B, Matrix3f S, Matrix3f& R, sibr::Vector3f& T) {\r\n\r\n\t//finding R,T such that B = RA + T;\r\n\tconst int numPoint = B.size();\r\n\t//Scaling the points to align\r\n\tstd::vector<sibr::Vector3f> AScaled;\r\n\tfor (int i = 0; i < numPoint; i++) {\r\n\t\tAScaled.push_back(S * A[i]);\r\n\t}\r\n\r\n\t//Computing the centroids\r\n\tsibr::Vector3f centroidB(0, 0, 0);\r\n\tsibr::Vector3f centroidA(0, 0, 0);\r\n\tfor (int i = 0; i < numPoint; i++) {\r\n\t\tcentroidB += B[i];\r\n\t\tcentroidA += AScaled[i];\r\n\t}\r\n\tcentroidB /= numPoint;\r\n\tcentroidA /= numPoint;\r\n\r\n\r\n\t//Now we estimate the rotation :\r\n\tMatrix3f H;\r\n\tH << 0, 0, 0,\r\n\t\t0, 0, 0,\r\n\t\t0, 0, 0;\r\n\r\n\tfor (int i = 0; i < numPoint; i++) {\r\n\t\tH += (AScaled[i] - centroidA) * (B[i] - centroidB).transpose();\r\n\t}\r\n\r\n\tEigen::JacobiSVD<Eigen::MatrixXf> svd(H, Eigen::ComputeThinU | Eigen::ComputeThinV);\r\n\r\n\tR = svd.matrixV() * svd.matrixU().transpose();\r\n\tif (R.determinant() < 0) {\r\n\t\t//R.col(2) *= -1;\r\n\t\t//std::cout << \"Warning : determinant of rotation matrix is negative, multiplying last column by -1\" << std::endl;\r\n\t}\r\n\r\n\t//Translation estimation :\r\n\tT = -R * centroidA + centroidB;\r\n\r\n}\r\n\r\nfloat computeS(std::vector<sibr::Vector3f> A, std::vector<sibr::Vector3f>B, float& minScale, float& maxScale) {\r\n\t//findin S such that A*S has the same scale as B\r\n\tsibr::Vector3f meanPosA(0, 0, 0);\r\n\tsibr::Vector3f meanPosB(0, 0, 0);\r\n\r\n\tfor (int i = 0; i < A.size(); i++) {\r\n\t\tmeanPosA += A[i];\r\n\t\tmeanPosB += B[i];\r\n\t}\r\n\r\n\tmeanPosA /= A.size();\r\n\tmeanPosB /= A.size();\r\n\r\n\tfloat scale = 0;\r\n\tfor (int i = 0; i < A.size(); i++) {\r\n\r\n\t\tfloat scale_i = (B[i] - meanPosB).norm() / (A[i] - meanPosA).norm();\r\n\t\tscale += scale_i;\r\n\r\n\t\tif (scale_i > maxScale)\r\n\t\t\tmaxScale = scale_i;\r\n\t\tif (scale_i < minScale)\r\n\t\t\tminScale = scale_i;\r\n\t}\r\n\r\n\tscale /= A.size();\r\n\r\n\treturn scale;\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/cameraConverter/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(cameraConverter)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n\tsibr_system\n\tsibr_assets\n    sibr_graphics\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/cameraConverter/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"core/system/CommandLineArgs.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n\nusing namespace sibr;\n\n/* Camera converter args. */\nstruct CameraConverterArgs : virtual AppArgs {\n\tRequiredArg<std::string> input = { \"input\",  \"input camera file\" };\n\tRequiredArg<std::string> output = { \"output\",   \"output camera file\" };\n\tRequiredArg<std::string> colmapPath = { \"colmapPath\",   \"path to colmap recon for camera file\" };\n\tArg<std::string> transfo = { \"transfo\",  \"\", \"matrix file\" };\n\tArg<sibr::Vector2u> inputRes = {\"ires\", {1920, 1080}, \"input camera resolution (not required for all formats)\"};\n\tArg<sibr::Vector2u> outputRes = { \"ores\", {1920, 1080}, \"output camera resolution (not required for all formats)\" };\n\tArg<bool> inverse = {\"inverse\",  \"reverse the transformation\"};\n\tArg<bool> bundleImageList = { \"images_list\", \"for a bundle output, output list_images.txt\" };\n\tArg<bool> bundleImageFiles = { \"images_files\",  \"for a bundle output, output empty images in a 'visualize' subdirectory\" };\n\tArg<std::string> inImageFilePath = { \"in_images_files\", \"\", \"for a bundle input images file directory (for list_images etc)\" };\n\tArg<float> scale = { \"scale\", 1.0, \"scale images for cameras.txt file\" };\n};\n\n/* SIBR binary path loader helper.\n * \\param filename the .path binary file\n * \\param cams will be populated with the loaded cameras\n * \\param res the image resolution (for aspect ratio)\n * \\return the loading status\n */\nbool load(const std::string& filename, std::vector<InputCamera::Ptr> & cams, const sibr::Vector2u & res){\n\tsibr::ByteStream stream;\n\tif(!stream.load(filename)) {\n\t\treturn false;\n\t}\n\tint32 num = 0;\n\tstream >> num;\n\twhile (num > 0) {\n\t\tCamera cam;\n\t\tstream >> cam;\n\t\tcams.push_back(std::make_shared<InputCamera>(cam, res[0], res[1]));\n\t\t--num;\n\t}\n\treturn stream;\n}\n\n/** SIBR binary path saver helper.\n * \\param filename the .path output file\n * \\param cams the cameras to save\n */\nvoid save(const std::string& filename, const std::vector<InputCamera::Ptr> & cams){\n\tsibr::ByteStream stream;\n\tconst int32 num = int32(cams.size());\n\tstream << num;\n\tfor (const InputCamera::Ptr& cam : cams) {\n\t\tCamera subcam(*cam);\n\t\tstream << subcam;\n\t}\n\tstream.saveToFile(filename);\n}\n\n\t\n\nvoid colmapSave(const std::string& filename, const std::vector<InputCamera::Ptr> & xformPath, float scale, float focaly, float focalx) {\n\t// save as colmap images.txt file\n\tsibr::Matrix3f converter;\n\tconverter << 1, 0, 0,\n\t\t     0, -1, 0,\n\t\t     0, 0, -1;\n\t\n\tstd::ofstream outputColmapPath, outputColmapPathCams;\n\tstd::string colmapPathCams = parentDirectory(filename) + std::string(\"/cameras.txt\");\n\n\tstd::cerr << std::endl;\n\tstd::cerr << std::endl;\n\tstd::cerr << \"Writing colmap path to \" << parentDirectory(filename) << std::endl;\n\t\n\toutputColmapPath.open(filename);\n\tif(!outputColmapPath.good())\n\t\tSIBR_ERR << \"Cant open output file \" <<  filename << std::endl;\n\toutputColmapPathCams.open(colmapPathCams);\n\n\toutputColmapPathCams << \"# Camera list with one line of data per camera:\" << std::endl;\n\toutputColmapPathCams << \"#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]\" << std::endl;\n\toutputColmapPathCams << \"# Number of cameras: 1\" << std::endl;\n\tif (focalx == -1) {\n\t\tfocalx = xformPath[0]->focal() * xformPath[0]->aspect(); // use aspect ratio\n\t\tSIBR_WRG << \"No focal x given making it equal to focaly * aspect ratio; use result at own risk. Should have a colmap dataset as input\" << std::endl;\n\t}\n\telse\n\t{\n\t\tstd::cerr << \"FX \" << focalx << std::endl;\n\t\tfocalx = xformPath[0]->focal() * (focalx / focaly);\n\t\tSIBR_WRG << \"Focal x set to f / (fx/fy); f of first image :\" << focalx<<std::endl;\n\t}\n\tfor(int i=0; i<xformPath.size(); i++)  {\n\t\toutputColmapPathCams << i+1 << \" PINHOLE \" << xformPath[0]->w()*scale << \" \" << xformPath[0]->h()*scale\n\t\t\t<< \" \" << xformPath[0]->focal()*scale << \" \" << focalx*scale \n\t\t\t<< \" \" << xformPath[0]->w()*scale * 0.5 << \" \" << xformPath[0]->h()*scale * 0.5 << std::endl;\n\t}\n\n\n\toutputColmapPath<< \"# Image list with two lines of data per image:\" << std::endl;\n\toutputColmapPath<< \"#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME\" << std::endl;\n\toutputColmapPath<< \"#   POINTS2D[] as (X, Y, POINT3D_ID)\" << std::endl;\n\tfor(int i=0; i<xformPath.size(); i++)  {\n\t\tsibr::Matrix3f tmp = xformPath[i]->rotation().toRotationMatrix() * converter;\n\t\tsibr::Matrix3f Qinv = tmp.transpose();\n\t\tsibr::Quaternionf q = quatFromMatrix(Qinv);\n\t\tsibr::Vector3f t = -Qinv*xformPath[i]->position();\n\n\t\toutputColmapPath << i << \" \" <<  q.w() << \" \" <<  -q.x() << \" \" <<  -q.y() << \" \" <<  -q.z() << \" \" <<  \n\t\t\tt.x() << \" \" << t.y() << \" \" << t.z() << \" \" << 1 << \" \" << \"pathImage\"<<i << std::endl;\n\t\toutputColmapPath << std::endl; // empty line, no points\n\t}\n\toutputColmapPath.close();\n\toutputColmapPathCams.close();\n}\n\n\n/** The camera converter is a utility to convert a camera path from a file format to another, with additional options.\n * You can specify a 4x4 transformation matrix to apply, stored in a txt file (values separated by spaces/newlines, f.i. the output of CloudCompare alignment).\n * If you want to apply the inverse transformation, use the --inverse flag.\n * Supported inputs: path (*), lookat (*), bundle, nvm (*), colmap\n * Supported outputs: path, bundle(*), lookat, colmap (images.txt)\n * (*): requires an input/output resolution (often only for the aspect ratio).\n * */\nint main(int ac, char** av) {\n\n\t// Parse Command-line Args\n\tsibr::CommandLineArgs::parseMainArgs(ac, av);\n\tconst CameraConverterArgs args;\n\n\t// Load cameras.\n\tstd::vector<InputCamera::Ptr> cams;\n\tconst std::string ext = sibr::getExtension(args.input);\n\tif(ext == \"path\") {\n\t\tload(args.input, cams, args.inputRes);\n\t} else if(ext == \"lookat\") {\n\t\tcams = InputCamera::loadLookat(args.input, { args.inputRes });\n\t} else if (ext == \"out\") {\n\t\tif (std::string(args.inImageFilePath) == \"\")\n\t\t\tSIBR_ERR << \"Please provide image file directory for bundler input (use option -in_images_files DIRECTORY_CONTAINING_LIST_IMAGES.txt )\\nIf necessary use the generate_list_images.py script to generate list_images.txt \" << std::endl;\n\t\tcams = InputCamera::loadBundle(args.input, 0.01, 1000, args.inImageFilePath, true);\n\t} else if (ext == \"nvm\") {\n\t\tcams = InputCamera::loadNVM(args.input, 0.01f, 1000.0f, {args.inputRes});\n\t} else if (sibr::directoryExists(args.input)) {\n\t\t// If we got a directory, assume colmap sparse.\n\t\tcams = InputCamera::loadColmap(args.input, 0.01, 1000, 1);\n\t} else {\n\t\tSIBR_ERR << \"Unsupported path file extension: \" << ext << \".\" << std::endl;\n\t\treturn EXIT_FAILURE;\n\t}\n\tSIBR_LOG << \"Loaded \" << cams.size() << \" cameras.\" << std::endl;\n\n\tfloat focaly = cams[0]->focal(); // y by default\n\tfloat focalx = cams[0]->focalx();\n\n\t// if a path is given try and get focalx\n\tstd::vector<InputCamera::Ptr> camsFx;\n\tif (args.colmapPath != \"\") {\n\t\tstd::cerr << \"COLMAP \" << args.colmapPath << std::endl;\n\t\tstd::string cm_sparse_path = args.colmapPath.get() + \"/stereo/sparse\";\n\t\tif (directoryExists(cm_sparse_path)) {\n\t\t\tcamsFx = InputCamera::loadColmap(cm_sparse_path, 0.01, 1000, 1);\n\t\t\tstd::cerr << \"Found \" << camsFx.size() << \" cameras fovx \" << camsFx[0]->focalx() << std::endl;\n\t\t\tfocalx = camsFx[0]->focalx();\n\t\t}\n\t\telse\n\t\t\tstd::cerr << \"Cant find \" << cm_sparse_path << std::endl;\n\t}\n\n\t// Load the transformation.\n\tstd::ifstream transFile(args.transfo.get());\n\tsibr::Matrix4f transf = sibr::Matrix4f::Identity();\n\tif (transFile.is_open()) {\n\t\tfor (int i = 0; i < 16; ++i) {\n\t\t\tfloat f;\n\t\t\ttransFile >> f;\n\t\t\ttransf(i) = f;\n\t\t}\n\t\ttransFile.close();\n\t}\n\ttransf.transposeInPlace();\n\tif (args.inverse) {\n\t\ttransf = transf.inverse().eval();\n\t}\n\n\t// Apply transformation to each camera keypoints, if it's not identity.\n\tif(!transf.isIdentity()) {\n\t\tSIBR_LOG << \"Applying transformation: \" << std::endl << transf << std::endl;\n\t\tfor (auto & cam : cams) {\n\t\t\tsibr::Vector3f pos = cam->position();\n\t\t\tsibr::Vector3f center = cam->position() + cam->dir();\n\t\t\tsibr::Vector3f up = cam->position() + cam->up();\n\t\t\tpos = (transf * pos.homogeneous()).xyz();\n\t\t\tcenter = (transf * center.homogeneous()).xyz();\n\t\t\tup = (transf * up.homogeneous()).xyz();\n\t\t\tcam->setLookAt(pos, center, (up - pos).normalized());\n\n\t\t}\n\t}\n\n\t// Save cameras.\n\tconst std::string outExt = sibr::getExtension(args.output);\n\tif (outExt == \"path\") {\n\t\tsave(args.output, cams);\n\t} else if (outExt == \"out\") { // bundler\n\t\tstd::vector<InputCamera::Ptr> outCams;\n\t\tfor(const auto & cam : cams) {\n\t\t\tconst int outH = int(args.outputRes.get()[1]);\n\t\t\tconst int outW = int(std::round(cam->aspect() * float(outH)));\n\t\t\tInputCamera::Ptr oc;\n\t\t\toutCams.push_back(oc=std::make_shared<InputCamera>(*cam, outW, outH));\n\t\t\t// reset focal\n\t\t\toc->setFocal(cam->focal());\n\t\t}\n\t\tsibr::InputCamera::saveAsBundle(outCams, args.output, args.bundleImageList, args.bundleImageFiles, false);\n\t} else if (outExt == \"lookat\") {\n\t\tstd::vector<InputCamera::Ptr> outCams;\n\t\tfor (const auto& cam : cams) {\n\t\t\tconst int outH = int(args.outputRes.get()[1]);\n\t\t\tconst int outW = int(std::round(cam->aspect() * float(outH)));\n\t\t\toutCams.push_back(std::make_shared<InputCamera>(*cam, outW, outH));\n\t\t}\n\t\tsibr::InputCamera::saveAsLookat(outCams, args.output);\n\t}\n\telse if (getFileName(args.output) == \"images.txt\" ) { // colmap\n\t\tcolmapSave(args.output, cams, args.scale, focaly, focalx);\n\t} else {\n\t\tSIBR_ERR << \"Unsupported output file extension: \" << outExt << \".\" << std::endl;\n\t\treturn EXIT_FAILURE;\n\t}\n\tSIBR_LOG << \"Saved transformed cameras to \\\"\" << args.output.get() << \"\\\".\" << std::endl;\n\t\t\n\treturn EXIT_SUCCESS;\n}\n\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/clippingPlanes/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(clippingPlanes)\n\n# libraries used:\n# * sibr graphics (vector2i class)\n# * sibr system (programArg class)\n# * boost filesystem (path class)\n# * openmp\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n    sibr_graphics\n    sibr_assets\n    sibr_raycaster\n    sibr_system\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/clippingPlanes/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <iostream>\n#include <core/system/CommandLineArgs.hpp>\n#include <core/raycaster/CameraRaycaster.hpp>\n#include <core/assets/ImageListFile.hpp>\n#include <core/system/Utils.hpp>\n\n/*\ngenerate clipping_planes.txt file\n*/\nconst char* USAGE\t\t\t\t\t\t= \"Usage: clippingPlanes <dataset-path> \\n\";\nconst char* TAG\t\t\t\t\t\t\t= \"[clippingPlanes]\";\n\nusing namespace sibr;\n\n\nint main(const int argc, const char** argv)\n{\n\n\tif (argc < 1 + 1)\n\t{\n\t\tstd::cout << USAGE << std::endl;\n\t\treturn 1;\n\t}\n\n\tstd::string\t\tdatasetPath = argv[1];\n\n\tif (directoryExists(datasetPath) == false) {\n\t\tSIBR_ERR << \"Wrong program options, check the usage.\";\n\t\treturn 1;\n\t}\n\n\t// load rest of the things\n\tstd::vector<InputCamera::Ptr>\tinCams = InputCamera::load(datasetPath);\n\tImageListFile\t\t\t\timageListFile;\n\tMesh\t\t\t\t\t\tproxy(false);\n\n\t// check needed things are there\n\tif (imageListFile.load(datasetPath + \"/images/list_images.txt\") == false && imageListFile.load(datasetPath + \"/list_images.txt\") == false)\n\t\treturn 1;\n\n\tif ((proxy.load(datasetPath + \"/meshes/pmvs_recon.ply\") == false) && (proxy.load(datasetPath + \"/meshes/mesh.ply\") == false) && (proxy.load(datasetPath + \"/pmvs_recon.ply\") == false) && (proxy.load(datasetPath + \"/recon.ply\") == false) && (proxy.load(datasetPath + \"/meshes/recon.ply\") == false))\n\t\treturn 1;\n\n\tconst std::string clipping_planes_file_path = datasetPath + \"/clipping_planes.txt\";\n\tif (!sibr::fileExists(clipping_planes_file_path)) {\n\n\t\tstd::vector<sibr::Vector2f> nearsFars;\n\t\tCameraRaycaster::computeClippingPlanes(proxy, inCams, nearsFars);\n\n\t\tstd::ofstream file(clipping_planes_file_path, std::ios::trunc | std::ios::out);\n\t\tif (file) {\n\t\t\tfor (const auto & nearFar : nearsFars) {\n\t\t\t\tif (nearFar[0] > 0 && nearFar[1] > 0) {\n\t\t\t\t\tfile << nearFar[0] << ' ' << nearFar[1] << std::endl;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t/** \\todo [SP]Temporary fix. Ideally we should exclude these images. */\n\t\t\t\t\tfile << \"0.1 100.0\" << std::endl;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile.close();\n\t\t}\n\t\telse {\n\t\t\tSIBR_WRG << \" Could not save file '\" << clipping_planes_file_path << \"'.\" << std::endl;\n\t\t}\n\t}\n\n\tstd::cout << TAG << \" done!\\n\";\n\treturn 0;\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(dataset_tools_converters)\n\nfile(GLOB_RECURSE SCRIPTS \"*.py\" \"*.sh\" \"*.mlx\")\n\nadd_custom_target(${PROJECT_NAME} ALL)\n\ninclude(install_runtime)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\nibr_install_rsc(${PROJECT_NAME} TYPE \"scripts\" FILES ${SCRIPTS} RELATIVE)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/bundle.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# in python, elements declared outside __init__ are static (belong to the class)\n# while ones declared inside __init__ belong to the object\n\nimport os\nimport get_image_size   # way faster than loading images in opencv and lightweighted (not as the case with python's pillow)\nfrom enum import IntEnum\n\nclass InputImage:\n\n    def __init__(self, cam_id, path_to_image):\n        self.id         = cam_id\n        self.path       = path_to_image         # absolute path\n        self.filename   = os.path.basename(path_to_image)\n        width, height   = get_image_size.get_image_size(path_to_image)\n        self.resolution = [width, height]\n\n    def __str__(self):\n        return \"{0}\\t{1}\\t{2}\".format(self.path, self.resolution[0], self.resolution[1])\n        # return str(self.filename) + delimiter + str(self.resolution[0]) + delimiter + str(self.resolution[1])\n        #return \"{0}\\t{1}\\t{2}\".format(self.filename, self.resolution[0], self.resolution[1])\n\n\nclass BundleFeaturePointLine (IntEnum):\n    POSITION    = 0\n    COLOR       = 1\n    VIEW_LIST   = 2\n\n\nclass BundleCamera:\n\n    def __init__(self, cam_id, focal_length, radial_dist, rotation, translation):\n        self.id                     = cam_id\n        self.focal_length           = focal_length\n        self.radial_dist            = radial_dist\n        self.rotation               = rotation\n        self.translation            = translation\n\n        # inverse index of feature points\n        self.list_of_feature_points  = []\n\n    def add_feature_point (feature_point):\n        pass\n\n    def set_feature_points (list_of_feature_points):\n        pass\n\n    def scale_focal_length (self, factor):\n        self.focal_length = self.focal_length * factor\n\n    def __str__(self):\n        first_line      = \"{0:g} {1:g} {2:g}\\n\".format(self.focal_length, self.radial_dist[0], self.radial_dist[1])\n        second_line     = \"{0:g} {1:g} {2:g}\\n\".format(self.rotation[0][0], self.rotation[0][1], self.rotation[0][2])\n        third_line      = \"{0:g} {1:g} {2:g}\\n\".format(self.rotation[1][0], self.rotation[1][1], self.rotation[1][2])\n        fourth_line     = \"{0:g} {1:g} {2:g}\\n\".format(self.rotation[2][0], self.rotation[2][1], self.rotation[2][2])\n        fifth_line      = \"{0:g} {1:g} {2:g}\".format(self.translation[0], self.translation[1], self.translation[2])\n        return first_line + second_line + third_line + fourth_line + fifth_line\n\n\nclass BundleFeaturePoint:\n\n    def __init__(self, feature_point_id, position, color, view_list):\n        self.id         = feature_point_id\n        self.position   = position\n        self.color      = color # each channel between [0, 255]\n\n        # list of cameras\n        # each camera has the following info:\n        # (<camId> <sift keypoint> <x> <y>) # (x,y) floating point value with (0,0) center of the img\n        self.view_list  = view_list\n        # for colmap conversion\n        self.point2d_index = {}\n\n    def remove_cam(self, cam_id):\n        for index in range (len(self.view_list)):\n            if (self.view_list[index][0] == cam_id):\n                del self.view_list[index]\n                break\n        # fix all subsequent indices\n\n        newlist = []\n        nr1 = len(self.view_list)\n        change = False\n        for vl_item in self.view_list:\n            newitem = list(vl_item)\n            if (vl_item[0] > cam_id):\n#                change = True\n                newitem[0] = newitem[0]-1\n                newlist.append(tuple(newitem))\n            else:\n                newlist.append(vl_item)\n\n        if change:\n            print(\"NEW : {}\\n\".format( newlist ))\n            print(\"OLD : {}\\n\".format( self.view_list ))\n\n        self.view_list = newlist\n\n    def __str__(self):\n        first_line      = \"{0:g} {1:g} {2:g}\\n\".format(self.position[0], self.position[1], self.position[2])\n        second_line     = \"{0} {1} {2}\\n\".format(self.color[0], self.color[1], self.color[2])\n        third_line      = str(len(self.view_list)) + \" \"\n        cam_index = 0\n        for view_info in self.view_list:\n            third_line = third_line + \"{0:g} {1:g} {2:g} {3:g}\".format(view_info[0], view_info[1], view_info[2], view_info[3])\n            if (cam_index != len(self.view_list) - 1):\n                third_line = third_line + \" \"\n            cam_index = cam_index + 1\n        return first_line + second_line + third_line\n\nclass Bundle:\n\n    MAX_NR_FEATURE_POINTS = 8000000   # if bundle file has more than this limit, features will not be processed at all\n\n    def __init__(self, path_to_bundle):\n        # read bundle file\n        input_file = open(path_to_bundle, \"r\")\n\n        # first line is the header containing bundle version\n        self.header             = input_file.readline().strip()\n        self.nr_cameras, self.nr_feature_points = map(int, input_file.readline().strip().split(\" \"))\n\n        self.using_feature_points = (self.nr_feature_points < Bundle.MAX_NR_FEATURE_POINTS)\n        if (not self.using_feature_points):\n            self.nr_feature_points = 0\n            print (\"[bundle.py] Warning: Too many feature points. They are going to be discarded\")\n\n        self.list_of_cameras = []\n        self.list_of_feature_points = []\n\n        for i in range(self.nr_cameras):\n            # read each camera\n            focal_length, radial_dist_x, radial_dist_y = map(float, input_file.readline().strip().split(\" \"))\n            r11, r12, r13 = map(float, input_file.readline().strip().split(\" \"))\n            r21, r22, r23 = map(float, input_file.readline().strip().split(\" \"))\n            r31, r32, r33 = map(float, input_file.readline().strip().split(\" \"))\n            tx, ty, tz = map(float, input_file.readline().strip().split(\" \"))\n\n            camera = BundleCamera(i, focal_length, (radial_dist_x, radial_dist_y), [ [r11, r12, r13], [r21, r22, r23], [r31, r32, r33] ], [tx, ty, tz])\n\n            self.list_of_cameras.append(camera)\n\n        if (self.using_feature_points):\n            # keep reading input file\n            # read feature points (sometimes there aren't as many as reported in the header)\n            # display a warning when this happens\n            type_of_line_to_read = BundleFeaturePointLine.POSITION\n\n            feature_point_position  = None\n            feature_point_color     = None\n            feature_point_view_list = None\n            feature_point_id        = 0\n\n            for line in input_file:\n                if (type_of_line_to_read == BundleFeaturePointLine.POSITION):\n                    x, y, z = map(float, line.strip().split(\" \"))\n                    feature_point_position = [x, y, z]\n                elif (type_of_line_to_read == BundleFeaturePointLine.COLOR):\n                    r, g, b = map(int, line.strip().split(\" \"))\n                    feature_point_color = [r, g, b]\n                elif (type_of_line_to_read == BundleFeaturePointLine.VIEW_LIST):\n                    tokens = line.split()\n                    nr_cams_that_see_point = int(tokens[0])\n                    list_of_view_info = []\n                    for i in range(nr_cams_that_see_point):\n                        cam_id  = int   (tokens[1 + i*4+0])\n                        sift    = int   (tokens[1 + i*4+1])\n                        x_pos   = float (tokens[1 + i*4+2])\n                        y_pos   = float (tokens[1 + i*4+3])\n                        list_of_view_info.append( (cam_id, sift, x_pos, y_pos) )\n\n                    # add feature point to the list of feature points contained in the bundle\n                    feature_point = BundleFeaturePoint(feature_point_id, feature_point_position, feature_point_color, list_of_view_info)\n                    \n                    # for colmap conversion\n                    for v in list_of_view_info:\n                        if v[0] >= len(self.list_of_cameras):\n                            print(\"ERROR \", v[0], \"  \", len(self.list_of_cameras))\n                        else:\n                            self.list_of_cameras[v[0]].list_of_feature_points.append(feature_point)\n\n                    feature_point_id = feature_point_id + 1\n\n                    self.list_of_feature_points.append(feature_point)\n\n                type_of_line_to_read = (type_of_line_to_read + 1) % len (BundleFeaturePointLine)\n\n        # done processing input file\n        input_file.close()\n\n        # paths\n        self.path_to_bundle_file = path_to_bundle\n        self.root_directory = os.path.dirname(path_to_bundle)\n\n        # get absolute path to input images\n        image_id = 0\n        self.list_of_input_images = []\n        for file_in_dir in os.listdir( self.root_directory ):\n            # input imgs have a [jpg|jpeg|png] extension\n            if (file_in_dir.lower().endswith(\".jpg\" ) or file_in_dir.lower().endswith(\".png\" ) or file_in_dir.lower().endswith(\".jpeg\")):\n                # input images must also have a numerical filename (avoid reading things as texture images that are stored in the same folder)\n                if (os.path.splitext(file_in_dir)[0].isdigit()):\n                    absolute_path = os.path.join(self.root_directory, file_in_dir)\n                    image = InputImage(image_id, absolute_path)\n                    self.list_of_input_images.append(image)\n                    image_id = image_id + 1\n\n        # additional data\n        self.list_of_excluded_cams = []\n        self.has_right_nr_feature_pts = False\n        self.has_right_nr_images = (len(self.list_of_cameras) == len(self.list_of_input_images))\n\n        if (not self.has_right_nr_images):\n            print (\"[bundle.py] Warning: nr cameras in bundle file (\" + str(len(self.list_of_cameras)) + \") is not the same as nr of images in \" + self.root_directory + \" (\" + str(len(self.list_of_input_images)) + \")\")\n\n        print (\"[bundle.py] Message: Done reading bundle file\", path_to_bundle)\n        print (\"[bundle.py] Message: Nr cams in bundle file\", len(self.list_of_cameras))\n        print (\"[bundle.py] Message: Nr images in root folder\", len(self.list_of_input_images))\n        print (\"[bundle.py] Message: Nr feature points\", len(self.list_of_feature_points))\n\n    def get_avg_resolution (self):\n        result = [0, 0]\n        for image in self.list_of_input_images:\n            result[0] = result[0] + image.resolution[0]\n            result[1] = result[1] + image.resolution[1]\n        if (len(self.list_of_input_images) != 0):\n            result[0] = (int)(result[0] / len(self.list_of_input_images))\n            result[1] = (int)(result[1] / len(self.list_of_input_images))\n        return result\n\n    def generate_list_of_images_file (self, path_to_output):\n        output_file = open (path_to_output, \"w\")\n        for image in self.list_of_input_images:\n            output_file.write(str(image) + '\\n')\n        output_file.close()\n\n\n    def scale (self, factor):\n        for cam in self.list_of_cameras:\n            cam.scale_focal_length(factor)\n\n    def exclude_cams (self, cam_list, verbose = True):\n        if (verbose):\n            print (\"[bundle.py] Message: excluding images\", cam_list)\n        # calling this method twice doesn't make sense because\n        # we need to make sure the index passed refer to the right cameras to remove\n\n        # sort list of cams to exclude by decreasing order\n        cam_list.sort(reverse=True)\n        for index in cam_list:\n            # don't forget to go through feature points and remove ref to cam\n            for feature_point in self.list_of_feature_points:\n                feature_point.remove_cam(index)\n\n            # log the cam id that was removed by adding it to the internal list of excluded cams\n            self.list_of_excluded_cams.append(index)\n\n            del self.list_of_cameras[index]\n            del self.list_of_input_images[index]\n\n        # update nr_cameras attribute\n        self.nr_cameras = len (self.list_of_cameras)\n\n    def save (self, path_to_output_file, new_res=[]):\n        output_file = open(path_to_output_file, \"w\")\n\n        output_file.write(self.header + '\\n')\n        output_file.write(str(self.nr_cameras) + \" \" + str(self.nr_feature_points) + '\\n')\n\n        if new_res == []:\n            for cam in self.list_of_cameras:\n                output_file.write(str(cam) + '\\n')\n        else:\n            # not needed TODO: verify\n            #indx = 0\n            for cam in self.list_of_cameras:\n                #im = self.list_of_input_images[indx]\n                #old_w = im.resolution[0]\n                #old_h = im.resolution[1]\n                #new_focal = cam.focal_length*(min(old_h/new_res[1], old_w/new_res[0]))\n                #print(\"Old : \", cam.focal_length, \" New : \" , new_focal)\n                #cam.focal_length = new_focal\n                output_file.write(str(cam) + '\\n')\n                #indx = indx + 1\n            \n        for feature_point in self.list_of_feature_points:\n#            print(\"Writing \", len(feature_point.view_list) , \" FEATURE POINTS \" )\n            if len(feature_point.view_list)> 0:\n                output_file.write(str(feature_point) + '\\n')\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/colmap2sibr.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\n\"\"\" @package dataset_tools_preprocess\nThis script calls meshlab to simplify a mesh\n\nParameters: --h help,\n            --path <path to dataset>\n\nUsage: python colmap2sibr.py --path <path to dataset> [required]\n\n\"\"\"\n\nimport os, sys\nimport argparse\nimport os, sys, getopt\nimport re\nfrom utils.commands import getProcess\nfrom utils.paths import getBinariesPath\nfrom utils.commands import runCommand\nimport subprocess\nfrom pathlib import Path\n\n\ndef checkColmapConsistent(pathdir):\n    colmapDir = Path(pathdir+\"/colmap\")\n\n    if not os.path.isdir(colmapDir):\n        return False\n\n    # check for mesh\n    colmapmesh = Path(pathdir+\"/colmap/stereo/meshed-delaunay.ply\")\n    if not os.path.isfile(colmapmesh):\n        print(\"SIBR_ERROR: colmap directory exists but there is no mesh \", colmapmesh)\n        print(\"No file\", colmapmesh)\n        return False\n\n    return True\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--path\", type=str, required=True, help=\"path to dataset folder\")\n\n    args = vars(parser.parse_args())\n\n    if not checkColmapConsistent(args['path']):\n        print(\"SIBR_ERROR Colmap hasnt been run properly; run it first (ie dont use --noColmap)\")\n        sys.exit(1)\n\n\t# prepareColmap4Sibr: convert cameras and create bundle file put everything in sfm_mvs_cm, then run the normal preprocessing\n    # \n    prepareColmap_app = getProcess(\"prepareColmap4Sibr\")\n    \n    prepareColmap_args = [prepareColmap_app,\n        \"--path\", args['path'],\n        ]\n\n    print(\"Running prepareColmap4Sibr \", prepareColmap_args)\n    p_exit = subprocess.call(prepareColmap_args)\n    if p_exit != 0:\n        print(\"SIBR ERROR: prepareColmap4Sibr failed, exiting\")\n        sys.exit(1)\n\n\t# run rc_to_sibr process to make all images have the same size and be compatible with spixelwarp pipeline\n    p_exit = subprocess.call([\"python\", \"ibr_preprocess_rc_to_sibr.py\",  \"-i\", args['path']+\"/sfm_mvs_cm\",  \"-o\", args['path']+\"/sibr_cm\"])\n    if p_exit != 0:\n        print(\"SIBR_ERROR preprocess to sibr_cm failed\");\n        sys.exit(1)\n\n    prepareColmap_app = getProcess(\"prepareColmap4Sibr\")\n    \n    prepareColmap_args = [prepareColmap_app,\n\t\t\"--fix_metadata\", \n        \"--path\", args['path'],\n        ]\n\n    print(\"Running prepareColmap4Sibr to fix scene_dataset.txt \", prepareColmap_args)\n    p_exit = subprocess.call(prepareColmap_args)\n    if p_exit != 0:\n        print(\"SIBR ERROR: prepareColmap4Sibr failed, exiting\")\n        sys.exit(1)\n\n\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/generate_list_images.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport argparse, sys, os\nfrom PIL import Image, UnidentifiedImageError\n\ndef generateListImages(imagesPath, outputPath = None, filename = \"list_images.txt\"):\n    if not os.path.exists(imagesPath):\n        print(\"Path '%s' does not exists. Aborting.\" % imagesPath)\n        sys.exit(1)\n\n    if not outputPath:\n        outputPath = imagesPath\n    elif not os.path.exists(outputPath):\n        print(\"Path '%s' does not exists. Aborting.\" % outputPath)\n        sys.exit(1)\n\n    files = os.listdir(imagesPath)\n\n    if not files:\n        print(\"No files found in directory '%s'. Aborting.\" % imagesPath)\n        sys.exit(1)\n\n    with open(os.path.join(outputPath, filename), \"w\") as list_images:\n        for file in files:\n            try:\n                if os.path.isdir(os.path.join(imagesPath, file)):\n                    raise UnidentifiedImageError()\n                with Image.open(os.path.join(imagesPath, file)) as image:\n                    list_images.write(\"%s %s %s\\n\" % (file, image.width, image.height))\n            except UnidentifiedImageError:\n                print(\"File '%s' is not a recognizable image. Skipping.\" % file)\n\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--imagesPath\", type=str, required=True, help=\"path to your images folder\")\n    parser.add_argument(\"--outputPath\", type=str, default=None, help=\"output path where to place the list_images.txt\")\n    parser.add_argument(\"--filename\", type=str, default=None, help=\"filename for the image list\")\n\n    args = vars(parser.parse_args())\n\n    if not args[\"outputPath\"]:\n        args[\"outputPath\"] = args[\"imagesPath\"]\n    elif not os.path.isdir(args[\"outputPath\"]) and os.path.basename(args[\"outputPath\"]) and not args[\"filename\"]:\n        args[\"outputPath\"], args[\"filename\"] = os.path.split(args[\"outputPath\"])\n    \n    if not args[\"filename\"]:\n        args[\"filename\"] = \"list_images.txt\"\n\n    print(\"Generating '%s' file from images in '%s' and saving to '%s'.\" % (args[\"filename\"], args[\"imagesPath\"], args[\"outputPath\"]))\n\n    generateListImages(os.path.abspath(args[\"imagesPath\"]), os.path.abspath(args[\"outputPath\"]), args[\"filename\"])\n\n    print(\"'%s' generated successfully.\" % args[\"filename\"])\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/get_image_size.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom __future__ import print_function\n\"\"\"\n\nget_image_size.py\n====================\n\n    :Name:        get_image_size\n    :Purpose:     extract image dimensions given a file path\n\n    :Author:      Paulo Scardine (based on code from Emmanuel VAÏSSE)\n\n    :Created:     26/09/2013\n    :Copyright:   (c) Paulo Scardine 2013\n    :Licence:     MIT\n\n\"\"\"\nimport collections\nimport json\nimport os\nimport struct\n\nFILE_UNKNOWN = \"Sorry, don't know how to get size for this file.\"\n\n\nclass UnknownImageFormat(Exception):\n    pass\n\n\ntypes = collections.OrderedDict()\nBMP = types['BMP'] = 'BMP'\nGIF = types['GIF'] = 'GIF'\nICO = types['ICO'] = 'ICO'\nJPEG = types['JPEG'] = 'JPEG'\nPNG = types['PNG'] = 'PNG'\nTIFF = types['TIFF'] = 'TIFF'\n\nimage_fields = ['path', 'type', 'file_size', 'width', 'height']\n\n\nclass Image(collections.namedtuple('Image', image_fields)):\n\n    def to_str_row(self):\n        return (\"%d\\t%d\\t%d\\t%s\\t%s\" % (\n            self.width,\n            self.height,\n            self.file_size,\n            self.type,\n            self.path.replace('\\t', '\\\\t'),\n        ))\n\n    def to_str_row_verbose(self):\n        return (\"%d\\t%d\\t%d\\t%s\\t%s\\t##%s\" % (\n            self.width,\n            self.height,\n            self.file_size,\n            self.type,\n            self.path.replace('\\t', '\\\\t'),\n            self))\n\n    def to_str_json(self, indent=None):\n        return json.dumps(self._asdict(), indent=indent)\n\n\ndef get_image_size(file_path):\n    \"\"\"\n    Return (width, height) for a given img file content - no external\n    dependencies except the os and struct builtin modules\n    \"\"\"\n    img = get_image_metadata(file_path)\n    return (img.width, img.height)\n\n\ndef get_image_metadata(file_path):\n    \"\"\"\n    Return an `Image` object for a given img file content - no external\n    dependencies except the os and struct builtin modules\n\n    Args:\n        file_path (str): path to an image file\n\n    Returns:\n        Image: (path, type, file_size, width, height)\n    \"\"\"\n    size = os.path.getsize(file_path)\n\n    # be explicit with open arguments - we need binary mode\n    with open(file_path, \"rb\") as input:\n        height = -1\n        width = -1\n        data = input.read(26)\n        msg = \" raised while trying to decode as JPEG.\"\n\n        if (size >= 10) and data[:6] in (b'GIF87a', b'GIF89a'):\n            # GIFs\n            imgtype = GIF\n            w, h = struct.unpack(\"<HH\", data[6:10])\n            width = int(w)\n            height = int(h)\n        elif ((size >= 24) and data.startswith(b'\\211PNG\\r\\n\\032\\n')\n              and (data[12:16] == b'IHDR')):\n            # PNGs\n            imgtype = PNG\n            w, h = struct.unpack(\">LL\", data[16:24])\n            width = int(w)\n            height = int(h)\n        elif (size >= 16) and data.startswith(b'\\211PNG\\r\\n\\032\\n'):\n            # older PNGs\n            imgtype = PNG\n            w, h = struct.unpack(\">LL\", data[8:16])\n            width = int(w)\n            height = int(h)\n        elif (size >= 2) and data.startswith(b'\\377\\330'):\n            # JPEG\n            imgtype = JPEG\n            input.seek(0)\n            input.read(2)\n            b = input.read(1)\n            try:\n                while (b and ord(b) != 0xDA):\n                    while (ord(b) != 0xFF):\n                        b = input.read(1)\n                    while (ord(b) == 0xFF):\n                        b = input.read(1)\n                    if (ord(b) >= 0xC0 and ord(b) <= 0xC3):\n                        input.read(3)\n                        h, w = struct.unpack(\">HH\", input.read(4))\n                        break\n                    else:\n                        input.read(\n                            int(struct.unpack(\">H\", input.read(2))[0]) - 2)\n                    b = input.read(1)\n                width = int(w)\n                height = int(h)\n            except struct.error:\n                raise UnknownImageFormat(\"StructError\" + msg)\n            except ValueError:\n                raise UnknownImageFormat(\"ValueError\" + msg)\n            except Exception as e:\n                raise UnknownImageFormat(e.__class__.__name__ + msg)\n        elif (size >= 26) and data.startswith(b'BM'):\n            # BMP\n            imgtype = 'BMP'\n            headersize = struct.unpack(\"<I\", data[14:18])[0]\n            if headersize == 12:\n                w, h = struct.unpack(\"<HH\", data[18:22])\n                width = int(w)\n                height = int(h)\n            elif headersize >= 40:\n                w, h = struct.unpack(\"<ii\", data[18:26])\n                width = int(w)\n                # as h is negative when stored upside down\n                height = abs(int(h))\n            else:\n                raise UnknownImageFormat(\n                    \"Unkown DIB header size:\" +\n                    str(headersize))\n        elif (size >= 8) and data[:4] in (b\"II\\052\\000\", b\"MM\\000\\052\"):\n            # Standard TIFF, big- or little-endian\n            # BigTIFF and other different but TIFF-like formats are not\n            # supported currently\n            imgtype = TIFF\n            byteOrder = data[:2]\n            boChar = \">\" if byteOrder == \"MM\" else \"<\"\n            # maps TIFF type id to size (in bytes)\n            # and python format char for struct\n            tiffTypes = {\n                1: (1, boChar + \"B\"),  # BYTE\n                2: (1, boChar + \"c\"),  # ASCII\n                3: (2, boChar + \"H\"),  # SHORT\n                4: (4, boChar + \"L\"),  # LONG\n                5: (8, boChar + \"LL\"),  # RATIONAL\n                6: (1, boChar + \"b\"),  # SBYTE\n                7: (1, boChar + \"c\"),  # UNDEFINED\n                8: (2, boChar + \"h\"),  # SSHORT\n                9: (4, boChar + \"l\"),  # SLONG\n                10: (8, boChar + \"ll\"),  # SRATIONAL\n                11: (4, boChar + \"f\"),  # FLOAT\n                12: (8, boChar + \"d\")   # DOUBLE\n            }\n            ifdOffset = struct.unpack(boChar + \"L\", data[4:8])[0]\n            try:\n                countSize = 2\n                input.seek(ifdOffset)\n                ec = input.read(countSize)\n                ifdEntryCount = struct.unpack(boChar + \"H\", ec)[0]\n                # 2 bytes: TagId + 2 bytes: type + 4 bytes: count of values + 4\n                # bytes: value offset\n                ifdEntrySize = 12\n                for i in range(ifdEntryCount):\n                    entryOffset = ifdOffset + countSize + i * ifdEntrySize\n                    input.seek(entryOffset)\n                    tag = input.read(2)\n                    tag = struct.unpack(boChar + \"H\", tag)[0]\n                    if(tag == 256 or tag == 257):\n                        # if type indicates that value fits into 4 bytes, value\n                        # offset is not an offset but value itself\n                        type = input.read(2)\n                        type = struct.unpack(boChar + \"H\", type)[0]\n                        if type not in tiffTypes:\n                            raise UnknownImageFormat(\n                                \"Unkown TIFF field type:\" +\n                                str(type))\n                        typeSize = tiffTypes[type][0]\n                        typeChar = tiffTypes[type][1]\n                        input.seek(entryOffset + 8)\n                        value = input.read(typeSize)\n                        value = int(struct.unpack(typeChar, value)[0])\n                        if tag == 256:\n                            width = value\n                        else:\n                            height = value\n                    if width > -1 and height > -1:\n                        break\n            except Exception as e:\n                raise UnknownImageFormat(str(e))\n        elif size >= 2:\n                # see http://en.wikipedia.org/wiki/ICO_(file_format)\n            imgtype = 'ICO'\n            input.seek(0)\n            reserved = input.read(2)\n            if 0 != struct.unpack(\"<H\", reserved)[0]:\n                raise UnknownImageFormat(FILE_UNKNOWN)\n            format = input.read(2)\n            assert 1 == struct.unpack(\"<H\", format)[0]\n            num = input.read(2)\n            num = struct.unpack(\"<H\", num)[0]\n            if num > 1:\n                import warnings\n                warnings.warn(\"ICO File contains more than one image\")\n            # http://msdn.microsoft.com/en-us/library/ms997538.aspx\n            w = input.read(1)\n            h = input.read(1)\n            width = ord(w)\n            height = ord(h)\n        else:\n            raise UnknownImageFormat(FILE_UNKNOWN)\n\n    return Image(path=file_path,\n                 type=imgtype,\n                 file_size=size,\n                 width=width,\n                 height=height)\n\n\nimport unittest\n\n\nclass Test_get_image_size(unittest.TestCase):\n    data = [{\n        'path': 'lookmanodeps.png',\n        'width': 251,\n        'height': 208,\n        'file_size': 22228,\n        'type': 'PNG'}]\n\n    def setUp(self):\n        pass\n\n    def test_get_image_metadata(self):\n        img = self.data[0]\n        output = get_image_metadata(img['path'])\n        self.assertTrue(output)\n        self.assertEqual(output.path, img['path'])\n        self.assertEqual(output.width, img['width'])\n        self.assertEqual(output.height, img['height'])\n        self.assertEqual(output.type, img['type'])\n        self.assertEqual(output.file_size, img['file_size'])\n        for field in image_fields:\n            self.assertEqual(getattr(output, field), img[field])\n\n    def test_get_image_metadata__ENOENT_OSError(self):\n        with self.assertRaises(OSError):\n            get_image_metadata('THIS_DOES_NOT_EXIST')\n\n    def test_get_image_metadata__not_an_image_UnknownImageFormat(self):\n        with self.assertRaises(UnknownImageFormat):\n            get_image_metadata('README.rst')\n\n    def test_get_image_size(self):\n        img = self.data[0]\n        output = get_image_size(img['path'])\n        self.assertTrue(output)\n        self.assertEqual(output,\n                         (img['width'],\n                          img['height']))\n\n    def tearDown(self):\n        pass\n\n\ndef main(argv=None):\n    \"\"\"\n    Print image metadata fields for the given file path.\n\n    Keyword Arguments:\n        argv (list): commandline arguments (e.g. sys.argv[1:])\n    Returns:\n        int: zero for OK\n    \"\"\"\n    import logging\n    import optparse\n    import sys\n\n    prs = optparse.OptionParser(\n        usage=\"%prog [-v|--verbose] [--json|--json-indent] <path0> [<pathN>]\",\n        description=\"Print metadata for the given image paths \"\n                    \"(without image library bindings).\")\n\n    prs.add_option('--json',\n                   dest='json',\n                   action='store_true')\n    prs.add_option('--json-indent',\n                   dest='json_indent',\n                   action='store_true')\n\n    prs.add_option('-v', '--verbose',\n                   dest='verbose',\n                   action='store_true',)\n    prs.add_option('-q', '--quiet',\n                   dest='quiet',\n                   action='store_true',)\n    prs.add_option('-t', '--test',\n                   dest='run_tests',\n                   action='store_true',)\n\n    argv = list(argv) if argv is not None else sys.argv[1:]\n    (opts, args) = prs.parse_args(args=argv)\n    loglevel = logging.INFO\n    if opts.verbose:\n        loglevel = logging.DEBUG\n    elif opts.quiet:\n        loglevel = logging.ERROR\n    logging.basicConfig(level=loglevel)\n    log = logging.getLogger()\n    log.debug('argv: %r', argv)\n    log.debug('opts: %r', opts)\n    log.debug('args: %r', args)\n\n    if opts.run_tests:\n        import sys\n        sys.argv = [sys.argv[0]] + args\n        import unittest\n        return unittest.main()\n\n    output_func = Image.to_str_row\n    if opts.json_indent:\n        import functools\n        output_func = functools.partial(Image.to_str_json, indent=2)\n    elif opts.json:\n        output_func = Image.to_str_json\n    elif opts.verbose:\n        output_func = Image.to_str_row_verbose\n\n    EX_OK = 0\n    EX_NOT_OK = 2\n\n    if len(args) < 1:\n        prs.print_help()\n        print('')\n        prs.error(\"You must specify one or more paths to image files\")\n\n    errors = []\n    for path_arg in args:\n        try:\n            img = get_image_metadata(path_arg)\n            print(output_func(img))\n        except KeyboardInterrupt:\n            raise\n        except OSError as e:\n            log.error((path_arg, e))\n            errors.append((path_arg, e))\n        except Exception as e:\n            log.exception(e)\n            errors.append((path_arg, e))\n            pass\n    if len(errors):\n        import pprint\n        print(\"ERRORS\", file=sys.stderr)\n        print(\"======\", file=sys.stderr)\n        print(pprint.pformat(errors, indent=2), file=sys.stderr)\n        return EX_NOT_OK\n    return EX_OK\n\n\nif __name__ == \"__main__\":\n    import sys\n    sys.exit(main(argv=sys.argv[1:]))\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/ibr_convert_old_to_new.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# --------------------------------------------\n\"\"\" @package dataset_tools_preprocess\nThis script creates a SIBR template dataset from the old SIBR dataset which can be fed to a SIBR application\n\nParameters: -h help,\n            -i <path to input directory which is the output from RC> <default: ${CMAKE_INSTALL_DIR}/bin/datasets/rc_out/>,\n            -o <path to output directory which can be fed into SIBR apps> <default: input directory> [optional],\n            -r use release w/ debug symbols executables\n\nUsage: python ibr_preprocess_rc_to_sibr.py -i <path_to_sibr>\\sibr\\install\\bin\\datasets\\museum_sibr_old_preproc\n                                           -d <path_to_sibr>\\sibr\\install\\bin\\datasets\\museum_sibr_new_preproc2\n\n\"\"\"\n\nimport subprocess\nimport shutil\nimport os, sys, getopt\nimport re\nfrom utils.commands import getProcess\nfrom utils.paths import getBinariesPath\n\nfrom os import walk\n\n#--------------------------------------------\n\n#===============================================================================\n\nimport struct\nimport imghdr\n\ndef get_image_size(fname):\n    '''Determine the image type of fhandle and return its size.\n    from draco'''\n    with open(fname, 'rb') as fhandle:\n        head = fhandle.read(24)\n        if len(head) != 24:\n            return\n        if imghdr.what(fname) == 'png':\n            check = struct.unpack('>i', head[4:8])[0]\n            if check != 0x0d0a1a0a:\n                return\n            width, height = struct.unpack('>ii', head[16:24])\n        elif imghdr.what(fname) == 'gif':\n            width, height = struct.unpack('<HH', head[6:10])\n        elif imghdr.what(fname) == 'jpeg':\n            try:\n                fhandle.seek(0) # Read 0xff next\n                size = 2\n                ftype = 0\n                while not 0xc0 <= ftype <= 0xcf:\n                    fhandle.seek(size, 1)\n                    byte = fhandle.read(1)\n                    while ord(byte) == 0xff:\n                        byte = fhandle.read(1)\n                    ftype = ord(byte)\n                    size = struct.unpack('>H', fhandle.read(2))[0] - 2\n                # We are at a SOFn block\n                fhandle.seek(1, 1)  # Skip `precision' byte.\n                height, width = struct.unpack('>HH', fhandle.read(4))\n            except Exception: #IGNORE:W0703\n                return\n        else:\n            return\n        return width, height\n    \ndef checkOutput( output, force_continue ):\n    if( output != 0):\n        if( not force_continue ):\n            sys.exit()\n        else:\n            return False\n    else:\n        return True\n    \n\n#===============================================================================\n\n#--------------------------------------------\n# 0. Paths, commands and options\n\ndef main(argv, path_dest):\n    opts, args = getopt.getopt(argv, \"hi:ro:\", [\"idir=\", \"bin=\"])\n    executables_suffix = \"\"\n    executables_folder = getBinariesPath()\n    path_data = \"\"\n    for opt, arg in opts:\n        if opt == '-h':\n            print(\"-i path_to_old_dataset -d path_to_new_dataset [-r (use release w/ debug symbols executables)]\")\n            sys.exit()\n        elif opt == '-i':\n            path_data = arg\n            print(['Setting path_data to ', path_data])\n        elif opt == '-r':\n            executables_suffix = \"_rwdi\"\n            print(\"Using rwdi executables.\")\n        elif opt == '-o':\n            path_dest = arg\n            print(['Setting path_dest to ', path_dest])\n\n    return (path_data, path_dest, executables_suffix, executables_folder)\n\npath_dest = \"\"\npath_data, path_dest, executables_suffix, executables_folder = main(sys.argv[1:], path_dest)\n\nif(path_data == \"\"):\n    path_data = os.path.abspath(os.path.join(os.path.dirname(__file__), \"../datasets\"))\n\nif(path_dest == \"\"):\n    path_dest = path_data\n\npath_data = os.path.abspath(path_data + \"/\") + \"/\"\npath_dest = os.path.abspath(path_dest + \"/\") + \"/\"\n\npath_in_imgs = path_data\n\n\nprint(['Raw_data folder: ', path_data])\nprint(['Path_dest: ', path_dest])\n\n#path_dest_pmvs    = path_dest + \"pmvs/models/\";\nfile_nameList   = path_data + \"images/list_images.txt\";\n# path_scene_metadata = path_data + \"scene_metadata.txt\"\n\n\n#--------------------------------------------\n# Create scene metadata file from list image file\nscene_metadata = \"Scene Metadata File\\n\\n\"\n\n# read list image file\npath_list_images = os.path.join(path_in_imgs, \"list_images.txt\")\nlist_images = []\n\nprint(path_list_images)\nif os.path.exists(path_list_images):\n    list_image_file = open(path_list_images, \"r\")\n\n    for line in list_image_file:\n        list_images.append(line)\n\n    list_image_file.close()\n\n# read clipping planes file\npath_clipping_planes = os.path.join(path_data, \"clipping_planes.txt\")\nclipping_planes = []\n\nif os.path.exists(path_clipping_planes):\n    clipping_planes_file = open(path_clipping_planes, \"r\")\n\n    for line in clipping_planes_file:\n        line = line.strip('\\n')\n        clipping_planes.append(line)\n\n    clipping_planes_file.close()\n\n\nif not os.path.exists(path_dest):\n    os.mkdir(path_dest)\n\nfolder_to_create = [\"images\",\"cameras\",\"meshes\",\"textures\"]\nfor f in folder_to_create:\n    if not os.path.exists(os.path.join(path_dest,f)):\n        os.mkdir(os.path.join(path_dest,f))\n\nscene_metadata = scene_metadata + \"[list_images]\\n<filename> <image_width> <image_height> <near_clipping_plane> <far_clipping_plane>\\n\"\n\nfor im in list_images:\n    print(\"copying: \"+im.split(' ', 1)[0])\n    shutil.copy(\n        os.path.join(path_data,im.split(' ', 1)[0]),\n        os.path.join(path_dest,\"images\",im.split(' ', 1)[0])\n        )\n\n    if len(clipping_planes) is not 0:\n        scene_metadata = scene_metadata + im[:-1] + \" \" + clipping_planes[0] + \"\\n\"\n    else:\n        scene_metadata = scene_metadata + im[:-1] + \" 0.01 100\\n\"\n\nshutil.copy(\n        os.path.join(path_data,\"list_images.txt\"),\n        os.path.join(path_dest,\"images\",\"list_images.txt\")\n        )\n\nshutil.copy(\n        os.path.join(path_data,\"bundle.out\"),\n        os.path.join(path_dest,\"cameras\",\"bundle.out\")\n        )\n\nshutil.copy(\n        os.path.join(path_data,\"pmvs/models/pmvs_recon.ply\"),\n        os.path.join(path_dest,\"meshes/recon.ply\")\n        )\n\nscene_metadata = scene_metadata + \"\\n\\n// Always specify active/exclude images after list images\\n\\n[exclude_images]\\n<image1_idx> <image2_idx> ... <image3_idx>\\n\"\n\n\npath_scene_metadata = os.path.join(path_dest, \"scene_metadata.txt\")\n\nscene_metadata_file = open(path_scene_metadata, \"w\")\nscene_metadata_file.write(scene_metadata)\nscene_metadata_file.close()"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/ibr_preprocess_rc_to_sibr.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# --------------------------------------------\n\"\"\" @package dataset_tools_preprocess\nThis script converts a Reality Capture dataset to SIBR template dataset which can be fed to an SIBR application\n\nParameters: -h help,\n            -i <path to input directory which is the output from RC> <default: ${CMAKE_INSTALL_DIR}/bin/datasets/rc_out/>,\n            -o <path to output directory which can be fed into SIBR apps> <default: input directory> [optional],\n            -r use release w/ debug symbols executables\n\nUsage: python ibr_preprocess_rc_to_sibr.py -r\n                                           -i <path_to_sibr>\\sibr\\install\\bin\\datasets\\museum_sibr_new_preproc_template_RCOut\n                                           -o <path_to_sibr>\\sibr\\install\\bin\\datasets\\museum_sibr_new_preproc2\n\n\"\"\"\n\nimport subprocess\nimport shutil\nimport os\nimport re\nfrom utils.commands import getProcess\nfrom utils.paths import getBinariesPath\nfrom generate_list_images import generateListImages\n\nfrom os import walk\n\n# --------------------------------------------\n\nfrom tempfile import mkstemp\nfrom shutil import move\nfrom os import remove, close\n\n# ===============================================================================\n\nimport sys, getopt\nimport struct\nimport imghdr\n\n# ===============================================================================\nimport bundle\n\n\ndef get_image_size(fname):\n    '''Determine the image type of fhandle and return its size.\n    from draco'''\n    with open(fname, 'rb') as fhandle:\n        head = fhandle.read(24)\n        if len(head) != 24:\n            return\n        if imghdr.what(fname) == 'png':\n            check = struct.unpack('>i', head[4:8])[0]\n            if check != 0x0d0a1a0a:\n                return\n            width, height = struct.unpack('>ii', head[16:24])\n        elif imghdr.what(fname) == 'gif':\n            width, height = struct.unpack('<HH', head[6:10])\n        elif imghdr.what(fname) == 'jpeg':\n            try:\n                fhandle.seek(0)  # Read 0xff next\n                size = 2\n                ftype = 0\n                while not 0xc0 <= ftype <= 0xcf:\n                    fhandle.seek(size, 1)\n                    byte = fhandle.read(1)\n                    while ord(byte) == 0xff:\n                        byte = fhandle.read(1)\n                    ftype = ord(byte)\n                    size = struct.unpack('>H', fhandle.read(2))[0] - 2\n                # We are at a SOFn block\n                fhandle.seek(1, 1)  # Skip `precision' byte.\n                height, width = struct.unpack('>HH', fhandle.read(4))\n            except Exception:  # IGNORE:W0703\n                return\n        else:\n            return\n        return width, height\n\n\n# ===============================================================================\n\ndef replace(file_path, pattern, subst):\n    # Create temp file\n    fh, abs_path = mkstemp()\n    with open(abs_path, 'w') as new_file:\n        with open(file_path) as old_file:\n            for line in old_file:\n                new_file.write(line.replace(pattern, subst))\n    close(fh)\n    # Remove original file\n    remove(file_path)\n    # Move new file\n    move(abs_path, file_path)\n\ndef checkOutput( output, force_continue ):\n    \"\"\"Check if an external process succeeded and if we should abort if something went wrong\n\n    Args:\n        output          (int): output of some external launched process (0=fine, 1 or greater=error)\n        force_continue  (bool): should we continue anyway?\n\n    Returns:\n        bool: True if last process succeeded\n    \"\"\"\n    if(output != 0):\n        if( not force_continue ):\n            sys.exit()\n        else:\n            return False\n    else:\n        return True\n\ndef get_textured_mesh_base_name (source_folder):\n    \"\"\"Return the base name of a textured mesh obtained with RealityCapture\n\n    Args:\n        source_folder (str): path to dataset (bundle file, textured and images)\n\n    Returns:\n        str: base name of <base name>.obj, <base name>.mtl and <base name>_u1_v1.png\n    \"\"\"\n    default_name = \"textured\"\n    for file_in_dir in os.listdir (source_folder):\n        if (file_in_dir.lower().endswith(\".mtl\")):\n            return os.path.splitext( file_in_dir )[0]\n    return default_name\n\n\ndef get_scale_factor (current_res, target_res):\n    \"\"\"Return the scale factor needed to go from current_res to target_res.\n    The value is calculated based on the idea that we don't want to crop the input dataset anymore\n    and that we prefer to add black borders in order to reach the target resolution\n\n    Args:\n        current_res (vec2): current dataset resolution\n        target_res  (vec2): target dataset resolution\n\n    Returns:\n        float: scale factor\n    \"\"\"\n\n    # to go from current to target resolution, we have to options:\n    # either scale down current width to match target width and modify height accordingly\n    # or scalen down current height to match target height and modify width accordingly.\n    # we take the option that doesn't crop the remaining dimension and add the least black border\n\n    # trying scaling down width\n    alpha_by_width  = (float)(target_res[0]) / current_res[0]\n    adjusted_height = (int) (alpha_by_width * current_res[1])\n    delta_height    = target_res[1] - adjusted_height\n\n    # trying scaling down height\n    alpha_by_height = (float)(target_res[1]) / current_res[1]\n    adjusted_width  = (int) (alpha_by_height * current_res[0])\n    delta_width     = target_res[0] - adjusted_width\n\n    # since we don't want to crop the input images even more, we considerer only\n    # the options were black borders need to be added\n\n    if (delta_height < 0):\n        # height would need to be cropped. take the other option\n        return alpha_by_height\n    elif (delta_width < 0):\n        # width would need to be cropped. take the other option\n        return alpha_by_width\n    else:\n        # none of them need to be cropped. take option that adds less black border\n        return alpha_by_width if (delta_height < delta_width) else alpha_by_height\n\n\n\n# ===============================================================================\n\n# --------------------------------------------\n# 0. Paths, commands and options\n\ndef main(argv, path_dest):\n    opts, args = getopt.getopt(argv, \"hi:ro:\", [\"idir=\", \"bin=\"])\n    executables_suffix = \"\"\n    executables_folder = getBinariesPath()\n    path_data = \"\"\n    for opt, arg in opts:\n        if opt == '-h':\n            print(\"-i path_to_rc_data_dir -o path_to_destination_dir [-r (use release w/ debug symbols executables)]\")\n            sys.exit()\n        elif opt == '-i':\n            path_data = arg\n            print(['Setting path_data to ', path_data])\n        elif opt == '-r':\n            executables_suffix = \"_rwdi\"\n            print(\"Using rwdi executables.\")\n        elif opt == '-o':\n            path_dest = arg\n            print(['Setting path_dest to ', path_dest])\n        elif opt in ('-bin', '--bin'):\n            executables_folder = os.path.abspath(arg)\n\n    return (path_data, path_dest, executables_suffix, executables_folder)\n\n\npath_dest = \"\"\npath_data, path_dest, executables_suffix, executables_folder = main(sys.argv[1:], path_dest)\n\nif(path_data == \"\"):\n    path_data = os.path.abspath(os.path.join(os.path.dirname(__file__), \"../datasets\"))\n\nif(path_dest == \"\"):\n    path_dest = path_data\n\npath_data = os.path.abspath(path_data + \"/\") + \"/\"\npath_dest = os.path.abspath(path_dest + \"/\") + \"/\"\nexecutables_folder = os.path.abspath(executables_folder + \"/\") + \"/\"\n\npath_in_imgs = path_data\npath_out_imgs = path_dest + \"images/\"\n\nprint(['Raw_data folder: ', path_data])\nprint(['Path_dest: ', path_dest])\nprint(['Executables folder: ', executables_folder])\n\n# dirs to create\n\nraw_data = \"raw/\"\ncameras_dir = \"cameras/\"\nimages_dir = \"images/\"\npmvs_model_dir\t= \"meshes/\"\nparentdir = os.path.dirname(os.path.split(path_dest)[0]) \nprint(\"COMPARE \" ,  parentdir , \" AND \" ,  os.path.dirname(os.path.split(path_data)[0]))\nif( parentdir == os.path.dirname(os.path.split(path_data)[0])):\n\tcapreal_dir = os.path.join(parentdir, \"capreal/\")\n\tprint(\"CAPREAL \" ,  capreal_dir)\n\tif not os.path.exists(capreal_dir):\n\t\tos.makedirs(capreal_dir)\n\ndirs_to_create = [ raw_data, cameras_dir, images_dir, pmvs_model_dir]\n\nfor dir_to_create in dirs_to_create:\n    path_to_dir = os.path.join(path_dest, dir_to_create)\n    if not os.path.exists(path_to_dir):\n        os.makedirs(path_to_dir)\n\n\n################################# GLOBALS ######################################\n\n# half size parameters\nwidth_limit         = 2500\ncreate_temp_folders = False\n\ninput_bundle = bundle.Bundle(path_data + \"bundle.out\")\n\n\n############################# RUN DISTORDCROP ##################################\n# by calling distordCrop (preprocess/distordCrop), input images that have a\n# resolution completely different from the average or that have too much\n# black border added by RealityCapture will be listed in a exclude_images.txt file.\n# A new proposed resolution will be also output in a file called cropNewSize.txt\n# In order to accelerate this process and avoid loading the images multiple times,\n# make sure that average resolution was already calculated and there is file called\n# resolutions.txt in the dataset source folder containing the current resolution\n# of each image.\n\n# distordCrop executable\ncrop_app = getProcess(\"distordCrop\" + executables_suffix, executables_folder)\n\n# query current avera resolution in dataset\navg_resolution = input_bundle.get_avg_resolution()\n\n# generate resolutions.txt and put it in the current dataset folder\nresolutions_txt_path = os.path.join(path_data, \"resolutions.txt\")\ninput_bundle.generate_list_of_images_file(resolutions_txt_path)\n\n# call distordCrop\np_exit = subprocess.call([crop_app, \"--path\", path_data, \"--ratio\",  \"0.3\", \"--avg_width\", str(avg_resolution[0]), \"--avg_height\", str(avg_resolution[1]) ])\nprint(crop_app, \" exited with \", p_exit);\ncheckOutput(p_exit, False)\n\n# read new proposed resolution and check if images were discarded\nexclude = []\npath_to_exclude_images_txt = os.path.join(path_data, \"exclude_images.txt\")\nif (os.path.exists(path_to_exclude_images_txt)):\n    # list of excluded cameras (one line having all the camera ids to exclude)\n    exclusion_file = open(path_to_exclude_images_txt, \"r\")\n    line = exclusion_file.readline()\n    tokens = line.split()\n\n    for cam_id in tokens:\n        exclude.append(int(cam_id))\n    exclusion_file.close()\n\n# exclude cams from bundle file\ninput_bundle.exclude_cams (exclude)\n\n# read proposed cropped resolution\npath_to_crop_new_size_txt = os.path.join(path_data, \"cropNewSize.txt\")\nwith open(path_to_crop_new_size_txt) as crop_size_file:\n    line = crop_size_file.readline()\n    tokens = line.split()\n    new_width   = int(tokens[0])\n    new_height  = int(tokens[1])\n    proposed_res = [new_width, new_height]\n\nprint(\"crop size:\", proposed_res)\n\n################################################################################\n\n##################### TRANSFORM IMAGES TO TARGET RESOLUTION ####################\n# we need to crop images to the previous proposed resolution (crop applied from the center).\n# if a target resolution was passed as parameter, we also need to scale down the images\n# and pad them with black borders in order to end up with the exact target resolution.\n# Before padding the images, we need to have available the temporary result of the\n# dataset scaled down in order to potentially call harmonize (which doesn't work with\n# images that were padded with black borders).\n# Scaled down dataset will be stored inside \\<destination_folder\\>/scaledDown\n# and we will store the scale down factor in a scale_factor.txt file\ntarget_res = None\nif (proposed_res[0] > width_limit):\n    half_width  = (int)(proposed_res[0] * 0.5)\n    half_height = (int)(proposed_res[0] * 0.5)\n    target_res  = [half_width, half_height]\n\n# cropFromCenter executable\ncrop_from_center_app = getProcess(\"cropFromCenter\" + executables_suffix, executables_folder)\n\n# generate file with list of current selected images to process\npath_to_transform_list_txt = os.path.join (path_data, \"toTransform.txt\")\ninput_bundle.generate_list_of_images_file(path_to_transform_list_txt)\n\ncrop_from_center_args = [crop_from_center_app,\n    \"--inputFile\", path_to_transform_list_txt,\n    \"--outputPath\", path_out_imgs,\n    \"--avgResolution\", str(avg_resolution[0]), str(avg_resolution[1]),\n    \"--cropResolution\", str(proposed_res[0]), str(proposed_res[1])\n]\n\n# calculate scale factor and how to achieve target resolution\n# scaled dataset will be store in destionation_folder/scaled\nif (target_res is not None):\n    scale_factor = get_scale_factor(proposed_res, target_res)\n    crop_from_center_args.extend([\n        \"--scaleDownFactor\", str(scale_factor),\n        \"--targetResolution\", str(target_res[0]), str(target_res[1])\n    ])\n\n# call cropFromCenter\np_exit = subprocess.call(crop_from_center_args)\nprint(crop_from_center_app, \" exited with \", p_exit);\ncheckOutput(p_exit, False)\n\n# write bundle file in output cameras folder\npath_to_output_bundle = os.path.join (path_dest, cameras_dir, \"bundle.out\")\ninput_bundle.save(path_to_output_bundle)\n\n# and also in scaled down output folder if needed\nif (target_res is not None):\n    # scale bundle file for the same factor\n    input_bundle.scale(scale_factor)\n    path_to_scaled_down_output_bundle = os.path.join (os.path.join (path_dest, \"images/scaled\"), \"bundle.out\")\n    input_bundle.save(path_to_scaled_down_output_bundle)\n\n################################################################################\n\n############################ MOVE REST OF ASSETS ###############################\n\ntextured_mesh_base_name = get_textured_mesh_base_name(path_data)\nprint(\"***** TEXT * \", textured_mesh_base_name)\n\n# copy files\nfiles_to_move = [   #['pmvs/models/pmvs_recon.ply',''],\n                    ['pmvs_recon.ply', pmvs_model_dir],\n                    ['mesh.ply', pmvs_model_dir],\n                    ['mesh.ply', capreal_dir],\n                    ['recon.ply', pmvs_model_dir],\n                    ['rc_out.csv', path_dest],\n                    [\"textured.obj\", capreal_dir],\n                    [\"textured.mtl\", capreal_dir],\n                    [\"textured_u1_v1.png\", capreal_dir],\n                    [textured_mesh_base_name + \".obj\", capreal_dir],\n                    [textured_mesh_base_name + \".mtl\", capreal_dir],\n                    [textured_mesh_base_name + \"_u1_v1.png\", capreal_dir] ]\nfor filename, directory_name in files_to_move:\n    source_file = os.path.join (path_data, filename)\n    destination_file = os.path.join (os.path.join (path_dest, directory_name), filename)\n    # print(\"Trying \",  source_folder + file , \" \", destination_folder + dir + file )\n    print(\"Trying \",  source_file , \"-->\", destination_file )\n    if (os.path.exists(source_file)):\n        print(\"Moving \",  source_file , \" \", destination_file )\n        shutil.copy( source_file , destination_file )\n\n################################################################################\n\n######################## CALCULATE CLIPPING PLANES #############################\n# clippingPlanes executable\nclipping_planes_app = getProcess(\"clippingPlanes\" + executables_suffix, executables_folder)\n\nclipping_planes_args = [clipping_planes_app, path_dest]\n\n# call clippingPlanes app\np_exit = subprocess.call(clipping_planes_args)\nprint(clipping_planes_app, \" exited with \", p_exit);\ncheckOutput(p_exit, False)\n\n################################################################################\n\n########################## CREATE LIST IMAGES ##################################\n\npath_images = os.path.join(path_dest, images_dir)\npath_list_images = os.path.join(path_images, \"list_images.txt\")\ngenerateListImages(path_images)\n\n################################################################################\n\n######################## CREATE SCENE METADATA #################################\n\n# read list image file\nlist_images = []\n\nif os.path.exists(path_list_images):\n    list_image_file = open(path_list_images, \"r\")\n\n    for line in list_image_file:\n        list_images.append(line)\n\n    list_image_file.close()\n\n# read clipping planes file\npath_clipping_planes = os.path.join(path_dest, \"clipping_planes.txt\")\nclipping_planes = []\n\nif os.path.exists(path_clipping_planes):\n    clipping_planes_file = open(path_clipping_planes, \"r\")\n\n    for line in clipping_planes_file:\n        clipping_planes.append(line)\n\n    clipping_planes_file.close()\n\n\n# Create scene metadata file from list image file\nscene_metadata = \"Scene Metadata File\\n\\n\"\n\nif len(list_images) == len(clipping_planes):\n    scene_metadata = scene_metadata + \"[list_images]\\n<filename> <image_width> <image_height> <near_clipping_plane> <far_clipping_plane>\\n\"\n    new_list = [a[:-1] + \" \" + b for a, b in zip(list_images, clipping_planes)]\n    for line in new_list:\n        scene_metadata = scene_metadata + line\n\nscene_metadata = scene_metadata + \"\\n\\n// Always specify active/exclude images after list images\\n\\n[exclude_images]\\n<image1_idx> <image2_idx> ... <image3_idx>\\n\"\n\n# if len(exclude) > 0:\n#     for line in exclude:\n#         scene_metadata = scene_metadata + str(line) + \" \"\n\nscene_metadata = scene_metadata + \"\\n\\n\\n[other parameters]\"\n\n\n# rename pmvs_recon.ply to recon.ply\nif (os.path.exists(os.path.join(path_dest, pmvs_model_dir, \"pmvs_recon.ply\"))):\n    shutil.copy(os.path.join(path_dest, pmvs_model_dir, \"pmvs_recon.ply\"), os.path.join(path_dest, pmvs_model_dir, \"recon.ply\"))\n\nif (os.path.exists(os.path.join(path_dest, pmvs_model_dir, \"mesh.ply\"))):\n    shutil.copy(os.path.join(path_dest, pmvs_model_dir, \"mesh.ply\"), os.path.join(path_dest, pmvs_model_dir, \"recon.ply\"))\n\npath_scene_metadata = os.path.join(path_dest, \"scene_metadata.txt\")\n\nscene_metadata_file = open(path_scene_metadata, \"w\")\nscene_metadata_file.write(scene_metadata)\nscene_metadata_file.close()\n\n################################################################################\n\nfor filename in os.listdir(path_data):\n    src = os.path.join(path_data, filename)\n    dst = os.path.join(path_dest, raw_data)\n    if not os.path.isdir(src):\n        shutil.copy(src, dst)\n\nprint(\"Fin.\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/meshlab/simplify.mlx",
    "content": "<!DOCTYPE FilterScript>\n<FilterScript>\n <filter name=\"Remove Duplicate Vertices\"/>\n <filter name=\"Remove Unreferenced Vertices\"/>\n <filter name=\"Simplification: Quadric Edge Collapse Decimation\">\n  <Param name=\"TargetFaceNum\" type=\"RichInt\" tooltip=\"The desired final number of faces.\" value=\"100000\" description=\"Target number of faces\"/>\n  <Param name=\"TargetPerc\" type=\"RichFloat\" tooltip=\"If non zero, this parameter specifies the desired final size of the mesh as a percentage of the initial size.\" value=\"0\" description=\"Percentage reduction (0..1)\"/>\n  <Param name=\"QualityThr\" type=\"RichFloat\" tooltip=\"Quality threshold for penalizing bad shaped faces.&lt;br>The value is in the range [0..1]&#xa; 0 accept any kind of face (no penalties),&#xa; 0.5  penalize faces with quality &lt; 0.5, proportionally to their shape&#xa;\" value=\"0.3\" description=\"Quality threshold\"/>\n  <Param name=\"PreserveBoundary\" type=\"RichBool\" tooltip=\"The simplification process tries to do not affect mesh boundaries during simplification\" value=\"false\" description=\"Preserve Boundary of the mesh\"/>\n  <Param name=\"BoundaryWeight\" type=\"RichFloat\" tooltip=\"The importance of the boundary during simplification. Default (1.0) means that the boundary has the same importance of the rest. Values greater than 1.0 raise boundary importance and has the effect of removing less vertices on the border. Admitted range of values (0,+inf). \" value=\"1\" description=\"Boundary Preserving Weight\"/>\n  <Param name=\"PreserveNormal\" type=\"RichBool\" tooltip=\"Try to avoid face flipping effects and try to preserve the original orientation of the surface\" value=\"false\" description=\"Preserve Normal\"/>\n  <Param name=\"PreserveTopology\" type=\"RichBool\" tooltip=\"Avoid all the collapses that should cause a topology change in the mesh (like closing holes, squeezing handles, etc). If checked the genus of the mesh should stay unchanged.\" value=\"false\" description=\"Preserve Topology\"/>\n  <Param name=\"OptimalPlacement\" type=\"RichBool\" tooltip=\"Each collapsed vertex is placed in the position minimizing the quadric error.&#xa; It can fail (creating bad spikes) in case of very flat areas. &#xa;If disabled edges are collapsed onto one of the two original vertices and the final mesh is composed by a subset of the original vertices. \" value=\"true\" description=\"Optimal position of simplified vertices\"/>\n  <Param name=\"PlanarQuadric\" type=\"RichBool\" tooltip=\"Add additional simplification constraints that improves the quality of the simplification of the planar portion of the mesh, as a side effect, more triangles will be preserved in flat areas (allowing better shaped triangles).\" value=\"false\" description=\"Planar Simplification\"/>\n  <Param name=\"PlanarWeight\" type=\"RichFloat\" tooltip=\"How much we should try to preserve the triangles in the planar regions. If you lower this value planar areas will be simplified more.\" value=\"0.001\" description=\"Planar Simp. Weight\"/>\n  <Param name=\"QualityWeight\" type=\"RichBool\" tooltip=\"Use the Per-Vertex quality as a weighting factor for the simplification. The weight is used as a error amplification value, so a vertex with a high quality value will not be simplified and a portion of the mesh with low quality values will be aggressively simplified.\" value=\"false\" description=\"Weighted Simplification\"/>\n  <Param name=\"AutoClean\" type=\"RichBool\" tooltip=\"After the simplification an additional set of steps is performed to clean the mesh (unreferenced vertices, bad faces, etc)\" value=\"true\" description=\"Post-simplification cleaning\"/>\n  <Param name=\"Selected\" type=\"RichBool\" tooltip=\"The simplification is applied only to the selected set of faces.&#xa; Take care of the target number of faces!\" value=\"false\" description=\"Simplify only selected faces\"/>\n </filter>\n</FilterScript>\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/meshlab/simplify200.mlx",
    "content": "<!DOCTYPE FilterScript>\n<FilterScript>\n <filter name=\"Remove Duplicate Vertices\"/>\n <filter name=\"Remove Unreferenced Vertices\"/>\n <filter name=\"Simplification: Quadric Edge Collapse Decimation\">\n  <Param name=\"TargetFaceNum\" type=\"RichInt\" tooltip=\"The desired final number of faces.\" value=\"200000\" description=\"Target number of faces\"/>\n  <Param name=\"TargetPerc\" type=\"RichFloat\" tooltip=\"If non zero, this parameter specifies the desired final size of the mesh as a percentage of the initial size.\" value=\"0\" description=\"Percentage reduction (0..1)\"/>\n  <Param name=\"QualityThr\" type=\"RichFloat\" tooltip=\"Quality threshold for penalizing bad shaped faces.&lt;br>The value is in the range [0..1]&#xa; 0 accept any kind of face (no penalties),&#xa; 0.5  penalize faces with quality &lt; 0.5, proportionally to their shape&#xa;\" value=\"0.3\" description=\"Quality threshold\"/>\n  <Param name=\"PreserveBoundary\" type=\"RichBool\" tooltip=\"The simplification process tries to do not affect mesh boundaries during simplification\" value=\"false\" description=\"Preserve Boundary of the mesh\"/>\n  <Param name=\"BoundaryWeight\" type=\"RichFloat\" tooltip=\"The importance of the boundary during simplification. Default (1.0) means that the boundary has the same importance of the rest. Values greater than 1.0 raise boundary importance and has the effect of removing less vertices on the border. Admitted range of values (0,+inf). \" value=\"1\" description=\"Boundary Preserving Weight\"/>\n  <Param name=\"PreserveNormal\" type=\"RichBool\" tooltip=\"Try to avoid face flipping effects and try to preserve the original orientation of the surface\" value=\"false\" description=\"Preserve Normal\"/>\n  <Param name=\"PreserveTopology\" type=\"RichBool\" tooltip=\"Avoid all the collapses that should cause a topology change in the mesh (like closing holes, squeezing handles, etc). If checked the genus of the mesh should stay unchanged.\" value=\"false\" description=\"Preserve Topology\"/>\n  <Param name=\"OptimalPlacement\" type=\"RichBool\" tooltip=\"Each collapsed vertex is placed in the position minimizing the quadric error.&#xa; It can fail (creating bad spikes) in case of very flat areas. &#xa;If disabled edges are collapsed onto one of the two original vertices and the final mesh is composed by a subset of the original vertices. \" value=\"true\" description=\"Optimal position of simplified vertices\"/>\n  <Param name=\"PlanarQuadric\" type=\"RichBool\" tooltip=\"Add additional simplification constraints that improves the quality of the simplification of the planar portion of the mesh, as a side effect, more triangles will be preserved in flat areas (allowing better shaped triangles).\" value=\"false\" description=\"Planar Simplification\"/>\n  <Param name=\"PlanarWeight\" type=\"RichFloat\" tooltip=\"How much we should try to preserve the triangles in the planar regions. If you lower this value planar areas will be simplified more.\" value=\"0.001\" description=\"Planar Simp. Weight\"/>\n  <Param name=\"QualityWeight\" type=\"RichBool\" tooltip=\"Use the Per-Vertex quality as a weighting factor for the simplification. The weight is used as a error amplification value, so a vertex with a high quality value will not be simplified and a portion of the mesh with low quality values will be aggressively simplified.\" value=\"false\" description=\"Weighted Simplification\"/>\n  <Param name=\"AutoClean\" type=\"RichBool\" tooltip=\"After the simplification an additional set of steps is performed to clean the mesh (unreferenced vertices, bad faces, etc)\" value=\"true\" description=\"Post-simplification cleaning\"/>\n  <Param name=\"Selected\" type=\"RichBool\" tooltip=\"The simplification is applied only to the selected set of faces.&#xa; Take care of the target number of faces!\" value=\"false\" description=\"Simplify only selected faces\"/>\n </filter>\n</FilterScript>\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/meshlab/simplify250.mlx",
    "content": "<!DOCTYPE FilterScript>\n<FilterScript>\n <filter name=\"Remove Duplicate Vertices\"/>\n <filter name=\"Remove Unreferenced Vertices\"/>\n <filter name=\"Simplification: Quadric Edge Collapse Decimation\">\n  <Param name=\"TargetFaceNum\" type=\"RichInt\" tooltip=\"The desired final number of faces.\" value=\"250000\" description=\"Target number of faces\"/>\n  <Param name=\"TargetPerc\" type=\"RichFloat\" tooltip=\"If non zero, this parameter specifies the desired final size of the mesh as a percentage of the initial size.\" value=\"0\" description=\"Percentage reduction (0..1)\"/>\n  <Param name=\"QualityThr\" type=\"RichFloat\" tooltip=\"Quality threshold for penalizing bad shaped faces.&lt;br>The value is in the range [0..1]&#xa; 0 accept any kind of face (no penalties),&#xa; 0.5  penalize faces with quality &lt; 0.5, proportionally to their shape&#xa;\" value=\"0.3\" description=\"Quality threshold\"/>\n  <Param name=\"PreserveBoundary\" type=\"RichBool\" tooltip=\"The simplification process tries to do not affect mesh boundaries during simplification\" value=\"false\" description=\"Preserve Boundary of the mesh\"/>\n  <Param name=\"BoundaryWeight\" type=\"RichFloat\" tooltip=\"The importance of the boundary during simplification. Default (1.0) means that the boundary has the same importance of the rest. Values greater than 1.0 raise boundary importance and has the effect of removing less vertices on the border. Admitted range of values (0,+inf). \" value=\"1\" description=\"Boundary Preserving Weight\"/>\n  <Param name=\"PreserveNormal\" type=\"RichBool\" tooltip=\"Try to avoid face flipping effects and try to preserve the original orientation of the surface\" value=\"false\" description=\"Preserve Normal\"/>\n  <Param name=\"PreserveTopology\" type=\"RichBool\" tooltip=\"Avoid all the collapses that should cause a topology change in the mesh (like closing holes, squeezing handles, etc). If checked the genus of the mesh should stay unchanged.\" value=\"false\" description=\"Preserve Topology\"/>\n  <Param name=\"OptimalPlacement\" type=\"RichBool\" tooltip=\"Each collapsed vertex is placed in the position minimizing the quadric error.&#xa; It can fail (creating bad spikes) in case of very flat areas. &#xa;If disabled edges are collapsed onto one of the two original vertices and the final mesh is composed by a subset of the original vertices. \" value=\"true\" description=\"Optimal position of simplified vertices\"/>\n  <Param name=\"PlanarQuadric\" type=\"RichBool\" tooltip=\"Add additional simplification constraints that improves the quality of the simplification of the planar portion of the mesh, as a side effect, more triangles will be preserved in flat areas (allowing better shaped triangles).\" value=\"false\" description=\"Planar Simplification\"/>\n  <Param name=\"PlanarWeight\" type=\"RichFloat\" tooltip=\"How much we should try to preserve the triangles in the planar regions. If you lower this value planar areas will be simplified more.\" value=\"0.001\" description=\"Planar Simp. Weight\"/>\n  <Param name=\"QualityWeight\" type=\"RichBool\" tooltip=\"Use the Per-Vertex quality as a weighting factor for the simplification. The weight is used as a error amplification value, so a vertex with a high quality value will not be simplified and a portion of the mesh with low quality values will be aggressively simplified.\" value=\"false\" description=\"Weighted Simplification\"/>\n  <Param name=\"AutoClean\" type=\"RichBool\" tooltip=\"After the simplification an additional set of steps is performed to clean the mesh (unreferenced vertices, bad faces, etc)\" value=\"true\" description=\"Post-simplification cleaning\"/>\n  <Param name=\"Selected\" type=\"RichBool\" tooltip=\"The simplification is applied only to the selected set of faces.&#xa; Take care of the target number of faces!\" value=\"false\" description=\"Simplify only selected faces\"/>\n </filter>\n</FilterScript>\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/meshlab/simplify300.mlx",
    "content": "<!DOCTYPE FilterScript>\n<FilterScript>\n <filter name=\"Remove Duplicate Vertices\"/>\n <filter name=\"Remove Unreferenced Vertices\"/>\n <filter name=\"Simplification: Quadric Edge Collapse Decimation\">\n  <Param name=\"TargetFaceNum\" type=\"RichInt\" tooltip=\"The desired final number of faces.\" value=\"300000\" description=\"Target number of faces\"/>\n  <Param name=\"TargetPerc\" type=\"RichFloat\" tooltip=\"If non zero, this parameter specifies the desired final size of the mesh as a percentage of the initial size.\" value=\"0\" description=\"Percentage reduction (0..1)\"/>\n  <Param name=\"QualityThr\" type=\"RichFloat\" tooltip=\"Quality threshold for penalizing bad shaped faces.&lt;br>The value is in the range [0..1]&#xa; 0 accept any kind of face (no penalties),&#xa; 0.5  penalize faces with quality &lt; 0.5, proportionally to their shape&#xa;\" value=\"0.3\" description=\"Quality threshold\"/>\n  <Param name=\"PreserveBoundary\" type=\"RichBool\" tooltip=\"The simplification process tries to do not affect mesh boundaries during simplification\" value=\"false\" description=\"Preserve Boundary of the mesh\"/>\n  <Param name=\"BoundaryWeight\" type=\"RichFloat\" tooltip=\"The importance of the boundary during simplification. Default (1.0) means that the boundary has the same importance of the rest. Values greater than 1.0 raise boundary importance and has the effect of removing less vertices on the border. Admitted range of values (0,+inf). \" value=\"1\" description=\"Boundary Preserving Weight\"/>\n  <Param name=\"PreserveNormal\" type=\"RichBool\" tooltip=\"Try to avoid face flipping effects and try to preserve the original orientation of the surface\" value=\"false\" description=\"Preserve Normal\"/>\n  <Param name=\"PreserveTopology\" type=\"RichBool\" tooltip=\"Avoid all the collapses that should cause a topology change in the mesh (like closing holes, squeezing handles, etc). If checked the genus of the mesh should stay unchanged.\" value=\"false\" description=\"Preserve Topology\"/>\n  <Param name=\"OptimalPlacement\" type=\"RichBool\" tooltip=\"Each collapsed vertex is placed in the position minimizing the quadric error.&#xa; It can fail (creating bad spikes) in case of very flat areas. &#xa;If disabled edges are collapsed onto one of the two original vertices and the final mesh is composed by a subset of the original vertices. \" value=\"true\" description=\"Optimal position of simplified vertices\"/>\n  <Param name=\"PlanarQuadric\" type=\"RichBool\" tooltip=\"Add additional simplification constraints that improves the quality of the simplification of the planar portion of the mesh, as a side effect, more triangles will be preserved in flat areas (allowing better shaped triangles).\" value=\"false\" description=\"Planar Simplification\"/>\n  <Param name=\"PlanarWeight\" type=\"RichFloat\" tooltip=\"How much we should try to preserve the triangles in the planar regions. If you lower this value planar areas will be simplified more.\" value=\"0.001\" description=\"Planar Simp. Weight\"/>\n  <Param name=\"QualityWeight\" type=\"RichBool\" tooltip=\"Use the Per-Vertex quality as a weighting factor for the simplification. The weight is used as a error amplification value, so a vertex with a high quality value will not be simplified and a portion of the mesh with low quality values will be aggressively simplified.\" value=\"false\" description=\"Weighted Simplification\"/>\n  <Param name=\"AutoClean\" type=\"RichBool\" tooltip=\"After the simplification an additional set of steps is performed to clean the mesh (unreferenced vertices, bad faces, etc)\" value=\"true\" description=\"Post-simplification cleaning\"/>\n  <Param name=\"Selected\" type=\"RichBool\" tooltip=\"The simplification is applied only to the selected set of faces.&#xa; Take care of the target number of faces!\" value=\"false\" description=\"Simplify only selected faces\"/>\n </filter>\n</FilterScript>\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/meshlab/simplify350.mlx",
    "content": "<!DOCTYPE FilterScript>\n<FilterScript>\n <filter name=\"Remove Duplicate Vertices\"/>\n <filter name=\"Remove Unreferenced Vertices\"/>\n <filter name=\"Simplification: Quadric Edge Collapse Decimation\">\n  <Param name=\"TargetFaceNum\" type=\"RichInt\" tooltip=\"The desired final number of faces.\" value=\"250000\" description=\"Target number of faces\"/>\n  <Param name=\"TargetPerc\" type=\"RichFloat\" tooltip=\"If non zero, this parameter specifies the desired final size of the mesh as a percentage of the initial size.\" value=\"0\" description=\"Percentage reduction (0..1)\"/>\n  <Param name=\"QualityThr\" type=\"RichFloat\" tooltip=\"Quality threshold for penalizing bad shaped faces.&lt;br>The value is in the range [0..1]&#xa; 0 accept any kind of face (no penalties),&#xa; 0.5  penalize faces with quality &lt; 0.5, proportionally to their shape&#xa;\" value=\"0.3\" description=\"Quality threshold\"/>\n  <Param name=\"PreserveBoundary\" type=\"RichBool\" tooltip=\"The simplification process tries to do not affect mesh boundaries during simplification\" value=\"false\" description=\"Preserve Boundary of the mesh\"/>\n  <Param name=\"BoundaryWeight\" type=\"RichFloat\" tooltip=\"The importance of the boundary during simplification. Default (1.0) means that the boundary has the same importance of the rest. Values greater than 1.0 raise boundary importance and has the effect of removing less vertices on the border. Admitted range of values (0,+inf). \" value=\"1\" description=\"Boundary Preserving Weight\"/>\n  <Param name=\"PreserveNormal\" type=\"RichBool\" tooltip=\"Try to avoid face flipping effects and try to preserve the original orientation of the surface\" value=\"false\" description=\"Preserve Normal\"/>\n  <Param name=\"PreserveTopology\" type=\"RichBool\" tooltip=\"Avoid all the collapses that should cause a topology change in the mesh (like closing holes, squeezing handles, etc). If checked the genus of the mesh should stay unchanged.\" value=\"false\" description=\"Preserve Topology\"/>\n  <Param name=\"OptimalPlacement\" type=\"RichBool\" tooltip=\"Each collapsed vertex is placed in the position minimizing the quadric error.&#xa; It can fail (creating bad spikes) in case of very flat areas. &#xa;If disabled edges are collapsed onto one of the two original vertices and the final mesh is composed by a subset of the original vertices. \" value=\"true\" description=\"Optimal position of simplified vertices\"/>\n  <Param name=\"PlanarQuadric\" type=\"RichBool\" tooltip=\"Add additional simplification constraints that improves the quality of the simplification of the planar portion of the mesh, as a side effect, more triangles will be preserved in flat areas (allowing better shaped triangles).\" value=\"false\" description=\"Planar Simplification\"/>\n  <Param name=\"PlanarWeight\" type=\"RichFloat\" tooltip=\"How much we should try to preserve the triangles in the planar regions. If you lower this value planar areas will be simplified more.\" value=\"0.001\" description=\"Planar Simp. Weight\"/>\n  <Param name=\"QualityWeight\" type=\"RichBool\" tooltip=\"Use the Per-Vertex quality as a weighting factor for the simplification. The weight is used as a error amplification value, so a vertex with a high quality value will not be simplified and a portion of the mesh with low quality values will be aggressively simplified.\" value=\"false\" description=\"Weighted Simplification\"/>\n  <Param name=\"AutoClean\" type=\"RichBool\" tooltip=\"After the simplification an additional set of steps is performed to clean the mesh (unreferenced vertices, bad faces, etc)\" value=\"true\" description=\"Post-simplification cleaning\"/>\n  <Param name=\"Selected\" type=\"RichBool\" tooltip=\"The simplification is applied only to the selected set of faces.&#xa; Take care of the target number of faces!\" value=\"false\" description=\"Simplify only selected faces\"/>\n </filter>\n</FilterScript>\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/meshlab/wedge_to_vertex_uvs.mlx",
    "content": "<!DOCTYPE FilterScript>\n<FilterScript>\n <filter name=\"Convert PerWedge UV into PerVertex UV\"/>\n</FilterScript>\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/simplify_mesh.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\n\"\"\" @package dataset_tools_preprocess\nThis script calls meshlab to simplify a mesh\n\nParameters: -h help,\n            -inputMesh <the mesh to simplify>,\n            -outputMesh <the output mesh>,\n            -meshlabPath <Meshlab binary directory>\n\nUsage: python simplify_mesh.py --inputMesh <the mesh to simplify>\n                               --outputMesh <the output mesh>\n                               --meshlabPath <Meshlab binary directory>\n                               --meshsize <size of the output mesh in K polygons (ie 200 == 200,000 polygons). Values allowed: 200, 250, 300, 350, 400>\n\n\"\"\"\n\nimport os, sys\nimport argparse\nfrom utils.commands import runCommand, getMeshlabServer\nfrom utils.paths import getMeshlabPath\n\ndef simplifyMesh(inputMesh, outputMesh, meshsize=\"\", meshlabPath = getMeshlabPath()):\n    mlxFileEnd = 'meshlab/simplify.mlx'\n\n    if( meshsize != \"\" ):\n        if( meshsize == \"200\"):\n            mlxFileEnd = 'meshlab/simplify200.mlx'\n        elif( meshsize == \"250\"):\n            mlxFileEnd = 'meshlab/simplify250.mlx'\n        elif( meshsize == \"300\"):\n            mlxFileEnd = 'meshlab/simplify300.mlx'\n        elif( meshsize == \"350\"):\n            mlxFileEnd = 'meshlab/simplify350.mlx'\n        elif( meshsize == \"400\"):\n            mlxFileEnd = 'meshlab/simplify400.mlx'\n\n\n    mlxFile = os.path.abspath(os.path.join(os.path.abspath(os.path.dirname(__file__)), mlxFileEnd))\n\n    return runCommand(getMeshlabServer(meshlabPath), ['-i', inputMesh,\n                                                      '-o', outputMesh,\n                                                      '-s', mlxFile])\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--inputMesh\", type=str, required=True, help=\"the mesh to simplify\")\n    parser.add_argument(\"--outputMesh\", type=str, required=True, help=\"the output mesh\")\n    parser.add_argument(\"--meshlabPath\", type=str, default=getMeshlabPath(), help=\"Meshlab binary directory\")\n    parser.add_argument(\"--meshsize\", type=str, help=\"size of the output mesh in K polygons (ie 200 == 200,000 polygons). Values allowed: 200, 250, 300, 350, 400\")\n\n    args = vars(parser.parse_args())\n\n    return simplifyMesh(args['inputMesh'], args['outputMesh'], args['meshsize'], args['meshlabPath'])\n\n#    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/converters/wedge_to_vertex_uvs.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\n\"\"\" @package dataset_tools_preprocess\nThis script calls meshlab to simplify a mesh\n\nParameters: -h help,\n            -inputMesh <the mesh to simplify>,\n            -outputMesh <the output mesh>,\n            -meshlabPath <Meshlab binary directory>\n\nUsage: python wedge_to_vertex_uvs.py -inputMesh <the mesh to convert>\n                               -outputMesh <the output mesh>\n                               -meshlabPath <Meshlab binary directory>\n\n\"\"\"\n\nimport os, sys\nimport argparse\nfrom utils.commands import runCommand, getMeshlabServer\nfrom utils.paths import getMeshlabPath\n\ndef convertUVs(inputMesh, outputMesh, meshlabPath = getMeshlabPath()):\n    mlxFile = os.path.abspath(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'meshlab/wedge_to_vertex_uvs.mlx'))\n\n    ret = runCommand(getMeshlabServer(meshlabPath), ['-i', inputMesh,\n                                                     '-o', outputMesh,\n                                                     '-m', 'vt',\n                                                     '-s', mlxFile])\n    return ret\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--inputMesh\", type=str, required=True, help=\"the mesh to simplify\")\n    parser.add_argument(\"--outputMesh\", type=str, required=True, help=\"the output mesh\")\n    parser.add_argument(\"--meshlabPath\", type=str, default=getMeshlabPath(), help=\"Meshlab binary directory\")\n\n    args = vars(parser.parse_args())\n\n    ret = convertUVs(args['inputMesh'], args['outputMesh'], args['meshlabPath'])\n    if( ret.returncode != 0 ):\n        print(\"SIBR_ERROR meshlab error in converting UVs\")\n        sys.exit(1)\n\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/cropFromCenter/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(cropFromCenter)\n\n# libraries used:\n# * sibr graphics (vector2i class)\n# * sibr system (programArg class)\n# * boost filesystem (path class)\n# * openmp\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\tsibr_system\n    sibr_graphics\n\tsibr_imgproc\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/cropFromCenter/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <core/imgproc/CropScaleImageUtility.hpp>\n#include <core/system/CommandLineArgs.hpp>\n\n\n\n/*\nCrop input images from center so they end up with resolution <crop_width> x <crop_height>\nif scale down factor is also passed, after the image has been cropped, it will be scaled down by that value\n*/\nconst char* USAGE = \"Usage: cropFromCenter --inputFile <path_to_input_file> --outputPath <path_to_output_folder> --avgResolution <width x height> --cropResolution <width x height> [--scaleDownFactor <alpha> --targetResolution <width x height>] \\n\";\n//const char* USAGE\t\t\t\t\t\t= \"Usage: cropFromCenter --inputFile <path_to_input_file> --outputPath <path_to_output_folder> --avgResolution <width x height> --cropResolution <widht x height> [--scaleDownFactor <alpha> --targetResolution <width x height>] \\n\";\nconst char* TAG = \"[cropFromCenter]\";\nconst unsigned PROCESSING_BATCH_SIZE = 150;\nconst char* LOG_FILE_NAME = \"cropFromCenter.log\";\nconst char* SCALED_DOWN_SUBFOLDER = \"scaled\";\nconst char* SCALED_DOWN_FILENAME = \"scale_factor.txt\";\n\nstruct CropAppArgs :\n\tvirtual sibr::BasicIBRAppArgs {\n\tsibr::Arg<std::string> inputFileArg = { \"inputFile\", \"\" };\n\tsibr::Arg<std::string> outputFolderArg = { \"outputPath\", \"\" };\n\tsibr::Arg<sibr::Vector2i> avgResolutionArg = { \"avgResolution\",{ 0, 0 } };\n\tsibr::Arg<sibr::Vector2i> cropResolutionArg = { \"cropResolution\",{ 0, 0 } };\n\tsibr::Arg<float> scaleDownFactorArg = { \"scaleDownFactor\", 0.0f };\n\tsibr::Arg<sibr::Vector2i> targetResolutionArg = { \"targetResolution\",{ 0, 0 } };\n};\n\nvoid printUsage()\n{\n\tstd::cout << USAGE << std::endl;\n}\n\n\nbool getParamas(int argc, const char ** argv,\n\tstd::string & inputFile, boost::filesystem::path & outputPath,\n\tsibr::Vector2i & avgResolution, sibr::Vector2i & cropResolution, float & scaleDownFactor, sibr::Vector2i & targetResolution)\n{\n\n\tsibr::CommandLineArgs::parseMainArgs(argc, argv);\n\tCropAppArgs myArgs;\n\n\tinputFile = myArgs.inputFileArg;\n\n\tstd::string outputFolder = myArgs.outputFolderArg;\n\toutputPath = outputFolder;\n\n\tavgResolution = myArgs.avgResolutionArg;\n\n\tcropResolution = myArgs.cropResolutionArg;\n\n\t// optional parameters\n\tif (myArgs.scaleDownFactorArg != 0.0f) {\n\t\tscaleDownFactor = myArgs.scaleDownFactorArg;\n\t}\n\n\tif (myArgs.targetResolutionArg.get() != sibr::Vector2i(0, 0)) {\n\t\ttargetResolution = myArgs.targetResolutionArg;\n\t}\n\n\n\tif (inputFile.empty() || outputFolder.empty() || avgResolution == sibr::Vector2i(0, 0) || cropResolution == sibr::Vector2i(0, 0)) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\nint main(const int argc, const char** argv)\n{\n\t// process parameters\n\tstd::string\t\t\t\t\tinputFileName;\n\tboost::filesystem::path\t\toutputFolder;\n\tboost::filesystem::path\t\tscaledDownOutputFolder;\n\tsibr::Vector2i\t\t\t\tavgInitialResolution;\t\t// just for statistics and log file\n\tsibr::Vector2i\t\t\t\tcropResolution;\n\tfloat\t\t\t\t\t\tscaleDownFactor = 0.f;\n\tsibr::Vector2i\t\t\t\ttargetResolution;\n\n\tsibr::CropScaleImageUtility appUtility;\n\n\tif (!getParamas(argc, argv, inputFileName, outputFolder, avgInitialResolution, cropResolution, scaleDownFactor, targetResolution)) {\n\t\tstd::cerr << TAG << \" ERROR: wrong parameters.\\n\";\n\t\tprintUsage();\n\t\treturn -1;\n\t}\n\n\tscaledDownOutputFolder = (outputFolder / SCALED_DOWN_SUBFOLDER);\n\n\tbool scaleDown = (scaleDownFactor > 0);\n\t//cv::Size resizedSize (finalResolution[0], cropResolution[1] * ((float)(finalResolution[0]) / cropResolution[0]));\n\tcv::Size resizedSize(int(cropResolution[0] * scaleDownFactor), int(cropResolution[1] * scaleDownFactor));\n\n\n\tif (!boost::filesystem::exists(outputFolder))\n\t{\n\t\tboost::filesystem::create_directory(outputFolder);\n\t}\n\n\tif (scaleDown && !boost::filesystem::exists(scaledDownOutputFolder)) {\n\t\tboost::filesystem::create_directory(scaledDownOutputFolder);\n\t}\n\n\t// read input file\n\tstd::vector<std::string> pathToImgs = appUtility.getPathToImgs(inputFileName);\n\tstd::vector<sibr::CropScaleImageUtility::Image> listOfImages(pathToImgs.size());\n\tstd::vector<sibr::CropScaleImageUtility::Image> listOfImagesScaledDown(scaleDown ? pathToImgs.size() : 0);\n\n\t// calculate nr batches\n\tconst int nrBatches = static_cast<int>(ceil((float)(pathToImgs.size()) / PROCESSING_BATCH_SIZE));\n\n\tstd::chrono::time_point <std::chrono::system_clock> start, end;\n\tstart = std::chrono::system_clock::now();\n\n\tconst int batchSize = int(PROCESSING_BATCH_SIZE);\n\t// run batches sequentially\n\tfor (int batchId = 0; batchId < nrBatches; batchId++) {\n\n\t\tconst int nrItems = (batchId != nrBatches - 1) ? batchSize : ((nrBatches * batchSize != int(pathToImgs.size())) ? (int(pathToImgs.size()) - (batchSize * batchId)) : batchSize);\n\n\t\t#pragma omp parallel for\n\t\tfor (int localImgIndex = 0; localImgIndex < nrItems; localImgIndex++) {\n\n\t\t\tconst int globalImgIndex = (batchId * batchSize) + localImgIndex;\n\n\t\t\t// using next code will keep filename in output directory\n\t\t\tboost::filesystem::path boostPath(pathToImgs[globalImgIndex]);\n\t\t\t//std::string outputFileName = (outputFolder / boostPath.filename()).string();\n\n\t\t\tstd::stringstream ss;\n\t\t\tss << std::setfill('0') << std::setw(8) << globalImgIndex << boostPath.extension().string();\n\t\t\tstd::string outputFileName = (outputFolder / ss.str()).string();\n\t\t\tstd::string scaledDownOutputFileName = (scaledDownOutputFolder / ss.str()).string();\n\n\t\t\tcv::Mat img = cv::imread(pathToImgs[globalImgIndex], 1);\n\n\t\t\tcv::Rect areOfIntererst = cv::Rect((img.cols - cropResolution[0]) / 2, (img.rows - cropResolution[1]) / 2, cropResolution[0], cropResolution[1]);\n\n\t\t\tcv::Mat croppedImg = img(areOfIntererst);\n\n\t\t\tcv::imwrite(outputFileName, croppedImg);\n\n\t\t\tlistOfImages[globalImgIndex].filename = ss.str();\n\t\t\tlistOfImages[globalImgIndex].width = croppedImg.cols;\n\t\t\tlistOfImages[globalImgIndex].height = croppedImg.rows;\n\n\t\t\tif (scaleDown) {\n\t\t\t\tcv::Mat resizedImg;\n\t\t\t\tcv::resize(croppedImg, resizedImg, resizedSize, 0, 0, cv::INTER_LINEAR);\n\n\t\t\t\tcv::imwrite(scaledDownOutputFileName, resizedImg);\n\n\t\t\t\tlistOfImagesScaledDown[globalImgIndex].filename\t= ss.str();\n\t\t\t\tlistOfImagesScaledDown[globalImgIndex].width\t= resizedImg.cols;\n\t\t\t\tlistOfImagesScaledDown[globalImgIndex].height\t= resizedImg.rows;\n\t\t\t}\n\t\t}\n\t}\n\n\tend = std::chrono::system_clock::now();\n\tauto elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(end - start).count();\n\n\tstd::cout << TAG << \" elapsed time=\" << elapsedTime << \"s.\\n\";\n\n\tappUtility.logExecution(avgInitialResolution, int(pathToImgs.size()), elapsedTime, scaleDown, LOG_FILE_NAME);\n\n\t// write list_images.txt\n\tappUtility.writeListImages((outputFolder / \"list_images.txt\").string(), listOfImages);\n\n\t// write list_images.txt and scale_factor in scaled down directoy if needed\n\tif (scaleDown) {\n\t\tappUtility.writeListImages((scaledDownOutputFolder / \"list_images.txt\").string(), listOfImagesScaledDown);\n\t\tappUtility.writeScaleFactor((scaledDownOutputFolder / SCALED_DOWN_FILENAME).string(), scaleDownFactor);\n\n\t\tif (targetResolution != sibr::Vector2i(0, 0)) {\n\t\t\tappUtility.writeTargetResolution((scaledDownOutputFolder / \"target_resolution.txt\").string(), targetResolution);\n\t\t}\n\t}\n\n\treturn 0;\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/distordCrop/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n# project name\nproject(distordCrop)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} ${SOURCES})\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n    OpenMP::OpenMP_CXX\n    sibr_system\n    sibr_graphics\n    sibr_imgproc\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\nif (WIN32)\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} /wd4251\")\nendif()\n\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/distordCrop/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <core/imgproc/DistordCropUtility.hpp>\n#include <core/system/CommandLineArgs.hpp>\n\ntypedef boost::filesystem::path Path;\nusing namespace boost::filesystem;\n\nint threshold_black_color = 10; //10\nint thinest_bounding_box_size = 5;\nint threshold_bounding_box_size = 500;\nfloat threshold_ratio_bounding_box_size = 0.2f;\n\nconst int PROCESSING_BATCH_SIZE = 150;\t// process PROCESSING_BATCH_SIZE images together\n\nsibr::Vector3i backgroundColor = sibr::Vector3i(0, 0, 0);\n\n/*\n\tif input image resolution is too different from the avg, it will be discarded automatically at the beginning\n*/\nfloat resolutionThreshold = 0.15f;\n\n/*\n\ttolerance factor is used to allow somehow some black borders in the final images.\n\tif tolerance factor is zero, then all black borders are remove.\n\tif tolerance factor is one, then the image keeps its original resolution\n*/\nfloat toleranceFactor = 0.0f;\n\nbool debug_viz = false;\n\nstruct DistordCropAppArgs :\n\tvirtual sibr::BasicIBRAppArgs {\n\tsibr::Arg<int> black_threshold = { \"black\", threshold_black_color };\n\tsibr::Arg<int> minSizeThresholdArg = { \"min\", threshold_bounding_box_size};\n\tsibr::Arg<float> minRatioThresholdArg = { \"ratio\", threshold_ratio_bounding_box_size };\n\tsibr::Arg<float> resThreshold = { \"resolution_threshold\", 0.15f };\n\tsibr::Arg<float> toleranceArg = { \"tolerance\", toleranceFactor };\n\tsibr::Arg<bool> vizArg = { \"debug\" };\n\tsibr::ArgSwitch modeArg = { \"modesame\", true };\n\tsibr::Arg<int> avgWidthArg = { \"avg_width\", 0 };\n\tsibr::Arg<int> avgHeightArg = { \"avg_height\", 0 };\n\tsibr::Arg<sibr::Vector3i> backgroundColor = { \"backgroundColor\", sibr::Vector3i(0, 0, 0) };\n};\n\n\n/*\nutility program that determines a new resolution taking into account that some input images have black borders added by reality capture.\nthe second output of the program [optional] is a excludeImages.txt file containing the id of the images that didn't pass the threshold test\n(they would have to be cropped to much). current pipeline (IBR_recons_RC.py) doesn't used that file properly.\n\nwe might need to call process_cam_selection manually passing as argument the excludeImages.txt in order to actually remove the cameras that\ndidn't pass the threshold test\n\nupdate: reality capture (using the 'fit' option when exporting bundle) sometimes produces datasets that have images not only with black borders\nbut also with a completely different resolution. We need to take into account those datasets too.\n*/\n\n\nusing namespace sibr;\n\n\nint main(const int argc, const char* const* argv)\n{\n\t// parameters stuff\n\tsibr::CommandLineArgs::parseMainArgs(argc, argv);\n\tDistordCropAppArgs myArgs;\n\n\tDistordCropUtility appUtils;\n\n\tstd::string datasetPath = myArgs.dataset_path;\n\n\tthreshold_black_color = myArgs.black_threshold;\n\tthreshold_bounding_box_size = myArgs.minSizeThresholdArg;\n\tthreshold_ratio_bounding_box_size = myArgs.minRatioThresholdArg;\n\ttoleranceFactor = myArgs.toleranceArg;\n\tbackgroundColor = myArgs.backgroundColor;\t\n\tresolutionThreshold = myArgs.resThreshold;\n\n\tif( myArgs.vizArg.get()) {\n\t\tdebug_viz = true;\n\t}\n\t\n\tint avgWidth = myArgs.avgWidthArg;\n\tint avgHeight = myArgs.avgHeightArg;\n\t\n\tbool sameSize = myArgs.modeArg;\n\t// end parameters stuff\n\n\tPath root(datasetPath);\n\n\tstd::cout << \"[distordCrop] looking for input images : \" << std::endl;\n\tstd::vector< Path > imagePaths;\n\tdirectory_iterator it(root), eod;\n\tstd::vector<sibr::Vector2i> resolutions;\n\n\tBOOST_FOREACH(Path const &p, std::make_pair(it, eod)) {\n\t\tif (is_regular_file(p) && ( p.extension() == \".jpg\" || p.extension() == \".JPG\" || p.extension() == \".PNG\" || p.extension() == \".png\" ) && appUtils.is_number(p.stem().string())) {\n\n\t\t\tstd::cout << \"\\t \" << p.filename().string() << std::endl;\n\t\t\timagePaths.push_back(p);\n\t\t}\n\t\telse if (is_regular_file(p) && p.extension() == \".txt\" && p.stem().string() == \"resolutions\") {\n\t\t\t\n\t\t\t// read resolutions file\n\t\t\tifstream inputFile(p.string());\n\n\t\t\tstd::string line;\n\t\t\twhile (getline(inputFile, line)) {\n\t\t\t\tstd::stringstream iss(line);\n\t\t\t\tstd::string pathToImg;\n\t\t\t\tstd::string widthStr;\n\t\t\t\tstd::string heightStr;\n\n\t\t\t\tgetline(iss, pathToImg, '\\t');\n\t\t\t\tgetline(iss, widthStr, '\\t');\n\t\t\t\tgetline(iss, heightStr, '\\n');\n\n\t\t\t\tsibr::Vector2i res(std::stoi(widthStr), std::stoi(heightStr));\n\n\t\t\t\tresolutions.push_back(res);\n\n\t\t\t}\n\n\t\t\tinputFile.close();\n\t\t}\n\t}\n\n\tif (resolutions.size() == 0) {\n\t\tstd::cout << \"[distordCrop] WARNING : no resolution.txt file found\" << std::endl;\n\t\treturn 0;\n\t}\n\n\tif (imagePaths.size() == 0) {\n\t\tstd::cout << \"[distordCrop] WARNING: no images found: need .jpg,.JPG,.png,.PNG \" << std::endl;\n\t\treturn 0;\n\t}\n\n\tif (resolutions.size() != imagePaths.size()) {\n\t\tstd::cout << \"[distordCrop] WARNING : different number of input images and resolutions written in resolutions.txt\" << std::endl;\n\t\treturn 0;\n\t}\n\n\tint minWidth, minHeight, new_half_w, new_half_h;\n\t\n\tif (sameSize) {\n\t\tstd::cout << \" ALL IMG SHOULD HAVE SAME SIZE \" << std::endl;\n\t\tsibr::Vector2i minSize = appUtils.findBiggestImageCenteredBox(root, imagePaths, resolutions, avgWidth, avgHeight, \n\t\t\tPROCESSING_BATCH_SIZE, resolutionThreshold, threshold_ratio_bounding_box_size, backgroundColor, \n\t\t\tthreshold_black_color, thinest_bounding_box_size, toleranceFactor);\n\n\t\tstd::cout << \"[distordCrop] minSize \" << minSize[0] << \"x\" << minSize[1] << std::endl;\n\t\tminWidth = minSize[0];\n\t\tminHeight = minSize[1];\n\t} else {\n\t\tstd::cout << \" ALL IMG SHOULD NOT HAVE SAME SIZE \" << std::endl;\n\t\tsibr::Vector2i minSize = appUtils.findMinImageSize(root, imagePaths);\n\t\tminWidth = minSize[0];\n\t\tminHeight = minSize[1];\n\t}\n\n\tnew_half_w = (minWidth % 2 == 0) ? (minWidth / 2) : (--minWidth / 2);\n\tnew_half_h = (minHeight % 2 == 0) ? (minHeight / 2) : (--minHeight / 2);\n\n\twhile ((new_half_w % 4) != 0) { --new_half_w; }\n\twhile ((new_half_h % 4) != 0) { --new_half_h; }\n\n\tstd::string outputFilePath = root.string() + \"/cropNewSize.txt\";\n\tstd::ofstream file(outputFilePath, std::ios::trunc);\n\tif (file) {\n\t\tfile << 2 * new_half_w << \" \" << 2 * new_half_h;\n\t\tfile.close(); \n\t}\n\telse {\n\t\tstd::cout << \"[distordCrop]  ERROR cant open file : \" << outputFilePath << std::endl;\n\t\treturn 1;\n\t}\n\n\tstd::cout << \"[distordCrop] done, new size is \" << 2 * new_half_w << \" x \" << 2 * new_half_h << std::endl;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(fullColmapProcess)\n\nfile(GLOB SCRIPTS \"*.py\" \"*.json\")\n\nadd_custom_target(${PROJECT_NAME} ALL)\n\ninclude(install_runtime)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\nibr_install_rsc(${PROJECT_NAME} TYPE \"scripts\" FILES ${SCRIPTS})\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/ColmapProcessSteps.json",
    "content": "{\n    \"steps\" : [\n        {\n            \"name\": \"build_dataset_structure\",\n            \"function\": \"utils.datasets.buildDatasetStructure\",\n            \"function_args\": {\n                \"path\" : \"${path}\",\n                \"types\" : [\"colmap\", \"capreal\"]\n            }\n        },\n        {\n            \"name\": \"colmap_feature_extractor\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"feature_extractor\",\n                \"--database_path\", \"${path}/colmap/dataset.db\",\n\t\t        \"--image_path\", \"${path}/images/\",\n                \"--ImageReader.camera_model\", \"OPENCV\",\n                \"--SiftExtraction.max_image_size\", \"${siftExtraction_ImageSize}\",\n                \"--SiftExtraction.estimate_affine_shape\", \"${siftExtraction_EstimateAffineShape}\",\n                \"--SiftExtraction.domain_size_pooling\", \"${siftExtraction_DomainSizePooling}\",\n                \"--SiftExtraction.max_num_features\", \"${siftExtraction_MaxNumFeatures}\",\n                \"--ImageReader.single_camera\", \"${imageReader_SingleCamera}\",\n                \"--SiftExtraction.gpu_index\", \"${gpusIndices}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_exhaustive_matcher\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"exhaustive_matcher\",\n                \"--database_path\", \"${path}/colmap/dataset.db\",\n                \"--SiftMatching.guided_matching\", \"1\",\n                \"--ExhaustiveMatching.block_size\", \"${exhaustiveMatcher_ExhaustiveMatchingBlockSize}\",\n                \"--SiftMatching.gpu_index\", \"${gpusIndices}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_mapper\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"mapper\",\n                \"--database_path\", \"${path}/colmap/dataset.db\",\n\t\t        \"--image_path\", \"${path}/images/\",\n                \"--output_path\", \"${path}/colmap/sparse/\",\n                \"--Mapper.ba_local_max_num_iterations\", \"${mapper_MapperDotbaLocalMaxNumIterations}\",\n                \"--Mapper.ba_global_max_num_iterations\", \"${mapper_MapperDotbaGlobalMaxNumIterations}\",\n                \"--Mapper.ba_global_images_ratio\", \"${mapper_MapperDotbaGlobalImagesRatio}\",\n                \"--Mapper.ba_global_points_ratio\", \"${mapper_MapperDotbaGlobalPointsRatio}\",\n                \"--Mapper.ba_global_max_refinements\", \"${mapper_MapperDotbaGlobalMaxRefinements}\",\n                \"--Mapper.ba_local_max_refinements\", \"${mapper_MapperDotbaLocalMaxRefinements}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_image_undistorter_colmap\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"image_undistorter\",\n                \"--image_path\", \"${path}/images/\",\n                \"--input_path\", \"${path}/colmap/sparse/0\",\n                \"--output_path\", \"${path}/colmap/stereo/\",\n                \"--output_type\", \"COLMAP\"\n            ]\n        },\n        {\n            \"name\": \"colmap_image_undistorter_capreal\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"image_undistorter\",\n                \"--image_path\", \"${path}/images/\",\n                \"--input_path\", \"${path}/colmap/sparse/0/\",\n                \"--output_path\", \"${path}/capreal/undistorted/\",\n                \"--output_type\", \"CMP-MVS\"\n            ]\n        },\n        {\n            \"name\": \"colmap_patch_match_stereo\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"patch_match_stereo\",\n                \"--workspace_path\", \"${path}/colmap/stereo\",\n                \"--workspace_format\", \"COLMAP\",\n                \"--PatchMatchStereo.max_image_size\", \"${patchMatchStereo_PatchMatchStereoDotMaxImageSize}\",\n                \"--PatchMatchStereo.window_radius\", \"${patchMatchStereo_PatchMatchStereoDotWindowRadius}\",\n                \"--PatchMatchStereo.window_step\", \"${patchMatchStereo_PatchMatchStereoDotWindowStep}\",\n                \"--PatchMatchStereo.num_samples\", \"${patchMatchStereo_PatchMatchStereoDotNumSamples}\",\n                \"--PatchMatchStereo.num_iterations\", \"${patchMatchStereo_PatchMatchStereoDotNumIterations}\",\n                \"--PatchMatchStereo.geom_consistency\", \"${patchMatchStereo_PatchMatchStereoDotGeomConsistency}\",\n                \"--PatchMatchStereo.gpu_index\", \"${gpusIndices}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_stereo_fusion\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"stereo_fusion\",\n                \"--workspace_path\", \"${path}/colmap/stereo/\",\n                \"--workspace_format\", \"COLMAP\",\n                \"--input_type\", \"geometric\",\n                \"--output_path\", \"${path}/colmap/stereo/fused.ply\",\n                \"--StereoFusion.max_image_size\", \"${stereoFusion_MaxImageSize}\",\n                \"--StereoFusion.check_num_images\", \"${stereoFusion_CheckNumImages}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_delaunay_mesher\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"delaunay_mesher\",\n                \"--input_path\", \"${path}/colmap/stereo/\",\n                \"--output_path\", \"${path}/colmap/stereo/meshed-delaunay.ply\",\n                \"--input_type\", \"dense\"\n            ]\n        },\n        {\n            \"name\": \"colmap_model_converter\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"model_converter\",\n                \"--input_path\", \"${path}/colmap/stereo/sparse/\",\n                \"--output_path\", \"${path}/colmap/stereo/sparse/\",\n                \"--output_type\", \"TXT\"\n            ]\n        },\n        {\n            \"name\": \"fix_mesh_eol\",\n            \"function\": \"utils.convert.fixMeshEol\",\n            \"function_args\": {\n                \"meshPath\" : \"${path}/colmap/stereo/meshed-delaunay.ply\",\n                \"newMeshPath\" : \"${path}/colmap/stereo/unix-meshed-delaunay.ply\"\n            }\n        },\n        {\n            \"if\": \"${with_texture}\",\n            \"name\": \"simplify_mesh\",\n            \"function\": \"simplify_mesh.simplifyMesh\",\n            \"function_args\": {\n                \"inputMesh\" : \"${path}/colmap/stereo/unix-meshed-delaunay.ply\",\n                \"outputMesh\" : \"${path}/colmap/stereo/unix-meshed-delaunay-simplified.ply\",\n                \"meshlabPath\" : \"${meshlabPath}\",\n\t\t\"meshsize\" : \"${meshsize}\"\n            }\n        },\n        {\n            \"if\": \"${with_texture}\",\n            \"name\": \"unwrap_mesh\",\n            \"app\": \"unwrapMesh\",\n            \"command_args\": [\n                \"--path\", \"${path}/colmap/stereo/unix-meshed-delaunay-simplified.ply\",\n                \"--output\", \"${path}/capreal/mesh.ply\",\n                \"--texture-name\", \"texture.png\"\n            ]\n        },\n        {\n            \"if\": \"${with_texture}\",\n            \"name\": \"texture_mesh\",\n            \"app\": \"textureMesh\",\n            \"command_args\": [\n                \"--path\", \"${path}\",\n                \"--output\", \"${path}/capreal/texture.png\",\n                \"--size\", \"8192\",\n                \"--flood\"\n            ]\n        },\n        {\n            \"name\": \"move_eol_dirty_mesh\",\n            \"function\": \"shutil.copy\",\n            \"function_args\": {\n                \"src\" : \"${path}/colmap/stereo/meshed-delaunay.ply\",\n                \"dst\" : \"${path}/colmap/stereo/meshed-delaunay-eolpb.ply\"\n            }\n        },\n        {\n            \"name\": \"use_eol_fixed_mesh\",\n            \"function\": \"shutil.copy\",\n            \"function_args\": {\n                \"src\" : \"${path}/colmap/stereo/unix-meshed-delaunay.ply\",\n                \"dst\" : \"${path}/colmap/stereo/meshed-delaunay.ply\"\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/ColmapQualityParameters.json",
    "content": "{\n    \"siftExtraction_ImageSize\": {\n        \"default\": 3200,\n        \"low\": 1000,\n        \"medium\": 1600,\n        \"average\": 3200,\n        \"high\": 2400,\n        \"extreme\": 3200\n    },\n    \"siftExtraction_EstimateAffineShape\": {\n        \"default\": false,\n        \"low\": false,\n        \"medium\": false,\n        \"average\": false,\n        \"high\": true,\n        \"extreme\": true\n    },\n    \"siftExtraction_DomainSizePooling\": {\n        \"default\": false,\n        \"low\": false,\n        \"medium\": false,\n        \"average\": false,\n        \"high\": false,\n        \"extreme\": true\n    },\n    \"siftExtraction_MaxNumFeatures\": {\n        \"default\": 16000,\n        \"low\": 8192,\n        \"medium\": 8192,\n        \"average\": 8192,\n        \"high\": 8192,\n        \"extreme\": 8192\n    },\n    \"imageReader_SingleCamera\": {\n        \"default\": false,\n        \"low\": true,\n        \"medium\": true,\n        \"average\": true,\n        \"high\": true,\n        \"extreme\": true\n    },\n    \"exhaustiveMatcher_ExhaustiveMatchingBlockSize\": {\n        \"default\": 50,\n        \"low\": 50,\n        \"medium\": 50,\n        \"average\": 50,\n        \"high\": 50,\n        \"extreme\": 50\n    },\n    \"mapper_MapperDotbaLocalMaxNumIterations\": {\n        \"default\": 25,\n        \"low\": 12,\n        \"medium\": 16,\n        \"average\": 25,\n        \"high\": 30,\n        \"extreme\": 40\n    },\n    \"mapper_MapperDotbaGlobalMaxNumIterations\": {\n        \"default\": 50,\n        \"low\": 25,\n        \"medium\": 33,\n        \"average\": 50,\n        \"high\": 75,\n        \"extreme\": 100\n    },\n    \"mapper_MapperDotbaGlobalImagesRatio\": {\n        \"default\": 1.100001,\n        \"low\": 1.32,\n        \"medium\": 1.21,\n        \"average\": 1.100001,\n        \"high\": 1.100001,\n        \"extreme\": 1.100001\n    },\n    \"mapper_MapperDotbaGlobalPointsRatio\": {\n        \"default\": 1.100001,\n        \"low\": 1.32,\n        \"medium\": 1.21,\n        \"average\": 1.100001,\n        \"high\": 1.100001,\n        \"extreme\": 1.100001\n    },\n    \"mapper_MapperDotbaGlobalMaxRefinements\": {\n        \"default\": 5,\n        \"low\": 2,\n        \"medium\": 2,\n        \"average\": 5,\n        \"high\": 5,\n        \"extreme\": 5\n    },\n    \"mapper_MapperDotbaLocalMaxRefinements\": {\n        \"default\": 2,\n        \"low\": 2,\n        \"medium\": 2,\n        \"average\": 2,\n        \"high\": 3,\n        \"extreme\": 3\n    },\n    \"patchMatchStereo_PatchMatchStereoDotMaxImageSize\": {\n        \"default\": -1,\n        \"low\": 1000,\n        \"medium\": 1600,\n        \"average\": -1,\n        \"high\": 2400,\n        \"extreme\": -1\n    },\n    \"patchMatchStereo_PatchMatchStereoDotWindowRadius\": {\n        \"default\": 5,\n        \"low\": 4,\n        \"medium\": 4,\n        \"average\": 5,\n        \"high\": 5,\n        \"extreme\": 5\n    },\n    \"patchMatchStereo_PatchMatchStereoDotWindowStep\": {\n        \"default\": 1,\n        \"low\": 2,\n        \"medium\": 2,\n        \"average\": 1,\n        \"high\": 1,\n        \"extreme\": 1\n    },\n    \"patchMatchStereo_PatchMatchStereoDotNumSamples\": {\n        \"default\": 15,\n        \"low\": 7,\n        \"medium\": 10,\n        \"average\": 15,\n        \"high\": 15,\n        \"extreme\": 15\n    },\n    \"patchMatchStereo_PatchMatchStereoDotNumIterations\": {\n        \"default\": 5,\n        \"low\": 3,\n        \"medium\": 5,\n        \"average\": 5,\n        \"high\": 5,\n        \"extreme\": 5\n    },\n    \"patchMatchStereo_PatchMatchStereoDotGeomConsistency\": {\n        \"default\": 1,\n        \"low\": 0,\n        \"medium\": 0,\n        \"average\": 1,\n        \"high\": 1,\n        \"extreme\": 1\n    },\n    \"stereoFusion_CheckNumImages\": {\n        \"default\": 50,\n        \"low\": 25,\n        \"medium\": 33,\n        \"average\": 50,\n        \"high\": 50,\n        \"extreme\": 50\n    },\n    \"stereoFusion_MaxImageSize\": {\n        \"default\": -1,\n        \"low\": 1000,\n        \"medium\": 1600,\n        \"average\": -1,\n        \"high\": 2400,\n        \"extreme\": -1\n    }\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/SelectiveColmapProcessSteps.json",
    "content": "{\n    \"steps\" : [\n        {\n            \"name\": \"build_dataset_structure\",\n            \"function\": \"utils.datasets.buildDatasetStructure\",\n            \"function_args\": {\n                \"path\" : \"${path}\",\n                \"types\" : [\"colmap\"]\n            }\n        },\n        {\n            \"name\": \"extract_video_frames\",\n            \"function\": \"selective_colmap_process.extract_video_frames\",\n            \"function_args\": {\n                \"pathIn\": \"${path}/videos\",\n                \"pathOut\": \"${path}/images\"\n            }\n        },\n        {\n            \"name\": \"colmap_feature_extractor\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"feature_extractor\",\n                \"--database_path\", \"${path}/colmap/dataset.db\",\n                \"--image_path\", \"${path}/images/\",\n                \"--ImageReader.camera_model\", \"OPENCV\",\n                \"--SiftExtraction.max_image_size\", \"${siftExtraction_ImageSize}\",\n                \"--SiftExtraction.estimate_affine_shape\", \"${siftExtraction_EstimateAffineShape}\",\n                \"--SiftExtraction.domain_size_pooling\", \"${siftExtraction_DomainSizePooling}\",\n                \"--SiftExtraction.max_num_features\", \"${siftExtraction_MaxNumFeatures}\",\n                \"--ImageReader.single_camera\", \"${imageReader_SingleCamera}\",\n                \"--SiftExtraction.gpu_index\", \"${gpusIndices}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_exhaustive_matcher\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"exhaustive_matcher\",\n                \"--database_path\", \"${path}/colmap/dataset.db\",\n                \"--SiftMatching.gpu_index\", \"${gpusIndices}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_mapper\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"mapper\",\n                \"--database_path\", \"${path}/colmap/dataset.db\",\n                \"--image_path\", \"${path}/images/\",\n                \"--output_path\", \"${path}/colmap/sparse/\",\n                \"--Mapper.num_threads\", \"16\",\n                \"--Mapper.init_min_tri_angle\", \"4\",\n                \"--Mapper.multiple_models\", \"0\",\n                \"--Mapper.extract_colors\", \"0\",\n                \"--Mapper.ba_global_images_ratio\", \"1.2\",\n                \"--Mapper.ba_global_points_ratio\", \"1.2\",\n                \"--Mapper.ba_global_max_num_iterations\", \"20\",\n                \"--Mapper.ba_global_max_refinements\", \"3\",\n                \"--Mapper.ba_global_points_freq\", \"200000\"\n            ]\n        },\n        {\n            \"name\": \"colmap_model_converter_sparse_0\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"model_converter\",\n                \"--input_path\", \"${path}/colmap/sparse/0\",\n                \"--output_path\", \"${path}/colmap/sparse/\",\n                \"--output_type\", \"TXT\"\n            ]\n        },\n        {    \n            \"name\": \"fix_cameras\",\n            \"function\": \"selective_colmap_process.fix_cameras\",\n            \"function_args\": {\n                \"path\": \"${path}\",\n                \"photoName\": \"MG_\",\n                \"sparseSubdir\": \"\"\n            }\n        },\n        {\n            \"name\": \"colmap_image_undistorter_colmap\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"image_undistorter\",\n                \"--image_path\", \"${path}/images/\",\n                \"--input_path\", \"${path}/colmap/sparse/\",\n                \"--output_path\", \"${path}/colmap/stereo\",\n                \"--output_type\", \"COLMAP\"\n            ]\n        },\n        {\n            \"name\": \"colmap_model_converter_stereo_sparse\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"model_converter\",\n                \"--input_path\", \"${path}/colmap/stereo/sparse\",\n                \"--output_path\", \"${path}/colmap/stereo/sparse/\",\n                \"--output_type\", \"TXT\"\n            ]\n        },\n        {\n            \"name\": \"build_dataset_structure\",\n            \"function\": \"utils.datasets.buildDatasetStructure\",\n            \"function_args\": {\n                \"path\" : \"${path}\",\n                \"types\" : [\"capreal\"]\n            }\n        },\n        {\n            \"name\": \"colmap_image_deleter_colmap\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"image_deleter\",\n                \"--input_path\", \"${path}/colmap/sparse/\",\n                \"--output_path\", \"${path}/colmap/sparse/\",\n                \"--image_names_path\", \"${path}/videos/Video_frames.txt\"\n            ]\n        },\n        { \n            \"name\": \"remove_video_images\",\n            \"function\": \"selective_colmap_process.remove_video_images\",\n            \"function_args\": {\n                \"path\": \"${path}\"\n            }\n        },\n        {\n            \"name\": \"colmap_patch_match_stereo\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"patch_match_stereo\",\n                \"--workspace_path\", \"${path}/colmap/stereo\",\n                \"--workspace_format\", \"COLMAP\",\n                \"--PatchMatchStereo.max_image_size\", \"${patchMatchStereo_PatchMatchStereoDotMaxImageSize}\",\n                \"--PatchMatchStereo.window_radius\", \"${patchMatchStereo_PatchMatchStereoDotWindowRadius}\",\n                \"--PatchMatchStereo.window_step\", \"${patchMatchStereo_PatchMatchStereoDotWindowStep}\",\n                \"--PatchMatchStereo.num_samples\", \"${patchMatchStereo_PatchMatchStereoDotNumSamples}\",\n                \"--PatchMatchStereo.num_iterations\", \"${patchMatchStereo_PatchMatchStereoDotNumIterations}\",\n                \"--PatchMatchStereo.geom_consistency\", \"${patchMatchStereo_PatchMatchStereoDotGeomConsistency}\",\n                \"--PatchMatchStereo.gpu_index\", \"${gpusIndices}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_stereo_fusion\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"stereo_fusion\",\n                \"--workspace_path\", \"${path}/colmap/stereo/\",\n                \"--workspace_format\", \"COLMAP\",\n                \"--input_type\", \"geometric\",\n                \"--output_path\", \"${path}/colmap/stereo/fused.ply\",\n                \"--StereoFusion.max_image_size\", \"${stereoFusion_MaxImageSize}\",\n                \"--StereoFusion.check_num_images\", \"${stereoFusion_CheckNumImages}\"\n            ]\n        },\n        {\n            \"name\": \"colmap_delaunay_mesher\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"delaunay_mesher\",\n                \"--input_path\", \"${path}/colmap/stereo/\",\n                \"--output_path\", \"${path}/colmap/stereo/meshed-delaunay.ply\",\n                \"--input_type\", \"dense\"\n            ]\n        },\n        {\n            \"name\": \"colmap_model_converter\",\n            \"app\": \"colmap\",\n            \"command_args\": [\n                \"model_converter\",\n                \"--input_path\", \"${path}/colmap/stereo/sparse/\",\n                \"--output_path\", \"${path}/colmap/stereo/sparse/\",\n                \"--output_type\", \"TXT\"\n            ]\n        },\n        {\n            \"name\": \"fix_mesh_eol\",\n            \"function\": \"utils.convert.fixMeshEol\",\n            \"function_args\": {\n                \"meshPath\" : \"${path}/colmap/stereo/meshed-delaunay.ply\",\n                \"newMeshPath\" : \"${path}/colmap/stereo/unix-meshed-delaunay.ply\"\n            }\n        },\n        {\n            \"if\": \"${with_texture}\",\n            \"name\": \"simplify_mesh\",\n            \"function\": \"simplify_mesh.simplifyMesh\",\n            \"function_args\": {\n                \"inputMesh\" : \"${path}/colmap/stereo/unix-meshed-delaunay.ply\",\n                \"outputMesh\" : \"${path}/colmap/stereo/unix-meshed-delaunay-simplified.ply\",\n                \"meshlabPath\" : \"${meshlabPath}\",\n                \"meshsize\" : \"${meshsize}\"\n            }\n        },\n        {\n            \"if\": \"${with_texture}\",\n            \"name\": \"unwrap_mesh\",\n            \"app\": \"unwrapMesh\",\n            \"command_args\": [\n                \"--path\", \"${path}/colmap/stereo/unix-meshed-delaunay-simplified.ply\",\n                \"--output\", \"${path}/capreal/mesh.ply\",\n                \"--texture-name\", \"texture.png\"\n            ]\n        },\n        {\n            \"if\": \"${with_texture}\",\n            \"name\": \"texture_mesh\",\n            \"app\": \"textureMesh\",\n            \"command_args\": [\n                \"--path\", \"${path}\",\n                \"--output\", \"${path}/capreal/texture.png\",\n                \"--size\", \"8192\",\n                \"--flood\"\n            ]\n        },\n        {\n            \"name\": \"move_eol_dirty_mesh\",\n            \"function\": \"shutil.copy\",\n            \"function_args\": {\n                \"src\" : \"${path}/colmap/stereo/meshed-delaunay.ply\",\n                \"dst\" : \"${path}/colmap/stereo/meshed-delaunay-eolpb.ply\"\n            }\n        },\n        {\n            \"name\": \"use_eol_fixed_mesh\",\n            \"function\": \"shutil.copy\",\n            \"function_args\": {\n                \"src\" : \"${path}/colmap/stereo/unix-meshed-delaunay.ply\",\n                \"dst\" : \"${path}/colmap/stereo/meshed-delaunay.ply\"\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/colmap2nerf.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2020-2022, NVIDIA CORPORATION.  All rights reserved.\n#\n# NVIDIA CORPORATION and its licensors retain all intellectual property\n# and proprietary rights in and to this software, related documentation\n# and any modifications thereto.  Any use, reproduction, disclosure or\n# distribution of this software and related documentation without an express\n# license agreement from NVIDIA CORPORATION is strictly prohibited.\n\nimport argparse\nimport os\nfrom pathlib import Path, PurePosixPath\n\nimport numpy as np\nimport json\nimport sys\nimport math\nimport cv2\nimport os\nimport shutil\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description=\"convert a text colmap export to nerf format transforms.json; optionally convert video to images, and optionally run colmap in the first place\")\n\n    parser.add_argument(\"--video_in\", default=\"\", help=\"run ffmpeg first to convert a provided video file into a set of images. uses the video_fps parameter also\")\n    parser.add_argument(\"--video_fps\", default=2)\n    parser.add_argument(\"--run_colmap\", action=\"store_true\", help=\"run colmap first on the image folder\")\n    parser.add_argument(\"--colmap_matcher\", default=\"sequential\", choices=[\"exhaustive\",\"sequential\",\"spatial\",\"transitive\",\"vocab_tree\"], help=\"select which matcher colmap should use. sequential for videos, exhaustive for adhoc images\")\n    parser.add_argument(\"--colmap_db\", default=\"colmap.db\", help=\"colmap database filename\")\n    parser.add_argument(\"--images\", default=\"images\", help=\"input path to the images\")\n    parser.add_argument(\"--text\", default=\"colmap_text\", help=\"input path to the colmap text files (set automatically if run_colmap is used)\")\n    parser.add_argument(\"--aabb_scale\", default=16, choices=[\"1\",\"2\",\"4\",\"8\",\"16\"], help=\"large scene scale factor. 1=scene fits in unit cube; power of 2 up to 16\")\n    parser.add_argument(\"--skip_early\", default=0, help=\"skip this many images from the start\")\n    parser.add_argument(\"--out\", default=\"transforms.json\", help=\"output path\")\n    parser.add_argument(\"--path\", default=\"\", help=\"top level dataset\")\n    args = parser.parse_args()\n    return args\n\ndef do_system(arg):\n    print(f\"==== running: {arg}\")\n    err=os.system(arg)\n    if err:\n        print(\"FATAL: command failed\")\n        sys.exit(err)\n\n\ndef run_ffmpeg(args):\n    if not os.path.isabs(args.images):\n        args.images = os.path.join(os.path.dirname(args.video_in), args.images)\n    images=args.images\n    video=args.video_in\n    fps=float(args.video_fps) or 1.0\n    print(f\"running ffmpeg with input video file={video}, output image folder={images}, fps={fps}.\")\n    if (input(f\"warning! folder '{images}' will be deleted/replaced. continue? (Y/n)\").lower().strip()+\"y\")[:1] != \"y\":\n        sys.exit(1)\n    try:\n        shutil.rmtree(images)\n    except:\n        pass\n    do_system(f\"mkdir {images}\")\n    do_system(f\"ffmpeg -i {video} -qscale:v 1 -qmin 1 -vf \\\"fps={fps}\\\" {images}/%04d.jpg\")\n\ndef run_colmap(args):\n    db=args.colmap_db\n    images=args.images\n    db_noext=str(Path(db).with_suffix(\"\"))\n\n    if args.text==\"text\":\n        args.text=db_noext+\"_text\"\n    text=args.text\n    sparse=db_noext+\"_sparse\"\n    print(f\"running colmap with:\\n\\tdb={db}\\n\\timages={images}\\n\\tsparse={sparse}\\n\\ttext={text}\")\n    if (input(f\"warning! folders '{sparse}' and '{text}' will be deleted/replaced. continue? (Y/n)\").lower().strip()+\"y\")[:1] != \"y\":\n        sys.exit(1)\n    if os.path.exists(db):\n        os.remove(db)\n    do_system(f\"colmap feature_extractor --ImageReader.camera_model OPENCV --ImageReader.single_camera 1 --database_path {db} --image_path {images}\")\n    do_system(f\"colmap {args.colmap_matcher}_matcher --database_path {db}\")\n    try:\n        shutil.rmtree(sparse)\n    except:\n        pass\n    do_system(f\"mkdir {sparse}\")\n    do_system(f\"colmap mapper --database_path {db} --image_path {images} --output_path {sparse}\")\n    do_system(f\"colmap bundle_adjuster --input_path {sparse}/0 --output_path {sparse}/0 --BundleAdjustment.refine_principal_point 1\")\n    try:\n        shutil.rmtree(text)\n    except:\n        pass\n    do_system(f\"mkdir {text}\")\n    do_system(f\"colmap model_converter --input_path {sparse}/0 --output_path {text} --output_type TXT\")\n\ndef variance_of_laplacian(image):\n    return cv2.Laplacian(image, cv2.CV_64F).var()\n\ndef sharpness(imagePath):\n    image = cv2.imread(imagePath)\n    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    fm = variance_of_laplacian(gray)\n    return fm\n\ndef qvec2rotmat(qvec):\n    return np.array([\n        [\n            1 - 2 * qvec[2]**2 - 2 * qvec[3]**2,\n            2 * qvec[1] * qvec[2] - 2 * qvec[0] * qvec[3],\n            2 * qvec[3] * qvec[1] + 2 * qvec[0] * qvec[2]\n        ], [\n            2 * qvec[1] * qvec[2] + 2 * qvec[0] * qvec[3],\n            1 - 2 * qvec[1]**2 - 2 * qvec[3]**2,\n            2 * qvec[2] * qvec[3] - 2 * qvec[0] * qvec[1]\n        ], [\n            2 * qvec[3] * qvec[1] - 2 * qvec[0] * qvec[2],\n            2 * qvec[2] * qvec[3] + 2 * qvec[0] * qvec[1],\n            1 - 2 * qvec[1]**2 - 2 * qvec[2]**2\n        ]\n    ])\n\ndef rotmat(a, b):\n    a, b = a / np.linalg.norm(a), b / np.linalg.norm(b)\n    v = np.cross(a, b)\n    c = np.dot(a, b)\n    s = np.linalg.norm(v)\n    kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])\n    return np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2 + 1e-10))\n\ndef closest_point_2_lines(oa, da, ob, db): # returns point closest to both rays of form o+t*d, and a weight factor that goes to 0 if the lines are parallel\n    da=da/np.linalg.norm(da)\n    db=db/np.linalg.norm(db)\n    c=np.cross(da,db)\n    denom=(np.linalg.norm(c)**2)\n    t=ob-oa\n    ta=np.linalg.det([t,db,c])/(denom+1e-10)\n    tb=np.linalg.det([t,da,c])/(denom+1e-10)\n    if ta>0:\n        ta=0\n    if tb>0:\n        tb=0\n    return (oa+ta*da+ob+tb*db)*0.5,denom\n\n\n\ndef convert(AABB_SCALE, SKIP_EARLY, IMAGE_FOLDER, TEXT_FOLDER, OUT_PATH, totp=-1, totw=-1, avglen = -1, rMat = np.array([])):\n    print(f\"outputting to {OUT_PATH}...\")\n    with open(os.path.join(TEXT_FOLDER,\"cameras.txt\"), \"r\") as f:\n        angle_x=math.pi/2\n        for line in f:\n            # 1 SIMPLE_RADIAL 2048 1536 1580.46 1024 768 0.0045691\n            # 1 OPENCV 3840 2160 3178.27 3182.09 1920 1080 0.159668 -0.231286 -0.00123982 0.00272224\n            # 1 RADIAL 1920 1080 1665.1 960 540 0.0672856 -0.0761443\n            # \n            if line[0]==\"#\":\n                continue\n            els=line.split(\" \")\n            w = float(els[2])\n            h = float(els[3])\n            fl_x = float(els[4])\n            fl_y = float(els[4])\n            k1 = 0\n            k2 = 0\n            p1 = 0\n            p2 = 0\n            cx = w/2\n            cy = h/2\n            if (els[1]==\"SIMPLE_RADIAL\"):\n                cx = float(els[5])\n                cy = float(els[6])\n                k1 = float(els[7])\n            elif (els[1]==\"RADIAL\"):\n                cx = float(els[5])\n                cy = float(els[6])\n                k1 = float(els[7])\n                k2 = float(els[8])\n            elif (els[1]==\"OPENCV\"):\n                fl_y = float(els[5])\n                cx = float(els[6])\n                cy = float(els[7])\n                k1 = float(els[8])\n                k2 = float(els[9])\n                p1 = float(els[10])\n                p2 = float(els[11])\n            elif (els[1]==\"PINHOLE\"):\n                cx = float(els[6])\n                cy = float(els[7])\n            else:\n                print(\"unknown camera model \", els[1])\n            # fl = 0.5 * w / tan(0.5 * angle_x);\n            angle_x= math.atan(w/(fl_x*2))*2\n            angle_y= math.atan(h/(fl_y*2))*2\n            fovx=angle_x*180/math.pi\n            fovy=angle_y*180/math.pi\n\n    #print(f\"camera:\\n\\tres={w,h}\\n\\tcenter={cx,cy}\\n\\tfocal={fl_x,fl_y}\\n\\tfov={fovx,fovy}\\n\\tk={k1,k2} p={p1,p2} \")\n\n    with open(os.path.join(TEXT_FOLDER,\"images.txt\"), \"r\") as f:\n        i=0\n        bottom = np.array([0,0,0,1.]).reshape([1,4])\n        out={\n            \"camera_angle_x\":angle_x,\n            \"camera_angle_y\":angle_y,\n            \"fl_x\":fl_x,\n            \"fl_y\":fl_y,\n            \"k1\":k1,\n            \"k2\":k2,\n            \"p1\":p1,\n            \"p2\":p2,\n            \"cx\":cx,\n            \"cy\":cy,\n            \"w\":w,\n            \"h\":h,\n            \"aabb_scale\":AABB_SCALE,\"frames\":[]\n        }\n\n        up=np.zeros(3)\n        for line in f:\n            line=line.strip()\n            if len(line)!=0 and line[0]==\"#\":\n                continue\n            i=i+1\n            if i < SKIP_EARLY*2:\n                continue\n            if  i%2==1 :\n                elems=line.split(\" \") # 1-4 is quat, 5-7 is trans, 9 is filename\n                #name = str(PurePosixPath(Path(IMAGE_FOLDER, elems[9])))\n                # why is this requireing a relitive path while using ^\n                image_rel = os.path.relpath(IMAGE_FOLDER)\n                name = str(f\"./{image_rel}/{elems[9]}\")\n                if not os.path.exists(name):\n                    name = name + \".png\"\n                    print(\"opening \", name)\n                b=sharpness(name)\n                #print(name, \"sharpness=\",b)\n                image_id = int(elems[0])\n                qvec = np.array(tuple(map(float, elems[1:5])))\n                tvec = np.array(tuple(map(float, elems[5:8])))\n                R = qvec2rotmat(-qvec)\n                t = tvec.reshape([3,1])\n                m = np.concatenate([np.concatenate([R, t], 1), bottom], 0)\n                c2w = np.linalg.inv(m)\n                c2w[0:3,2] *= -1 # flip the y and z axis\n                c2w[0:3,1] *= -1\n                c2w=c2w[[1,0,2,3],:] # swap y and z\n                c2w[2,:] *= -1 # flip whole world upside down\n\n                up += c2w[0:3,1]\n\n                #s=str(os.path.splitext(os.path.basename(elems[9]))[0])\n                s=str(os.path.basename(elems[9]))\n                #print(\"BASENAME \", s)\n                name = \"images/\"+ s # os.path.join(\"images\", s)\n                frame={\"file_path\":name,\"sharpness\":b,\"transform_matrix\": c2w}\n                #print(\"OUTPUT \", name)\n                out[\"frames\"].append(frame)\n    nframes = len(out[\"frames\"])\n    if len(rMat) == 0:\n        up = up / np.linalg.norm(up)\n        print(\"rMat is None up vector was \", up)\n        R=rotmat(up,[0,0,1]) # rotate up vector to [0,0,1]\n        R=np.pad(R,[0,1])\n        R[-1,-1]=1\n        rMat = R\n    else:\n        R = rMat\n\n\n    for f in out[\"frames\"]:\n        f[\"transform_matrix\"]=np.matmul(R,f[\"transform_matrix\"]) # rotate up to be the z axis\n\n    # find a central point they are all looking at\n    print(\"computing center of attention...\")\n\n    print(\"TOTP {} TOTW {}\".format(totp, totw))\n    if totw < 0 :\n        totw=0\n        totp=[0,0,0]\n        for f in out[\"frames\"]:\n            mf=f[\"transform_matrix\"][0:3,:]\n            for g in out[\"frames\"]:\n                mg=g[\"transform_matrix\"][0:3,:]\n                p,w=closest_point_2_lines(mf[:,3],mf[:,2],mg[:,3],mg[:,2])\n                if w>0.01:\n                    totp+=p*w\n                totw+=w\n        if totw >0:\n            totp/=totw\n\n    print(\"AFTER TOTP {} TOTW {}\".format(totp, totw))\n    print(totp) # the cameras are looking at totp\n    for f in out[\"frames\"]:\n        f[\"transform_matrix\"][0:3,3]-=totp\n\n    if avglen < 0:\n        avglen=0.\n        for f in out[\"frames\"]:\n            avglen+=np.linalg.norm(f[\"transform_matrix\"][0:3,3])\n        avglen/=nframes\n\n    print(\"avg camera distance from origin \", avglen)\n    for f in out[\"frames\"]:\n        f[\"transform_matrix\"][0:3,3]*=4./avglen     # scale to \"nerf sized\"\n\n    for f in out[\"frames\"]:\n        f[\"transform_matrix\"]=f[\"transform_matrix\"].tolist()\n    print(nframes,\"frames\")\n    print(f\"writing {OUT_PATH}\")\n    with open(OUT_PATH, \"w\") as outfile:\n        json.dump(out, outfile, indent=2)\n\n    return totp, totw, avglen, rMat\n\ndef createNerf(path, hires=False):\n    AABB_SCALE=int(16)\n    SKIP_EARLY=int(0)\n    print(\"Path is \", path, str(path))\n    if hires:\n        print(\"DOING HIRES !!\")\n        colmappath = os.path.join(os.path.join(str(path), \"sibr\"), \"colmap\")\n    else:\n        colmappath = os.path.join(os.path.join(str(path), \"colmap_1000\"), \"colmap\")\n    TEXT_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"sparse\")\n    IMAGE_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"images\")\n    OUT_PATH= os.path.join(os.path.join(colmappath,  \"stereo\"), \"transforms.json\")\n\n    totp, totw, avglen, rMat = convert(AABB_SCALE, SKIP_EARLY, IMAGE_FOLDER, TEXT_FOLDER, OUT_PATH, -1, -1, -1, np.array([]))\n\n    colmappath = os.path.join(os.path.join(str(path), \"colmap_1000\"), \"validation_colmap\") \n    TEXT_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"sparse\")\n    IMAGE_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"images\")\n    OUT_PATH= os.path.join(os.path.join(colmappath,  \"stereo\"), \"transforms.json\")\n    totp, totw, avglen, rMat = convert(AABB_SCALE, SKIP_EARLY, IMAGE_FOLDER, TEXT_FOLDER, OUT_PATH, totp, totw, avglen, rMat)\n\n    colmappath = os.path.join(os.path.join(str(path), \"colmap_1000\"), \"test_path_colmap\") \n    TEXT_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"sparse\")\n    IMAGE_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"images\")\n    OUT_PATH= os.path.join(os.path.join(colmappath,  \"stereo\"), \"transforms.json\")\n    totp, totw, avglen, rMat = convert(AABB_SCALE, SKIP_EARLY, IMAGE_FOLDER, TEXT_FOLDER, OUT_PATH, totp, totw, avglen, rMat)\n\n    # if test2_path exists\n    colmappath = os.path.join(os.path.join(str(path), \"colmap_1000\"), \"test_path2\") \n\n    if os.path.exists(colmappath):\n        TEXT_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"sparse\")\n        IMAGE_FOLDER=os.path.join(os.path.join(colmappath,  \"stereo\"), \"images\")\n        OUT_PATH= os.path.join(os.path.join(colmappath,  \"stereo\"), \"transforms.json\")\n        totp, totw, avglen, rMat = convert(AABB_SCALE, SKIP_EARLY, IMAGE_FOLDER, TEXT_FOLDER, OUT_PATH, totp, totw, avglen, rMat)\n\nif __name__ == \"__main__\":\n    args = parse_args()\n    if args.video_in != \"\":\n        run_ffmpeg(args)\n    if args.run_colmap:\n        run_colmap(args)\n    AABB_SCALE=int(args.aabb_scale)\n    SKIP_EARLY=int(args.skip_early)\n    IMAGE_FOLDER=args.images\n    TEXT_FOLDER=args.text\n    OUT_PATH=args.out\n    convert(AABB_SCALE, SKIP_EARLY, IMAGE_FOLDER, TEXT_FOLDER, OUT_PATH, -1, -1)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/fullColmapProcess.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\n\"\"\" @package dataset_tools_preprocess\nThis script runs a pipeline to create Colmap reconstruction data\n\nParameters: -h help,\n            -path <path to your dataset folder>,\n            -sibrBinariesPath <binaries directory of SIBR>,\n            -colmapPath <colmap path directory which contains colmap.bat / colmap.bin>,\n            -quality <quality of the reconstruction : 'low', 'medium', 'high', 'extreme'>,\n\nUsage: python fullColmapProcess.py -path <path to your dataset folder>\n                                   -sibrBinariesPath <binaries directory of SIBR>\n                                   -colmapPath <colmap path directory which contains colmap.bat / colmap.bin>\n                                   -quality <quality of the reconstruction : 'low', 'medium', 'high', 'extreme'>\n\n\"\"\"\n\nimport os, sys, shutil\nimport json\nimport argparse\nfrom utils.paths import getBinariesPath, getColmapPath, getMeshlabPath\nfrom utils.commands import  getProcess, getColmap\nfrom utils.TaskPipeline import TaskPipeline\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--path\", type=str, required=True, help=\"path to your dataset folder\")\n    parser.add_argument(\"--sibrBinariesPath\", type=str, default=getBinariesPath(), help=\"binaries directory of SIBR\")\n    parser.add_argument(\"--colmapPath\", type=str, default=getColmapPath(), help=\"path to directory colmap.bat / colmap.bin directory\")\n    parser.add_argument(\"--meshlabPath\", type=str, default=getMeshlabPath(), help=\"path to meshlabserver directory\")\n    parser.add_argument(\"--quality\", type=str, default='default', choices=['default', 'low', 'medium', 'average', 'high', 'extreme'],\n        help=\"quality of the reconstruction\")\n    parser.add_argument(\"--dry_run\", action='store_true', help=\"run without calling commands\")\n    parser.add_argument(\"--with_texture\", action='store_true', help=\"Add texture steps\")\n    parser.add_argument(\"--create_sibr_scene\", action='store_true', help=\"Create SIBR scene\")\n    parser.add_argument(\"--meshsize\", type=str, help=\"size of the output mesh in K polygons (ie 200 == 200,000 polygons). Values allowed: 200, 250, 300, 350, 400\")\n    \n    #colmap performance arguments\n    parser.add_argument(\"--numGPUs\", type=int, default=2, help=\"number of GPUs allocated to Colmap\")\n\n    # Feature extractor \n    parser.add_argument(\"--SiftExtraction.max_image_size\", type=int, dest=\"siftExtraction_ImageSize\")\n    parser.add_argument(\"--SiftExtraction.estimate_affine_shape\", type=int, dest=\"siftExtraction_EstimateAffineShape\") \n    parser.add_argument(\"--SiftExtraction.domain_size_pooling\", type=int, dest=\"siftExtraction_DomainSizePooling\")\n    parser.add_argument(\"--SiftExtraction.max_num_features\", type=int, dest=\"siftExtraction_MaxNumFeatures\")\n    parser.add_argument(\"--ImageReader.single_camera\", type=int, dest=\"imageReader_SingleCamera\")\n\n    # Exhaustive matcher\n    parser.add_argument(\"--ExhaustiveMatching.block_size\", type=int, dest=\"exhaustiveMatcher_ExhaustiveMatchingBlockSize\")\n\n    # Mapper\n    parser.add_argument(\"--Mapper.ba_local_max_num_iterations\", type=int, dest=\"mapper_MapperDotbaLocalMaxNumIterations\")\n    parser.add_argument(\"--Mapper.ba_global_max_num_iterations\", type=int, dest=\"mapper_MapperDotbaGlobalMaxNumIterations\")\n    parser.add_argument(\"--Mapper.ba_global_images_ratio\", type=float, dest=\"mapper_MapperDotbaGlobalImagesRatio\")\n    parser.add_argument(\"--Mapper.ba_global_points_ratio\", type=float, dest=\"mapper_MapperDotbaGlobalPointsRatio\")\n    parser.add_argument(\"--Mapper.ba_global_max_refinements\", type=int, dest=\"mapper_MapperDotbaGlobalMaxRefinements\")\n    parser.add_argument(\"--Mapper.ba_local_max_refinements\", type=int, dest=\"mapper_MapperDotbaLocalMaxRefinements\")\n\n    # Patch match stereo\n    parser.add_argument(\"--PatchMatchStereo.max_image_size\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotMaxImageSize\")\n    parser.add_argument(\"--PatchMatchStereo.window_radius\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotWindowRadius\")\n    parser.add_argument(\"--PatchMatchStereo.window_step\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotWindowStep\")\n    parser.add_argument(\"--PatchMatchStereo.num_samples\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotNumSamples\")\n    parser.add_argument(\"--PatchMatchStereo.num_iterations\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotNumIterations\")\n    parser.add_argument(\"--PatchMatchStereo.geom_consistency\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotGeomConsistency\")\n\n    # Stereo fusion\n    parser.add_argument(\"--StereoFusion.check_num_images\", type=int, dest=\"stereoFusion_CheckNumImages\")\n    parser.add_argument(\"--StereoFusion.max_image_size\", type=int, dest=\"stereoFusion_MaxImageSize\")\n\n    args = vars(parser.parse_args())\n\n    # Update args with quality values\n    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), \"ColmapQualityParameters.json\"), \"r\") as qualityParamsFile:\n        qualityParams = json.load(qualityParamsFile)\n\n        for key, value in qualityParams.items():\n            if not key in args or args[key] is None:\n                args[key] = qualityParams[key][args[\"quality\"]] if args[\"quality\"] in qualityParams[key] else qualityParams[key][\"default\"]\n\n    # Get process steps\n    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), \"ColmapProcessSteps.json\"), \"r\") as processStepsFile:\n        steps = json.load(processStepsFile)[\"steps\"]\n\n    # Fixing path values\n    args[\"path\"] = os.path.abspath(args[\"path\"])\n    args[\"sibrBinariesPath\"] = os.path.abspath(args[\"sibrBinariesPath\"])\n    args[\"colmapPath\"] = os.path.abspath(args[\"colmapPath\"])\n\n    args[\"gpusIndices\"] = ','.join([str(i) for i in range(args[\"numGPUs\"])])\n\n    programs = {\n        \"colmap\": {\n            \"path\": getColmap(args[\"colmapPath\"])\n        },\n        \"unwrapMesh\": {\n            \"path\": getProcess(\"unwrapMesh\", args[\"sibrBinariesPath\"])\n        },\n        \"textureMesh\": {\n            \"path\": getProcess(\"textureMesh\", args[\"sibrBinariesPath\"])\n        },\n    }\n\n    pipeline = TaskPipeline(args, steps, programs)\n\n    pipeline.runProcessSteps()\n    \n    print(\"fullColmapProcess has finished successfully.\")\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/read_write_model.py",
    "content": "# Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#\n#     * Redistributions of source code must retain the above copyright\n#       notice, this list of conditions and the following disclaimer.\n#\n#     * Redistributions in binary form must reproduce the above copyright\n#       notice, this list of conditions and the following disclaimer in the\n#       documentation and/or other materials provided with the distribution.\n#\n#     * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of\n#       its contributors may be used to endorse or promote products derived\n#       from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE\n# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n# POSSIBILITY OF SUCH DAMAGE.\n#\n# Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)\n\nimport os\nimport collections\nimport numpy as np\nimport struct\nimport argparse\n\n\nCameraModel = collections.namedtuple(\n    \"CameraModel\", [\"model_id\", \"model_name\", \"num_params\"])\nCamera = collections.namedtuple(\n    \"Camera\", [\"id\", \"model\", \"width\", \"height\", \"params\"])\nBaseImage = collections.namedtuple(\n    \"Image\", [\"id\", \"qvec\", \"tvec\", \"camera_id\", \"name\", \"xys\", \"point3D_ids\"])\nPoint3D = collections.namedtuple(\n    \"Point3D\", [\"id\", \"xyz\", \"rgb\", \"error\", \"image_ids\", \"point2D_idxs\"])\n\n\nclass Image(BaseImage):\n    def qvec2rotmat(self):\n        return qvec2rotmat(self.qvec)\n\n\nCAMERA_MODELS = {\n    CameraModel(model_id=0, model_name=\"SIMPLE_PINHOLE\", num_params=3),\n    CameraModel(model_id=1, model_name=\"PINHOLE\", num_params=4),\n    CameraModel(model_id=2, model_name=\"SIMPLE_RADIAL\", num_params=4),\n    CameraModel(model_id=3, model_name=\"RADIAL\", num_params=5),\n    CameraModel(model_id=4, model_name=\"OPENCV\", num_params=8),\n    CameraModel(model_id=5, model_name=\"OPENCV_FISHEYE\", num_params=8),\n    CameraModel(model_id=6, model_name=\"FULL_OPENCV\", num_params=12),\n    CameraModel(model_id=7, model_name=\"FOV\", num_params=5),\n    CameraModel(model_id=8, model_name=\"SIMPLE_RADIAL_FISHEYE\", num_params=4),\n    CameraModel(model_id=9, model_name=\"RADIAL_FISHEYE\", num_params=5),\n    CameraModel(model_id=10, model_name=\"THIN_PRISM_FISHEYE\", num_params=12)\n}\nCAMERA_MODEL_IDS = dict([(camera_model.model_id, camera_model)\n                         for camera_model in CAMERA_MODELS])\nCAMERA_MODEL_NAMES = dict([(camera_model.model_name, camera_model)\n                           for camera_model in CAMERA_MODELS])\n\n\ndef read_next_bytes(fid, num_bytes, format_char_sequence, endian_character=\"<\"):\n    \"\"\"Read and unpack the next bytes from a binary file.\n    :param fid:\n    :param num_bytes: Sum of combination of {2, 4, 8}, e.g. 2, 6, 16, 30, etc.\n    :param format_char_sequence: List of {c, e, f, d, h, H, i, I, l, L, q, Q}.\n    :param endian_character: Any of {@, =, <, >, !}\n    :return: Tuple of read and unpacked values.\n    \"\"\"\n    data = fid.read(num_bytes)\n    return struct.unpack(endian_character + format_char_sequence, data)\n\n\ndef write_next_bytes(fid, data, format_char_sequence, endian_character=\"<\"):\n    \"\"\"pack and write to a binary file.\n    :param fid:\n    :param data: data to send, if multiple elements are sent at the same time,\n    they should be encapsuled either in a list or a tuple\n    :param format_char_sequence: List of {c, e, f, d, h, H, i, I, l, L, q, Q}.\n    should be the same length as the data list or tuple\n    :param endian_character: Any of {@, =, <, >, !}\n    \"\"\"\n    if isinstance(data, (list, tuple)):\n        bytes = struct.pack(endian_character + format_char_sequence, *data)\n    else:\n        bytes = struct.pack(endian_character + format_char_sequence, data)\n    fid.write(bytes)\n\n\ndef read_cameras_text(path):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::WriteCamerasText(const std::string& path)\n        void Reconstruction::ReadCamerasText(const std::string& path)\n    \"\"\"\n    cameras = {}\n    with open(path, \"r\") as fid:\n        while True:\n            line = fid.readline()\n            if not line:\n                break\n            line = line.strip()\n            if len(line) > 0 and line[0] != \"#\":\n                elems = line.split()\n                camera_id = int(elems[0])\n                model = elems[1]\n                width = int(elems[2])\n                height = int(elems[3])\n                params = np.array(tuple(map(float, elems[4:])))\n                cameras[camera_id] = Camera(id=camera_id, model=model,\n                                            width=width, height=height,\n                                            params=params)\n    return cameras\n\n\ndef read_cameras_binary(path_to_model_file):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::WriteCamerasBinary(const std::string& path)\n        void Reconstruction::ReadCamerasBinary(const std::string& path)\n    \"\"\"\n    cameras = {}\n    with open(path_to_model_file, \"rb\") as fid:\n        num_cameras = read_next_bytes(fid, 8, \"Q\")[0]\n        for _ in range(num_cameras):\n            camera_properties = read_next_bytes(\n                fid, num_bytes=24, format_char_sequence=\"iiQQ\")\n            camera_id = camera_properties[0]\n            model_id = camera_properties[1]\n            model_name = CAMERA_MODEL_IDS[camera_properties[1]].model_name\n            width = camera_properties[2]\n            height = camera_properties[3]\n            num_params = CAMERA_MODEL_IDS[model_id].num_params\n            params = read_next_bytes(fid, num_bytes=8*num_params,\n                                     format_char_sequence=\"d\"*num_params)\n            cameras[camera_id] = Camera(id=camera_id,\n                                        model=model_name,\n                                        width=width,\n                                        height=height,\n                                        params=np.array(params))\n        assert len(cameras) == num_cameras\n    return cameras\n\n\ndef write_cameras_text(cameras, path):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::WriteCamerasText(const std::string& path)\n        void Reconstruction::ReadCamerasText(const std::string& path)\n    \"\"\"\n    HEADER = \"# Camera list with one line of data per camera:\\n\" + \\\n             \"#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]\\n\" + \\\n             \"# Number of cameras: {}\\n\".format(len(cameras))\n    with open(path, \"w\") as fid:\n        fid.write(HEADER)\n        for _, cam in cameras.items():\n            to_write = [cam.id, cam.model, cam.width, cam.height, *cam.params]\n            line = \" \".join([str(elem) for elem in to_write])\n            fid.write(line + \"\\n\")\n\n\ndef write_cameras_binary(cameras, path_to_model_file):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::WriteCamerasBinary(const std::string& path)\n        void Reconstruction::ReadCamerasBinary(const std::string& path)\n    \"\"\"\n    with open(path_to_model_file, \"wb\") as fid:\n        write_next_bytes(fid, len(cameras), \"Q\")\n        for _, cam in cameras.items():\n            model_id = CAMERA_MODEL_NAMES[cam.model].model_id\n            camera_properties = [cam.id,\n                                 model_id,\n                                 cam.width,\n                                 cam.height]\n            write_next_bytes(fid, camera_properties, \"iiQQ\")\n            for p in cam.params:\n                write_next_bytes(fid, float(p), \"d\")\n    return cameras\n\n\ndef read_images_text(path):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadImagesText(const std::string& path)\n        void Reconstruction::WriteImagesText(const std::string& path)\n    \"\"\"\n    images = {}\n    with open(path, \"r\") as fid:\n        while True:\n            line = fid.readline()\n            if not line:\n                break\n            line = line.strip()\n            if len(line) > 0 and line[0] != \"#\":\n                elems = line.split()\n                image_id = int(elems[0])\n                qvec = np.array(tuple(map(float, elems[1:5])))\n                tvec = np.array(tuple(map(float, elems[5:8])))\n                camera_id = int(elems[8])\n                image_name = elems[9]\n                elems = fid.readline().split()\n                xys = np.column_stack([tuple(map(float, elems[0::3])),\n                                       tuple(map(float, elems[1::3]))])\n                point3D_ids = np.array(tuple(map(int, elems[2::3])))\n                images[image_id] = Image(\n                    id=image_id, qvec=qvec, tvec=tvec,\n                    camera_id=camera_id, name=image_name,\n                    xys=xys, point3D_ids=point3D_ids)\n    return images\n\n\ndef read_images_binary(path_to_model_file):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadImagesBinary(const std::string& path)\n        void Reconstruction::WriteImagesBinary(const std::string& path)\n    \"\"\"\n    images = {}\n    with open(path_to_model_file, \"rb\") as fid:\n        num_reg_images = read_next_bytes(fid, 8, \"Q\")[0]\n        for _ in range(num_reg_images):\n            binary_image_properties = read_next_bytes(\n                fid, num_bytes=64, format_char_sequence=\"idddddddi\")\n            image_id = binary_image_properties[0]\n            qvec = np.array(binary_image_properties[1:5])\n            tvec = np.array(binary_image_properties[5:8])\n            camera_id = binary_image_properties[8]\n            image_name = \"\"\n            current_char = read_next_bytes(fid, 1, \"c\")[0]\n            while current_char != b\"\\x00\":   # look for the ASCII 0 entry\n                image_name += current_char.decode(\"utf-8\")\n                current_char = read_next_bytes(fid, 1, \"c\")[0]\n            num_points2D = read_next_bytes(fid, num_bytes=8,\n                                           format_char_sequence=\"Q\")[0]\n            x_y_id_s = read_next_bytes(fid, num_bytes=24*num_points2D,\n                                       format_char_sequence=\"ddq\"*num_points2D)\n            xys = np.column_stack([tuple(map(float, x_y_id_s[0::3])),\n                                   tuple(map(float, x_y_id_s[1::3]))])\n            point3D_ids = np.array(tuple(map(int, x_y_id_s[2::3])))\n            images[image_id] = Image(\n                id=image_id, qvec=qvec, tvec=tvec,\n                camera_id=camera_id, name=image_name,\n                xys=xys, point3D_ids=point3D_ids)\n    return images\n\n\ndef write_images_text(images, path):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadImagesText(const std::string& path)\n        void Reconstruction::WriteImagesText(const std::string& path)\n    \"\"\"\n    if len(images) == 0:\n        mean_observations = 0\n    else:\n        mean_observations = sum((len(img.point3D_ids) for _, img in images.items()))/len(images)\n    HEADER = \"# Image list with two lines of data per image:\\n\" + \\\n             \"#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME\\n\" + \\\n             \"#   POINTS2D[] as (X, Y, POINT3D_ID)\\n\" + \\\n             \"# Number of images: {}, mean observations per image: {}\\n\".format(len(images), mean_observations)\n\n    with open(path, \"w\") as fid:\n        fid.write(HEADER)\n        for _, img in images.items():\n            image_header = [img.id, *img.qvec, *img.tvec, img.camera_id, img.name]\n            first_line = \" \".join(map(str, image_header))\n            fid.write(first_line + \"\\n\")\n\n            points_strings = []\n            for xy, point3D_id in zip(img.xys, img.point3D_ids):\n                points_strings.append(\" \".join(map(str, [*xy, point3D_id])))\n            fid.write(\" \".join(points_strings) + \"\\n\")\n\n\ndef write_images_binary(images, path_to_model_file):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadImagesBinary(const std::string& path)\n        void Reconstruction::WriteImagesBinary(const std::string& path)\n    \"\"\"\n    with open(path_to_model_file, \"wb\") as fid:\n        write_next_bytes(fid, len(images), \"Q\")\n        for _, img in images.items():\n            write_next_bytes(fid, img.id, \"i\")\n            write_next_bytes(fid, img.qvec.tolist(), \"dddd\")\n            write_next_bytes(fid, img.tvec.tolist(), \"ddd\")\n            write_next_bytes(fid, img.camera_id, \"i\")\n            for char in img.name:\n                write_next_bytes(fid, char.encode(\"utf-8\"), \"c\")\n            write_next_bytes(fid, b\"\\x00\", \"c\")\n            write_next_bytes(fid, len(img.point3D_ids), \"Q\")\n            for xy, p3d_id in zip(img.xys, img.point3D_ids):\n                write_next_bytes(fid, [*xy, p3d_id], \"ddq\")\n\n\ndef read_points3D_text(path):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadPoints3DText(const std::string& path)\n        void Reconstruction::WritePoints3DText(const std::string& path)\n    \"\"\"\n    points3D = {}\n    with open(path, \"r\") as fid:\n        while True:\n            line = fid.readline()\n            if not line:\n                break\n            line = line.strip()\n            if len(line) > 0 and line[0] != \"#\":\n                elems = line.split()\n                point3D_id = int(elems[0])\n                xyz = np.array(tuple(map(float, elems[1:4])))\n                rgb = np.array(tuple(map(int, elems[4:7])))\n                error = float(elems[7])\n                image_ids = np.array(tuple(map(int, elems[8::2])))\n                point2D_idxs = np.array(tuple(map(int, elems[9::2])))\n                points3D[point3D_id] = Point3D(id=point3D_id, xyz=xyz, rgb=rgb,\n                                               error=error, image_ids=image_ids,\n                                               point2D_idxs=point2D_idxs)\n    return points3D\n\n\ndef read_points3D_binary(path_to_model_file):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadPoints3DBinary(const std::string& path)\n        void Reconstruction::WritePoints3DBinary(const std::string& path)\n    \"\"\"\n    points3D = {}\n    with open(path_to_model_file, \"rb\") as fid:\n        num_points = read_next_bytes(fid, 8, \"Q\")[0]\n        for _ in range(num_points):\n            binary_point_line_properties = read_next_bytes(\n                fid, num_bytes=43, format_char_sequence=\"QdddBBBd\")\n            point3D_id = binary_point_line_properties[0]\n            xyz = np.array(binary_point_line_properties[1:4])\n            rgb = np.array(binary_point_line_properties[4:7])\n            error = np.array(binary_point_line_properties[7])\n            track_length = read_next_bytes(\n                fid, num_bytes=8, format_char_sequence=\"Q\")[0]\n            track_elems = read_next_bytes(\n                fid, num_bytes=8*track_length,\n                format_char_sequence=\"ii\"*track_length)\n            image_ids = np.array(tuple(map(int, track_elems[0::2])))\n            point2D_idxs = np.array(tuple(map(int, track_elems[1::2])))\n            points3D[point3D_id] = Point3D(\n                id=point3D_id, xyz=xyz, rgb=rgb,\n                error=error, image_ids=image_ids,\n                point2D_idxs=point2D_idxs)\n    return points3D\n\n\ndef write_points3D_text(points3D, path):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadPoints3DText(const std::string& path)\n        void Reconstruction::WritePoints3DText(const std::string& path)\n    \"\"\"\n    if len(points3D) == 0:\n        mean_track_length = 0\n    else:\n        mean_track_length = sum((len(pt.image_ids) for _, pt in points3D.items()))/len(points3D)\n    HEADER = \"# 3D point list with one line of data per point:\\n\" + \\\n             \"#   POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)\\n\" + \\\n             \"# Number of points: {}, mean track length: {}\\n\".format(len(points3D), mean_track_length)\n\n    with open(path, \"w\") as fid:\n        fid.write(HEADER)\n        for _, pt in points3D.items():\n            point_header = [pt.id, *pt.xyz, *pt.rgb, pt.error]\n            fid.write(\" \".join(map(str, point_header)) + \" \")\n            track_strings = []\n            for image_id, point2D in zip(pt.image_ids, pt.point2D_idxs):\n                track_strings.append(\" \".join(map(str, [image_id, point2D])))\n            fid.write(\" \".join(track_strings) + \"\\n\")\n\n\ndef write_points3D_binary(points3D, path_to_model_file):\n    \"\"\"\n    see: src/base/reconstruction.cc\n        void Reconstruction::ReadPoints3DBinary(const std::string& path)\n        void Reconstruction::WritePoints3DBinary(const std::string& path)\n    \"\"\"\n    with open(path_to_model_file, \"wb\") as fid:\n        write_next_bytes(fid, len(points3D), \"Q\")\n        for _, pt in points3D.items():\n            write_next_bytes(fid, pt.id, \"Q\")\n            write_next_bytes(fid, pt.xyz.tolist(), \"ddd\")\n            write_next_bytes(fid, pt.rgb.tolist(), \"BBB\")\n            write_next_bytes(fid, pt.error, \"d\")\n            track_length = pt.image_ids.shape[0]\n            write_next_bytes(fid, track_length, \"Q\")\n            for image_id, point2D_id in zip(pt.image_ids, pt.point2D_idxs):\n                write_next_bytes(fid, [image_id, point2D_id], \"ii\")\n\n\ndef detect_model_format(path, ext):\n    if os.path.isfile(os.path.join(path, \"cameras\"  + ext)) and \\\n       os.path.isfile(os.path.join(path, \"images\"   + ext)) and \\\n       os.path.isfile(os.path.join(path, \"points3D\" + ext)):\n        print(\"Detected model format: '\" + ext + \"'\")\n        return True\n\n    return False\n\n\ndef read_model(path, ext=\"\"):\n    # try to detect the extension automatically\n    if ext == \"\":\n        if detect_model_format(path, \".bin\"):\n            ext = \".bin\"\n        elif detect_model_format(path, \".txt\"):\n            ext = \".txt\"\n        else:\n            print(\"Provide model format: '.bin' or '.txt'\")\n            return\n\n    if ext == \".txt\":\n        cameras = read_cameras_text(os.path.join(path, \"cameras\" + ext))\n        images = read_images_text(os.path.join(path, \"images\" + ext))\n        points3D = read_points3D_text(os.path.join(path, \"points3D\") + ext)\n    else:\n        cameras = read_cameras_binary(os.path.join(path, \"cameras\" + ext))\n        images = read_images_binary(os.path.join(path, \"images\" + ext))\n        points3D = read_points3D_binary(os.path.join(path, \"points3D\") + ext)\n    return cameras, images, points3D\n\n\ndef write_model(cameras, images, points3D, path, ext=\".bin\"):\n    if ext == \".txt\":\n        write_cameras_text(cameras, os.path.join(path, \"cameras\" + ext))\n        write_images_text(images, os.path.join(path, \"images\" + ext))\n        write_points3D_text(points3D, os.path.join(path, \"points3D\") + ext)\n    else:\n        write_cameras_binary(cameras, os.path.join(path, \"cameras\" + ext))\n        write_images_binary(images, os.path.join(path, \"images\" + ext))\n        write_points3D_binary(points3D, os.path.join(path, \"points3D\") + ext)\n    return cameras, images, points3D\n\n\ndef qvec2rotmat(qvec):\n    return np.array([\n        [1 - 2 * qvec[2]**2 - 2 * qvec[3]**2,\n         2 * qvec[1] * qvec[2] - 2 * qvec[0] * qvec[3],\n         2 * qvec[3] * qvec[1] + 2 * qvec[0] * qvec[2]],\n        [2 * qvec[1] * qvec[2] + 2 * qvec[0] * qvec[3],\n         1 - 2 * qvec[1]**2 - 2 * qvec[3]**2,\n         2 * qvec[2] * qvec[3] - 2 * qvec[0] * qvec[1]],\n        [2 * qvec[3] * qvec[1] - 2 * qvec[0] * qvec[2],\n         2 * qvec[2] * qvec[3] + 2 * qvec[0] * qvec[1],\n         1 - 2 * qvec[1]**2 - 2 * qvec[2]**2]])\n\n\ndef rotmat2qvec(R):\n    Rxx, Ryx, Rzx, Rxy, Ryy, Rzy, Rxz, Ryz, Rzz = R.flat\n    K = np.array([\n        [Rxx - Ryy - Rzz, 0, 0, 0],\n        [Ryx + Rxy, Ryy - Rxx - Rzz, 0, 0],\n        [Rzx + Rxz, Rzy + Ryz, Rzz - Rxx - Ryy, 0],\n        [Ryz - Rzy, Rzx - Rxz, Rxy - Ryx, Rxx + Ryy + Rzz]]) / 3.0\n    eigvals, eigvecs = np.linalg.eigh(K)\n    qvec = eigvecs[[3, 0, 1, 2], np.argmax(eigvals)]\n    if qvec[0] < 0:\n        qvec *= -1\n    return qvec\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Read and write COLMAP binary and text models\")\n    parser.add_argument(\"--input_model\", help=\"path to input model folder\")\n    parser.add_argument(\"--input_format\", choices=[\".bin\", \".txt\"],\n                        help=\"input model format\", default=\"\")\n    parser.add_argument(\"--output_model\",\n                        help=\"path to output model folder\")\n    parser.add_argument(\"--output_format\", choices=[\".bin\", \".txt\"],\n                        help=\"outut model format\", default=\".txt\")\n    args = parser.parse_args()\n\n    cameras, images, points3D = read_model(path=args.input_model, ext=args.input_format)\n\n    print(\"num_cameras:\", len(cameras))\n    print(\"num_images:\", len(images))\n    print(\"num_points3D:\", len(points3D))\n\n    if args.output_model is not None:\n        write_model(cameras, images, points3D, path=args.output_model, ext=args.output_format)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/selectiveColmapProcess.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\n\"\"\" @package dataset_tools_preprocess\nThis script runs a pipeline to create Colmap reconstruction data; you can choose which steps to do and it also handles test path data which are only calibrated\n\nParameters: -h help,\n            -path <path to your dataset folder>,\n            -sibrBinariesPath <binaries directory of SIBR>,\n            -colmapPath <colmap path directory which contains colmap.bat / colmap.bin>,\n            -quality <quality of the reconstruction : 'low', 'medium', 'high', 'extreme'>,\n\nUsage: python selectiveColmapProcess.py -path <path to your dataset folder>\n                                   -sibrBinariesPath <binaries directory of SIBR>\n                                   -colmapPath <colmap path directory which contains colmap.bat / colmap.bin>\n                                   -quality <quality of the reconstruction : 'low', 'medium', 'high', 'extreme'>\n\n\"\"\"\n\nimport os, sys, shutil\nimport json\nimport argparse\nfrom utils.paths import getBinariesPath, getColmapPath, getMeshlabPath\nfrom utils.commands import  getProcess, getColmap\nfrom utils.TaskPipeline import TaskPipeline\nimport selective_colmap_process\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--path\", type=str, required=True, help=\"path to your dataset folder\")\n    parser.add_argument(\"--sibrBinariesPath\", type=str, default=getBinariesPath(), help=\"binaries directory of SIBR\")\n    parser.add_argument(\"--colmapPath\", type=str, default=getColmapPath(), help=\"path to directory colmap.bat / colmap.bin directory\")\n    parser.add_argument(\"--meshlabPath\", type=str, default=getMeshlabPath(), help=\"path to meshlabserver directory\")\n    parser.add_argument(\"--quality\", type=str, default='default', choices=['default', 'low', 'medium', 'average', 'high', 'extreme'],\n        help=\"quality of the reconstruction\")\n    parser.add_argument(\"--dry_run\", action='store_true', help=\"run without calling commands\")\n    parser.add_argument(\"--with_texture\", action='store_true', help=\"Add texture steps\")\n    parser.add_argument(\"--create_sibr_scene\", action='store_true', help=\"Create SIBR scene\")\n    parser.add_argument(\"--meshsize\", type=str, help=\"size of the output mesh in K polygons (ie 200 == 200,000 polygons). Values allowed: 200, 250, 300, 350, 400\")\n\n    # additional step choice presets\n    parser.add_argument(\"--from_step\", type=str, default='default', help=\"Run from this step to --to_step\")\n    parser.add_argument(\"--to_step\", type=str, default='default', help=\"up to but *excluding* this step (from --from_step); must be unique steps\")\n    parser.add_argument(\"--exclude_steps\", type=str, default='default', help=\"Ignore these steps (string with space separated names of steps)\")\n    parser.add_argument(\"--calibrate_only\", action='store_true', help=\"Do calibration only (from dataset_build_structure to before colmap_path_match_stereo with no special test frame processing\")\n    parser.add_argument(\"--calibrate_only_process_test\", action='store_true', help=\"Do calibration only (from dataset_build_structure to before colmap_path_match_stereo with test frame processing\")\n    parser.add_argument(\"--mvs_only\", action='store_true', help=\"Do mvs only (from colmap_path_match_stereo onwards without test frame processing\")\n    parser.add_argument(\"--fix_cameras\", action='store_true', help=\"Do fix camera step only\")\n    parser.add_argument(\"--fix_cameras_stereo\", action='store_true', help=\"Do fix camera step only for stereo/sparse\")\n    \n    #colmap performance arguments\n    parser.add_argument(\"--numGPUs\", type=int, default=2, help=\"number of GPUs allocated to Colmap\")\n\n    # Feature extractor \n    parser.add_argument(\"--SiftExtraction.max_image_size\", type=int, dest=\"siftExtraction_ImageSize\")\n    parser.add_argument(\"--SiftExtraction.estimate_affine_shape\", type=int, dest=\"siftExtraction_EstimateAffineShape\") \n    parser.add_argument(\"--SiftExtraction.domain_size_pooling\", type=int, dest=\"siftExtraction_DomainSizePooling\")\n    parser.add_argument(\"--SiftExtraction.max_num_features\", type=int, dest=\"siftExtraction_MaxNumFeatures\")\n    parser.add_argument(\"--ImageReader.single_camera\", type=int, dest=\"imageReader_SingleCamera\")\n\n    # Exhaustive matcher\n    parser.add_argument(\"--ExhaustiveMatching.block_size\", type=int, dest=\"exhaustiveMatcher_ExhaustiveMatchingBlockSize\")\n\n    # Mapper\n    parser.add_argument(\"--Mapper.ba_local_max_num_iterations\", type=int, dest=\"mapper_MapperDotbaLocalMaxNumIterations\")\n    parser.add_argument(\"--Mapper.ba_global_max_num_iterations\", type=int, dest=\"mapper_MapperDotbaGlobalMaxNumIterations\")\n    parser.add_argument(\"--Mapper.ba_global_images_ratio\", type=float, dest=\"mapper_MapperDotbaGlobalImagesRatio\")\n    parser.add_argument(\"--Mapper.ba_global_points_ratio\", type=float, dest=\"mapper_MapperDotbaGlobalPointsRatio\")\n    parser.add_argument(\"--Mapper.ba_global_max_refinements\", type=int, dest=\"mapper_MapperDotbaGlobalMaxRefinements\")\n    parser.add_argument(\"--Mapper.ba_local_max_refinements\", type=int, dest=\"mapper_MapperDotbaLocalMaxRefinements\")\n\n    \n    # Patch match stereo\n    parser.add_argument(\"--PatchMatchStereo.max_image_size\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotMaxImageSize\")\n    parser.add_argument(\"--PatchMatchStereo.window_radius\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotWindowRadius\")\n    parser.add_argument(\"--PatchMatchStereo.window_step\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotWindowStep\")\n    parser.add_argument(\"--PatchMatchStereo.num_samples\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotNumSamples\")\n    parser.add_argument(\"--PatchMatchStereo.num_iterations\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotNumIterations\")\n    parser.add_argument(\"--PatchMatchStereo.geom_consistency\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotGeomConsistency\")\n\n    # Stereo fusion\n    parser.add_argument(\"--StereoFusion.check_num_images\", type=int, dest=\"stereoFusion_CheckNumImages\")\n    parser.add_argument(\"--StereoFusion.max_image_size\", type=int, dest=\"stereoFusion_MaxImageSize\")\n\n    args = vars(parser.parse_args())\n\n    # Update args with quality values\n    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), \"ColmapQualityParameters.json\"), \"r\") as qualityParamsFile:\n        qualityParams = json.load(qualityParamsFile)\n\n        for key, value in qualityParams.items():\n            if not key in args or args[key] is None:\n                args[key] = qualityParams[key][args[\"quality\"]] if args[\"quality\"] in qualityParams[key] else qualityParams[key][\"default\"]\n\n    # Get process steps\n    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), \"SelectiveColmapProcessSteps.json\"), \"r\") as processStepsFile:\n        steps = json.load(processStepsFile)[\"steps\"]\n\n    # apply selective processing\n    exclude_steps = []\n    if( args[\"exclude_steps\"] != 'default' ):\n        exclude_steps = args[\"exclude_steps\"].split()\n\n    from_step = args[\"from_step\"]\n    to_step = args[\"to_step\"]\n\n    # treat pre-defined cases\n    if( args[\"fix_cameras\"] or args[\"fix_cameras_stereo\"]):\n        new_steps = []\n        for s in steps:\n            if( s['name'] == \"fix_cameras\" ):\n                print(s)\n                (s['function_args'])['photoName'] = \"00\"\n                if args[\"fix_cameras_stereo\"]:\n                    (s['function_args'])['sparse_subdir'] = os.path.join(os.path.join(\"colmap\", \"stereo\"), \"sparse\")\n                new_steps.append(s)\n                break\n        steps = new_steps\n\n    if( args[\"calibrate_only\"] or args[\"calibrate_only_process_test\"] ):\n        from_step = \"build_dataset_structure\" \n        to_step = \"colmap_patch_match_stereo\" \n        if( args[\"calibrate_only\"] ):\n            exclude_steps = [ \"extract_video_frames\", \"fix_cameras\", \"colmap_image_deleter_colmap\", \"remove_video_images\" ]\n\n    if( args[\"mvs_only\"] ):\n        from_step = \"colmap_patch_match_stereo\" \n        to_step = \"use_eol_fixed_mesh\"\n    \n\n    if( from_step != 'default' ):\n        # check if to_step exists\n        if( to_step != 'default' ):\n            # select steps\n            newsteps = []\n            adding_steps = False\n            for s in steps:\n                if( s['name'] == from_step ):\n                    adding_steps = True\n                # special case for last step that should be added\n                if( s['name'] == to_step and s['name'] != \"use_eol_fixed_mesh\" ):\n                    break\n                if adding_steps :\n                    if not (s['name'] in exclude_steps):\n                        newsteps.append(s)\n            steps = newsteps\n        else:\n            print(\"--from_step given without --to_step; ignoring\")\n\n    if args[\"dry_run\"]:\n        print(\"Keeping only following steps: \")\n        for s in steps:\n           print (s['name'])\n\n    # Fixing path values\n    args[\"path\"] = os.path.abspath(args[\"path\"])\n    args[\"sibrBinariesPath\"] = os.path.abspath(args[\"sibrBinariesPath\"])\n    args[\"colmapPath\"] = os.path.abspath(args[\"colmapPath\"])\n\n    args[\"gpusIndices\"] = ','.join([str(i) for i in range(args[\"numGPUs\"])])\n\n    programs = {\n        \"colmap\": {\n            \"path\": getColmap(args[\"colmapPath\"])\n        },\n        \"unwrapMesh\": {\n            \"path\": getProcess(\"unwrapMesh\", args[\"sibrBinariesPath\"])\n        },\n        \"textureMesh\": {\n            \"path\": getProcess(\"textureMesh\", args[\"sibrBinariesPath\"])\n        },\n    }\n\n    pipeline = TaskPipeline(args, steps, programs)\n\n    pipeline.runProcessSteps()\n    \n    print(\"selectiveColmapProcess has finished successfully.\")\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/selective_colmap_process.py",
    "content": "import os\r\nimport os.path\r\nimport sys\r\nimport argparse\r\nimport shutil\r\nimport sqlite3\r\nimport read_write_model as rwm\r\n\r\n\r\nimport cv2\r\nprint(cv2.__version__)\r\n\r\n\r\ndef extract_images(pathIn, pathOut, videoName, maxNumFrames = -1, resize=False):\r\n    EVERY_NTH = 2\r\n    count = 0\r\n    vidcap = cv2.VideoCapture(pathIn)\r\n    fps = round(vidcap.get(cv2.CAP_PROP_FPS))\r\n    total_frames = vidcap.get(7)/EVERY_NTH\r\n    print(\"FPS = \", fps)\r\n    success,image = vidcap.read()\r\n    success = True\r\n    print(\"Extracting \", total_frames, \" Frames\" )\r\n    fileNames = []\r\n    newFolder = pathOut + \"\\\\%s\" % (videoName)\r\n    if not os.path.exists(newFolder):\r\n      print( \"Creating: \", newFolder)\r\n      os.makedirs(newFolder, exist_ok=True)\r\n\r\n    for frame in range(round(total_frames)):\r\n        # every Nth frame\r\n        vidcap.set(cv2.CAP_PROP_POS_FRAMES,(EVERY_NTH*frame))\r\n        success,image = vidcap.read()\r\n        if not success:\r\n           break\r\n        resized = image\r\n        if resize :\r\n            #print('Original Dimensions : ',image.shape)\r\n            scale_percent = 52 # percent of original size\r\n            width = int(image.shape[1] * scale_percent / 100)\r\n            height = int(image.shape[0] * scale_percent / 100)\r\n            dim = (width, height)\r\n            resized = cv2.resize(image, dim, interpolation = cv2.INTER_AREA)\r\n\r\n        print( \"Writing: frame # \", EVERY_NTH*frame, \" \" , pathOut + \"\\\\%s\\\\frame%04d.png\" % (videoName, count))     \r\n        fileNames.append(\"%s\\\\frame%04d.png\" % (videoName, count))     \r\n        cv2.imwrite( pathOut + \"\\\\%s\\\\frame%04d.png\" % (videoName, count), resized)     # save frame as PNG file\r\n \r\n        if maxNumFrames == count:\r\n           break;\r\n\r\n        count = count + 1\r\n\r\n    return fileNames\r\n\r\ndef extract_images_with_name(imageName, images_data, new_images_data):\r\n   add_next = False\r\n   new_images_data.append(images_data[0])\r\n   new_images_data.append(images_data[1])\r\n   new_images_data.append(images_data[2])\r\n   new_images_data.append(images_data[3])\r\n\r\n   img_cnt = 0\r\n   # create list with photo-only images\r\n   for line in images_data:\r\n       if line.split():\r\n           if imageName in line.split()[-1]:\r\n              new_images_data.append(line)\r\n              img_cnt = img_cnt+1\r\n              add_next = True\r\n           elif add_next:\r\n              new_images_data.append(line)\r\n              add_next = False\r\n           else:\r\n              add_next = False\r\n\r\n   return new_images_data, img_cnt\r\n\r\ndef remove_lines_from_file(fname, match, nextDel=False):\r\n    newdata = []\r\n    prevMatch = False\r\n    with open(fname, 'r') as imagesfile:\r\n        data = imagesfile.read().splitlines()\r\n    for line in data:\r\n        if match in line:\r\n           if nextDel:\r\n              prevMatch = True\r\n        elif (not match in line) and (not prevMatch):\r\n           newdata.append(line)\r\n           prevMatch = False\r\n        else:\r\n           prevMatch = False\r\n\r\n    # overwrite\r\n    with open(fname, 'w') as outfile:\r\n         for line in newdata:\r\n            outfile.write(line + \"\\n\")\r\n    \r\n\r\n\r\ndef remove_video_images(path, photoName=\"MG_\"):\r\n   # make backups\r\n   oldb = os.path.abspath(os.path.join(path, \"colmap\\\\dataset.db\" )) # will be modified\r\n   backuppath = dstpath = os.path.join(path, \"backups\\\\two_cams_all_images\\\\\")\r\n   if not os.path.exists(dstpath):\r\n      os.makedirs(dstpath, exist_ok=True)\r\n\r\n   dbfile = os.path.abspath(os.path.join(dstpath,\"dataset.db\"))\r\n   oldb = os.path.abspath(os.path.join(path, \"colmap\\\\dataset.db\" )) # will be modified\r\n   if not os.path.exists(dbfile):\r\n       shutil.copyfile(oldb, dbfile)\r\n\r\n\r\n   # Read images.txt & cameras.txt\r\n   backup_images = os.path.abspath(os.path.join(dstpath,\"images.txt\"))\r\n   images_fname = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\")) + \"\\\\images.txt\"\r\n   if not os.path.exists(backup_images):\r\n       shutil.copyfile(images_fname, backup_images)\r\n\r\n   backup_cameras = os.path.abspath(os.path.join(dstpath,\"cameras.txt\"))\r\n   cameras_fname = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\")) + \"\\\\cameras.txt\"\r\n   if not os.path.exists(backup_cameras):\r\n       shutil.copyfile(cameras_fname, backup_cameras)\r\n\r\n   # extract photos only for images.txt\r\n   with open(images_fname, 'r') as imagesfile:\r\n       images_data = imagesfile.read().splitlines()\r\n\r\n   videoDirList = []\r\n   imagespath = os.path.abspath(os.path.join(path, \"images\"))\r\n   for filename in os.listdir(imagespath):\r\n       if \"Video\" in filename:\r\n          videoDirList.append(filename)\r\n\r\n   new_images_data = []\r\n   new_images_data, img_cnt = extract_images_with_name(photoName, images_data, new_images_data)\r\n\r\n   # remaining images\r\n   print(\"Remaining images \", img_cnt)\r\n   \r\n   new_images_data[3] = ' '.join(images_data[3].split()[0:4]) + \" \" + str(img_cnt) +\" \" +  ' '.join(images_data[3].split()[5:-1] )\r\n\r\n   # overwrite current calibration\r\n   dstpath = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\"))\r\n   with open(dstpath+\"\\\\images.txt\", 'w') as outfile:\r\n     for line in new_images_data:\r\n       outfile.write(str(line) + \"\\n\")\r\n\r\n   outfile.close()\r\n   print(\"Writing fixed images \", dstpath + \"\\\\images.txt\")\r\n   ims = rwm.read_images_text(dstpath + \"\\\\images.txt\")\r\n   rwm.write_images_binary(ims, dstpath + \"\\\\images.bin\")\r\n\r\n   # fix the database\r\n\r\n   # open the database\r\n   db = sqlite3.connect(oldb)\r\n   cursor = db.cursor()\r\n   cursor.execute(\"SELECT name FROM sqlite_master WHERE type='table';\")\r\n   tables = cursor.fetchall()\r\n   # debug\r\n   delImagesQuery = \"\"\"DELETE from images WHERE name LIKE '%Video%'\"\"\"\r\n   cursor.execute(delImagesQuery)\r\n\r\n   db.commit()\r\n\r\n   # write out database \r\n   db.close()\r\n\r\n   # create GT path directories\r\n   dstpath = os.path.abspath(os.path.join(path, \"paths_GT\"))\r\n\r\n   if not os.path.exists(dstpath):\r\n      print(\"Creating \", dstpath)\r\n      os.makedirs(dstpath)\r\n\r\n\r\n   # backup original (distorted) video images & move undistorted images to special directories\r\n   # then create the colmap data for each\r\n\r\n   for currVideoName in videoDirList:\r\n      # move the original videos to backup\r\n      imagespath = os.path.abspath(os.path.join(path, \"images\"))\r\n      shutil.move(imagespath + \"\\\\\" + currVideoName, backuppath+ \"\\\\\" + currVideoName)\r\n\r\n      # create GT_path dir\r\n      dstpath = os.path.abspath(os.path.join(path, \"paths_GT\"))\r\n      curr_GTpath_dir = dstpath + \"\\\\\" + currVideoName\r\n      print(\"Creating \", curr_GTpath_dir)\r\n      os.makedirs(curr_GTpath_dir)\r\n      os.makedirs(curr_GTpath_dir+\"\\\\images\")\r\n\r\n      # move undistorted mages to GT_path dir\r\n      imagespath = path +  \"\\\\colmap\\\\stereo\\\\images\\\\\"+ currVideoName\r\n      shutil.move(imagespath, curr_GTpath_dir + \"\\\\images\")\r\n\r\n      video_images_list = []\r\n      video_images_list, img_cnt = extract_images_with_name(currVideoName, images_data, video_images_list)\r\n      video_images_list[2] = ' '.join(images_data[2].split()[0:4]) + \" \" + str(img_cnt) +\" \" +  ' '.join(images_data[2].split()[5:-1] )\r\n\r\n      # create colmap data\r\n      dstpath = os.path.abspath(os.path.join(curr_GTpath_dir, \"text\"))\r\n      os.makedirs(dstpath)\r\n\r\n      with open(dstpath+\"\\\\images.txt\", 'w') as outfile:\r\n         for line in video_images_list:\r\n           outfile.write(str(line) + \"\\n\")\r\n\r\n      cameras_fname = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\")) + \"\\\\cameras.txt\"\r\n      shutil.copyfile(cameras_fname, dstpath+\"\\\\cameras.txt\")\r\n\r\n      points_fname = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\")) + \"\\\\points3D.txt\"\r\n      shutil.copyfile(points_fname, dstpath+\"\\\\points3D.txt\")\r\n\r\n      fname = os.path.abspath(os.path.join(path, \"colmap\\\\stereo\\\\stereo\\\\fusion.cfg\")) \r\n      remove_lines_from_file(fname, \"Video\", False)\r\n      fname = os.path.abspath(os.path.join(path, \"colmap\\\\stereo\\\\stereo\\\\patch-match.cfg\")) \r\n      remove_lines_from_file(fname, \"Video\", True)\r\n      # all done\r\n\r\n\r\ndef fix_cameras(path, photoName=\"MG_\", sparseSubdir=\"\"):\r\n    if sparseSubdir == \"\":\r\n        sparse_subdir = os.path.join(\"colmap\", \"sparse\")\r\n    else:\r\n        sparse_subdir = sparseSubdir\r\n\r\n    # Read images.txt\r\n    images_fname = os.path.abspath(os.path.join(path, sparse_subdir)) + \"\\\\images.txt\"\r\n    with open(images_fname, 'r') as imagesfile:\r\n        images_data = imagesfile.read().splitlines()\r\n\r\n    # Read cameras.txt\r\n    cameras_fname = os.path.abspath(os.path.join(path, sparse_subdir)) + \"\\\\cameras.txt\"\r\n    with open(cameras_fname, 'r') as camerasfile:\r\n        cameras_data = camerasfile.read().splitlines()\r\n\r\n    # find the first camera index for a photo\r\n    photoCamIndex = -1\r\n    for line in images_data:\r\n        if line.split():\r\n            if (photoName in line.split()[-1])  and (int(line.split()[0]) > 2):\r\n              photoCamIndex = line.split()[0]\r\n              print(\"Found Photo Camera Index \", photoCamIndex, \" for camera \", line.split()[-1])\r\n              break\r\n\r\n    # find the first camera index for a video\r\n    videoCamIndex = -1\r\n    for line in images_data:\r\n        if line.split():\r\n            if (\"Video\" in line.split()[-1]) and ( int(line.split()[0]) > 2):\r\n              videoCamIndex = line.split()[0]\r\n              print(\"Found Video Camera Index \", videoCamIndex, \" for camera \", line.split()[-1])\r\n              break\r\n\r\n    # make backups of original files\r\n    dstpath = os.path.join(path, \"backups\\\\\")\r\n    if not os.path.exists(dstpath):\r\n       os.makedirs(dstpath, exist_ok=True)\r\n    # \r\n    dstpath = os.path.join(path, \"backups\\\\orig\\\\\")\r\n    if not os.path.exists(dstpath):\r\n       os.makedirs(dstpath, exist_ok=True)\r\n\r\n    # make backups of original files\r\n    if not os.path.exists(dstpath+\"\\\\images.txt\"):\r\n       shutil.copyfile(images_fname, dstpath +\"\\\\images.txt\")\r\n    if not os.path.exists(dstpath+\"\\\\cameras.txt\"):\r\n       shutil.copyfile(cameras_fname, dstpath +\"\\\\cameras.txt\")\r\n\r\n    dbfile = os.path.abspath(os.path.join(dstpath,\"dataset.db\"))\r\n    oldb = os.path.abspath(os.path.join(path, \"colmap\\\\dataset.db\" )) # will be modified\r\n    if os.path.exists(oldb): # only do DB processing if it exists\r\n        print(\"Old \", oldb, \" new \", dbfile, \" path \", path)\r\n        if not os.path.exists(dbfile):\r\n           shutil.copyfile(oldb, dbfile)\r\n\r\n        # open the database\r\n        db = sqlite3.connect(oldb)\r\n        cursor = db.cursor()\r\n        cursor.execute(\"SELECT name FROM sqlite_master WHERE type='table';\")\r\n        tables = cursor.fetchall()\r\n        # debug\r\n        #  table = pd.read_sql_query(\"SELECT * from %s\" % 'images', db)\r\n\r\n        # delete all cameras except videoCamEntry and photoCamEntry from database\r\n        delCamQuery = \"\"\"DELETE from cameras WHERE camera_id != '%s' and camera_id != '%s'\"\"\" % (videoCamIndex, photoCamIndex)\r\n        cursor.execute(delCamQuery )\r\n\r\n        # change photo cam id to 1 and video cam id to 2\r\n        setQuery = \"UPDATE cameras SET camera_id = '%s' WHERE  camera_id = '%s'\" % (\"1\", photoCamIndex)\r\n        cursor.execute(setQuery)\r\n\r\n        setQuery = \"UPDATE cameras SET camera_id = '%s' WHERE  camera_id = '%s'\" % (\"2\", videoCamIndex)\r\n        cursor.execute(setQuery)\r\n\r\n        # change photo cam id to 1 for all images of photos\r\n        setQuery = \"UPDATE images SET camera_id = '1' WHERE name LIKE '%MG_%'\"\r\n        cursor.execute(setQuery)\r\n\r\n        # change video cam id to 2 for all images\r\n        setQuery = \"UPDATE images SET camera_id = '2' WHERE name LIKE '%Video%'\"\r\n        cursor.execute(setQuery)\r\n\r\n        db.commit()\r\n\r\n        # write out database ; next step re-exports the result to TXT and BIN\r\n        db.close()\r\n      \r\n    # replace all camera indices in images.txt\r\n    new_images_data = images_data\r\n    cnt = 0\r\n    for line in new_images_data:\r\n        if line.split():\r\n            if photoName in line.split()[-1]:\r\n               new_images_data[cnt] = ' '.join(line.split()[0:-2]) + \" 1 \" + line.split()[-1] \r\n               print(\"replace \", line.split()[0],  \" by 1 in \", new_images_data[cnt] )\r\n            elif \"Video\" in line.split()[-1]:\r\n               new_images_data[cnt] = ' '.join(line.split()[0:-2]) + \" 2 \" + line.split()[-1] \r\n\r\n        cnt = cnt + 1\r\n\r\n    videoCamEntry = \"\"\r\n    for line in cameras_data:\r\n        if line.split():\r\n            if photoCamIndex == line.split()[0]:\r\n              photoCamEntry = \"1 \" + ' '.join(line.split()[1:])\r\n            if videoCamIndex == line.split()[0]:\r\n              videoCamEntry = \"2 \" + ' '.join(line.split()[1:])\r\n\r\n    # create two element camera file one for photos one for video\r\n    dstpath = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\"))\r\n    dst = dstpath + \"\\\\cameras_two.txt\"\r\n    with open(dst, 'w') as outfile:\r\n        outfile.write(photoCamEntry + \"\\n\")\r\n        if( videoCamEntry != \"\" ):\r\n            outfile.write(videoCamEntry + \"\\n\")\r\n    outfile.close()\r\n\r\n    # write out new file\r\n    dst = dstpath + \"\\\\images_two.txt\"\r\n    with open(dst, 'w') as outfile:\r\n         for line in new_images_data:\r\n            outfile.write(line + \"\\n\")\r\n    outfile.close()\r\n\r\n    # replace files\r\n    dstpath = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\"))\r\n    shutil.move(dstpath + \"\\\\images_two.txt\", images_fname)\r\n    shutil.move(dstpath + \"\\\\cameras_two.txt\", cameras_fname)\r\n\r\n    dstpath = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\\"))\r\n    print(\"Writing cam/im binary \", dstpath + \"\\\\cameras.bin\")\r\n    cams = rwm.read_cameras_text(dstpath + \"\\\\cameras.txt\")\r\n    ims = rwm.read_images_text(dstpath + \"\\\\images.txt\")\r\n    rwm.write_cameras_binary(cams, dstpath + \"\\\\cameras.bin\")\r\n    rwm.write_images_binary(ims, dstpath + \"\\\\images.bin\")\r\n\r\n    ptsbin = dstpath+\"\\\\0\\\\points3D.bin\"\r\n    print(\"Pts bin \", ptsbin, \" Exists \" ,os.path.exists(ptsbin))\r\n    if os.path.exists(ptsbin):\r\n       shutil.copyfile(ptsbin, dstpath+\"\\\\points3D.bin\")\r\n\r\n    # overwrite 0 as well\r\n    dstpath = os.path.abspath(os.path.join(path, \"colmap\\\\sparse\\\\0\\\\\"))\r\n    print(\"Writing cam/im binary \", dstpath + \"\\\\cameras.bin\")\r\n    rwm.write_cameras_binary(cams, dstpath + \"\\\\cameras.bin\")\r\n    rwm.write_images_binary(ims, dstpath + \"\\\\images.bin\")\r\n    rwm.write_cameras_text(cams, dstpath + \"\\\\cameras.txt\")\r\n    rwm.write_images_text(ims, dstpath + \"\\\\images.txt\")\r\n\r\n    return True\r\n\r\ndef extract_video_frames(pathIn, pathOut):\r\n    cnt = 0\r\n    fileNames = []\r\n    for filename in os.listdir(pathIn):\r\n      if (\"MP4\" in filename) or (\"mp4\" in filename):\r\n        with open(os.path.join(pathIn, filename), 'r') as f:\r\n          print(\"Extracting Video from File: \", f.name)\r\n#          fileNames  = fileNames + extract_images(f.name, pathOut, \"Video%d\" % cnt, maxNumFrames=3, resize=True)\r\n          fileNames  = fileNames + extract_images(f.name, pathOut, \"Video%d\" % cnt, resize=True)\r\n#          extract_images(f.name, pathOut, videoName=\"Video%d\" % cnt)\r\n          cnt = cnt+1\r\n\r\n    with open(os.path.dirname(pathIn) + \"\\\\videos\\\\Video_Frames.txt\", 'w') as f:\r\n       for item in fileNames:\r\n          f.write(\"%s\\n\" % item.replace(\"\\\\\", \"/\"))\r\n       f.close()\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/fullColmapProcess/textureOnly.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\n\"\"\" @package dataset_tools_preprocess\nThis script runs a pipeline to texture a mesh after colmap has been run\n\nParameters: -h help,\n            -path <path to your dataset folder>,\n            -sibrBinariesPath <binaries directory of SIBR>,\n\nUsage: python textureOnly.py -path <path to your dataset folder>\n                                   -sibrBinariesPath <binaries directory of SIBR>\n\n\"\"\"\n\nimport subprocess\nimport os, sys, getopt\nimport os, sys, shutil\nimport json\nimport argparse\nfrom utils.paths import getBinariesPath \nfrom utils.commands import  getProcess\nfrom utils.TaskPipeline import TaskPipeline\nfrom simplify_mesh import simplifyMesh\n\ndef main():\n    parser = argparse.ArgumentParser()\n\n    # common arguments\n    parser.add_argument(\"--path\", type=str, required=True, help=\"path to your dataset folder\")\n    parser.add_argument(\"--sibrBinariesPath\", type=str, default=getBinariesPath(), help=\"binaries directory of SIBR\")\n    parser.add_argument(\"--dry_run\", action='store_true', help=\"run without calling commands\")\n    \n\n    args = vars(parser.parse_args())\n\n\n    # Fixing path values\n    args[\"path\"] = os.path.abspath(args[\"path\"])\n    args[\"sibrBinariesPath\"] = os.path.abspath(args[\"sibrBinariesPath\"])\n\n    ret = simplifyMesh( args[\"path\"] + \"/colmap/stereo/unix-meshed-delaunay.ply\", args[\"path\"]  + \"/colmap/stereo/unix-meshed-delaunay-simplified.ply\")\n    print(\"RET \", ret)\n    if( ret.returncode != 0 ):\n        print(\"SIBR ERROR: meshlab simplify failed, exiting\")\n        sys.exit(1)\n\n    unwrap_app = getProcess(\"unwrapMesh\")\n    unwrap_args = [unwrap_app,\n             \"--path\", args[\"path\"] + \"/colmap/stereo/unix-meshed-delaunay-simplified.ply\",\n             \"--output\", args[\"path\"] + \"/capreal/mesh.ply\",\n        ]\n\n    print(\"Running unwrap mesh \", unwrap_args)\n    p_exit = subprocess.call(unwrap_args)\n    if p_exit != 0:\n        print(\"SIBR ERROR: unwrap failed, exiting\")\n        sys.exit(1)\n\n    texturemesh_app = getProcess(\"textureMesh\")\n    texturemesh_args = [texturemesh_app,\n             \"--path\", args[\"path\"], \n             \"--output\", args[\"path\"] + \"/capreal/texture.png\",\n             \"--size\", \"8192\",\n             \"--flood\"\n        ]\n\n    print(\"Texturing mesh \", texturemesh_args)\n    p_exit = subprocess.call(texturemesh_args)\n    if p_exit != 0:\n        print(\"SIBR ERROR: mesh texturing failed, exiting\")\n        sys.exit(1)\n\n\n    print(\"textureonly has finished successfully.\")\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/meshroomPythonScripts/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(meshroomPythonScripts)\n\nfile(GLOB SCRIPTS \"*.py\")  \n\nadd_custom_target(${PROJECT_NAME} ALL)\n\ninclude(install_runtime)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\nibr_install_rsc(${PROJECT_NAME} TYPE \"scripts\" FILES ${SCRIPTS})\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/meshroomPythonScripts/ULR.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n__version__ = \"3.0\"\n\nfrom meshroom.core import desc\nimport os\n\nclass ULR(desc.CommandLineNode):\n    commandLine = 'SIBR_ulrv2_app_rwdi {allParams}'\n\n    print(os.path.abspath('.'))\n    cpu = desc.Level.INTENSIVE\n    ram = desc.Level.INTENSIVE\n\n    inputs = [\n        desc.ListAttribute(\n            elementDesc = desc.File(\n                name = \"path\",\n                label = \"Cache folder\",\n                description = \"\",\n                value = desc.Node.internalFolder + \"../..\",\n                uid=[0],\n            ),\n            name='path',\n            label='Input Folder',\n            description='MeshroomCache folder containing the StructureFromMotion folder, PrepareDenseScene folder, and Texturing folder.'\n        ),\n        desc.ChoiceParam(\n            name='texture-width',\n            label='Texture Width',\n            description='''Output texture size''',\n            value=1024,\n            values=(256, 512, 1024, 2048, 4096),\n            exclusive=True,\n            uid=[0],\n        ),\n    ]\n\n    outputs = [\n    ]\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/nvmToSIBR/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(nvmToSIBR)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n    sibr_graphics\n    sibr_assets\n    sibr_raycaster\n    sibr_system\n    sibr_view\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/nvmToSIBR/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <iostream>\n#include <core/system/CommandLineArgs.hpp>\n#include <core/scene/BasicIBRScene.hpp>\n#include <core/raycaster/CameraRaycaster.hpp>\n#include <core/assets/ImageListFile.hpp>\n#include <core/system/Utils.hpp>\n\n\n#define PROGRAM_NAME \"sibr_nvm_to_sibr\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\"Usage: \" PROGRAM_NAME \" -path \" \"\\n\"\n;\n\nstruct ColmapPreprocessArgs :\n\tvirtual BasicIBRAppArgs {\n};\n\nint main(const int argc, const char** argv)\n{\n\n\tCommandLineArgs::parseMainArgs(argc, argv);\n\tColmapPreprocessArgs myArgs;\n\n\tstd::string pathScene = myArgs.dataset_path;\n\n\tstd::vector<std::string> dirs = { \"cameras\", \"images\", \"meshes\"};\n\n\tstd::cout << \"Generating SIBR scene.\" << std::endl;\n\tBasicIBRScene scene(myArgs, true);\n\n\t// load the cams\n\tstd::vector<InputCamera::Ptr>\tcams = scene.cameras()->inputCameras();\n\tconst int maxCam = int(cams.size());\n\tconst int minCam = 0;\n\n\tfor (auto dir : dirs) {\n\t\tstd::cout << dir << std::endl;\n\t\tif (!directoryExists(pathScene + \"/\" + dir.c_str())) {\n\t\t\tmakeDirectory(pathScene + \"/\" + dir.c_str());\n\t\t}\n\t}\n\n\tstd::ofstream outputBundleCam;\n\tstd::ofstream outputListIm;\n\tstd::ofstream outputSceneMetadata;\n\n\toutputBundleCam.open(pathScene + \"/cameras/bundle.out\");\n\toutputListIm.open(pathScene + \"/images/list_images.txt\");\n\toutputSceneMetadata.open(pathScene + \"/scene_metadata.txt\");\n\toutputBundleCam << \"# Bundle file v0.3\" << std::endl;\n\toutputBundleCam << maxCam << \" \" << 0 << std::endl;\n\toutputSceneMetadata << \"Scene Metadata File\\n\" << std::endl;\n\toutputSceneMetadata << \"[list_images]\\n<filename> <image_width> <image_height> <near_clipping_plane> <far_clipping_plane>\" << std::endl;\n\n\tstd::sort(cams.begin(), cams.end(), [](const InputCamera::Ptr & a, const InputCamera::Ptr & b) {\n\t\treturn a->id() < b->id();\n\t});\n\n\tfor (int c = minCam; c < maxCam; c++) {\n\t\tInputCamera & camIm = *cams[c];\n\n\t\tstd::string extensionFile = boost::filesystem::extension(camIm.name());\n\t\tstd::ostringstream ssZeroPad;\n\t\tssZeroPad << std::setw(8) << std::setfill('0') << camIm.id();\n\t\tstd::string newFileName = ssZeroPad.str() + extensionFile;\n\n\t\tboost::filesystem::copy_file(pathScene + \"/nvm/\" + camIm.name(), pathScene + \"/images/\" + newFileName, boost::filesystem::copy_option::overwrite_if_exists);\n\t\toutputBundleCam << camIm.toBundleString();\n\t\toutputListIm << newFileName << \" \" << camIm.w() << \" \" << camIm.h() << std::endl;\n\t\toutputSceneMetadata << newFileName << \" \" << camIm.w() << \" \" << camIm.h() << \" \" << camIm.znear() << \" \" << camIm.zfar() << std::endl;\n\t}\n\n\n\toutputSceneMetadata << \"\\n// Always specify active/exclude images after list images\\n\\n[exclude_images]\\n<image1_idx> <image2_idx> ... <image3_idx>\" << std::endl;\n\n\tfor (int i = 0; i < scene.data()->activeImages().size(); i++) {\n\t\tif (!scene.data()->activeImages()[i])\n\t\t\toutputSceneMetadata << i << \" \";\n\t}\n\n\toutputSceneMetadata << \"\\n\\n\\n[other parameters]\" << std::endl;\n\n\toutputBundleCam.close();\n\toutputListIm.close();\n\toutputSceneMetadata.close();\n\n\tconst std::string meshPath = pathScene + \"/capreal/mesh.ply\";\n\tsibr::copyFile(meshPath, pathScene + \"/meshes/recon.ply\", true);\n\n\treturn EXIT_SUCCESS;\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/prepareColmap4Sibr/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(prepareColmap4Sibr)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n    sibr_graphics\n    sibr_assets\n    sibr_raycaster\n    sibr_system\n    sibr_view\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/prepareColmap4Sibr/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <iostream>\n#include <core/system/CommandLineArgs.hpp>\n#include <core/scene/BasicIBRScene.hpp>\n#include <core/raycaster/CameraRaycaster.hpp>\n#include <core/assets/ImageListFile.hpp>\n#include <core/system/Utils.hpp>\n\n\n#define PROGRAM_NAME \"prepareColmap4Sibr\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\"Usage: \" PROGRAM_NAME \" -path \" \"\\n\"\n;\n\nstruct ColmapPreprocessArgs : public BasicIBRAppArgs {\n\tArg<bool> fix_metadata = { \"fix_metadata\", \"Fix scene_metadata after crop and distort \" };\n};\n\nint main(const int argc, const char** argv)\n{\n\n\tCommandLineArgs::parseMainArgs(argc, argv);\n\tColmapPreprocessArgs myArgs;\n\n\tstd::string pathScene = myArgs.dataset_path;\n\n\tstd::vector<std::string> dirs = { \"sfm_mvs_cm\" , \"sibr_cm\" };\n\n\tstd::ofstream outputSceneMetadata;\n\n\tif( myArgs.fix_metadata ) {\n\t\tstd::string cm_path = myArgs.dataset_path.get() + \"/sibr_cm\";\n\t\tmyArgs.dataset_path = cm_path;\n\n\t\tBasicIBRScene cm_scene(myArgs, true, true);\n\n\t\tstd::vector<InputCamera::Ptr>\tcams = cm_scene.cameras()->inputCameras();\n\n\t\tstd::string tmpFileName = cm_path +  \"/scene_metadata_tmp.txt\"; \n\t\t// done in a second pass, when everything has been created.\n\t\toutputSceneMetadata.open(tmpFileName);\n\n\t\t// overwrite previous version since image sizes have changed when running sibr preprocessing\n\t\toutputSceneMetadata << \"Scene Metadata File\\n\" << std::endl;\n\n\t\tif (outputSceneMetadata.bad())\n\t\t\tSIBR_ERR << \"Problem writing new metadata file\" << std::endl;\n\n\t\tSIBR_LOG << \"Writing new scene_metadata.txt file \" << cm_path + \"/scene_metadata.txt\" << std::endl;\n\n\t\toutputSceneMetadata << \"[list_images]\\n<filename> <image_width> <image_height> <near_clipping_plane> <far_clipping_plane>\" << std::endl;\n\n\t\tfor (int c = 0; c < cams.size(); c++) {\n\t\t\tInputCamera & camIm = *cams[c];\n\n\t\t\tstd::string extensionFile = boost::filesystem::extension(camIm.name());\n\t\t\tstd::ostringstream ssZeroPad;\n\t\t\tssZeroPad << std::setw(8) << std::setfill('0') << camIm.id();\n\t\t\tstd::string newFileName = ssZeroPad.str() + extensionFile;\n\t\t\t// load image\n\t\t\tstd::string imgpath = cm_path + \"/images/\" + camIm.name();\n\t\t\tsibr::ImageRGB im;\n\t\t\tif (!im.load(imgpath, false))\n\t\t\t\tSIBR_ERR << \"Cant open image \" << imgpath << std::endl;\n\n\t\t\tstd::cerr << newFileName << \" \" << im.w() << \" \" << im.h() << \" \" << camIm.znear() << \" \" << camIm.zfar() << std::endl;\n\t\t\toutputSceneMetadata << newFileName << \" \" << im.w() << \" \" << im.h() << \" \" << camIm.znear() << \" \" << camIm.zfar() << std::endl;\n\t\t}\n\n\t\toutputSceneMetadata << \"\\n// Always specify active/exclude images after list images\\n\\n[exclude_images]\\n<image1_idx> <image2_idx> ... <image3_idx>\" << std::endl;\n\n\t\tfor (int i = 0; i < cm_scene.data()->activeImages().size(); i++) {\n\t\t\tif (!cm_scene.data()->activeImages()[i])\n\t\t\t\toutputSceneMetadata << i << \" \";\n\t\t}\n\t\toutputSceneMetadata << \"\\n\\n\\n[other parameters]\" << std::endl;\n\t\toutputSceneMetadata.close();\n\n\t\tstd::string SMName = cm_path +  \"/scene_metadata.txt\"; \n\n\t\tSIBR_LOG << \"Copying \" << tmpFileName << \" to \" << SMName << std::endl;\n\t\tboost::filesystem::copy_file( tmpFileName, SMName, boost::filesystem::copy_option::overwrite_if_exists);\n\t\tboost::filesystem::remove(tmpFileName);\n\n\t\texit(0);\n\t}\n\tstd::cout << \"Creating bundle file for SIBR scene.\" << std::endl;\n\tBasicIBRScene scene(myArgs, true, true);\n\n\t// load the cams\n\tstd::vector<InputCamera::Ptr>\tcams = scene.cameras()->inputCameras();\n\tconst int maxCam = int(cams.size());\n\tconst int minCam = 0;\n\n\tfor (auto dir : dirs) {\n\t\tstd::cout << dir << std::endl;\n\t\tif (!directoryExists(pathScene + \"/\" + dir.c_str())) {\n\t\t\tmakeDirectory(pathScene + \"/\" + dir.c_str());\n\t\t}\n\t}\n\n\tstd::ofstream outputBundleCam;\n\tstd::ofstream outputListIm;\n\n\toutputBundleCam.open(pathScene + \"/sfm_mvs_cm/bundle.out\");\n\toutputListIm.open(pathScene + \"/sfm_mvs_cm/list_images.txt\");\n\toutputBundleCam << \"# Bundle file v0.3\" << std::endl;\n\toutputBundleCam << maxCam << \" \" << 0 << std::endl;\n\n\toutputSceneMetadata.open(pathScene +  \"/sibr_cm/scene_metadata.txt\");\n\toutputSceneMetadata << \"Scene Metadata File\\n\" << std::endl;\n\toutputSceneMetadata << \"[list_images]\\n<filename> <image_width> <image_height> <near_clipping_plane> <far_clipping_plane>\" << std::endl;\n\n\tstd::sort(cams.begin(), cams.end(), [](const InputCamera::Ptr & a, const InputCamera::Ptr & b) {\n\t\treturn a->id() < b->id();\n\t});\n\n\tfor (int c = minCam; c < maxCam; c++) {\n\t\tInputCamera & camIm = *cams[c];\n\n\t\tstd::string extensionFile = boost::filesystem::extension(camIm.name());\n\t\tstd::ostringstream ssZeroPad;\n\t\tssZeroPad << std::setw(8) << std::setfill('0') << camIm.id();\n\t\tstd::string newFileName = ssZeroPad.str() + extensionFile;\n\n\t\tboost::filesystem::copy_file(pathScene + \"/colmap/stereo/images/\" + camIm.name(), pathScene + \"/sfm_mvs_cm/\" + newFileName, boost::filesystem::copy_option::overwrite_if_exists);\n\t\t// keep focal\n\t\toutputBundleCam << camIm.toBundleString(false, true);\n\t\toutputListIm << newFileName << \" \" << camIm.w() << \" \" << camIm.h() << std::endl;\n\t\toutputSceneMetadata << newFileName << \" \" << camIm.w() << \" \" << camIm.h() << \" \" << camIm.znear() << \" \" << camIm.zfar() << std::endl;\n\t}\n\n\toutputSceneMetadata << \"\\n// Always specify active/exclude images after list images\\n\\n[exclude_images]\\n<image1_idx> <image2_idx> ... <image3_idx>\" << std::endl;\n\n\tfor (int i = 0; i < scene.data()->activeImages().size(); i++) {\n\t\tif (!scene.data()->activeImages()[i])\n\t\t\toutputSceneMetadata << i << \" \";\n\t}\n\toutputSceneMetadata << \"\\n\\n\\n[other parameters]\" << std::endl;\n\n\n\toutputBundleCam.close();\n\toutputListIm.close();\n\toutputSceneMetadata.close();\n\n\tstd::vector<std::vector<std::string>> meshPathList = {\n\t\t{ \"/capreal/mesh.ply\", \t\t\t\"/sfm_mvs_cm/recon.ply\"},\n\t\t{ \"/capreal/mesh.obj\", \t\t\t\"/sfm_mvs_cm/recon.ply\"},\n\t\t{ \"/capreal/mesh.mtl\", \t\t\t\"/sfm_mvs_cm/\"},\n\t\t{ \"/capreal/texture.png\", \t\t\t\"/sfm_mvs_cm/\"},\n\t\t{ \"/capreal/mesh_u1_v1.png\", \t\"/sfm_mvs_cm/\"},\n\t\t{ \"/colmap/stereo/meshed-delaunay.ply\", \t\t\t\"/sfm_mvs_cm/recon.ply\"},\n\t};\n\n\tbool success = false;\n\tfor(const std::vector<std::string> & meshPaths : meshPathList) {\n\t\tif(boost::filesystem::exists(pathScene + meshPaths[0])) {\n\t\t\tsibr::copyFile(pathScene + meshPaths[0], pathScene + meshPaths[1], true);\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\tif (!success) {\n\t\tstd::cerr << \"Couldnt file proxy geometry in any of the following places \";\n\t\tfor (const std::vector<std::string>& meshPaths : meshPathList)\n\t\t\tstd::cerr << pathScene + meshPaths[0] << std::endl;\n\t\tSIBR_ERR << \"No proxy geometry, exiting\" << std::endl;\n\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(realityCaptureTools)\n\nfile(GLOB SCRIPTS \"*.py\" \"*.json\" \".bat\" \".xml\") \n\nadd_custom_target(${PROJECT_NAME} ALL)\n\ninclude(install_runtime)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\nibr_install_rsc(${PROJECT_NAME} TYPE \"scripts\" FILES ${SCRIPTS})\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/SetVariables.bat",
    "content": "::CapturingReality\r\n:: switch off console output\r\n::@echo off\r\n\r\n\r\n:: root path to work folders where the dataset is stored \r\nset RootFolder=E:\\datasets\\Yorgos\\Reflections\\KitchenRCTest\\\r\nset Video=\"%RootFolder%\\videos\\MVI_3030.MP4\"\r\nset FPS=0.2\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/exportModel.xml",
    "content": "<Configuration>\r\n  <entry key=\"ModelExportFormatVersion\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportCamerasAsModelPart\" value=\"false\"/>\r\n  <entry key=\"MvsMeshExportTexturingAllowed\" value=\"-1\"/>\r\n  <entry key=\"MvsExportIsModelCoordinates\" value=\"0\"/>\r\n  <entry key=\"MvsExportIsGeoreferenced\" value=\"0x0\"/>\r\n  <entry key=\"MvsExportScaleZ\" value=\"1.0\"/>\r\n  <entry key=\"MvsMeshExportTileType\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportNormals\" value=\"true\"/>\r\n  <entry key=\"MvsExportScaleY\" value=\"1.0\"/>\r\n  <entry key=\"MvsMeshExportTexAlpha\" value=\"false\"/>\r\n  <entry key=\"MvsExportScaleX\" value=\"1.0\"/>\r\n  <entry key=\"MvsMeshExportTexImgFormat_Color8_0\" value=\"png\"/>\r\n  <entry key=\"MvsExportcoordinatesystemtype\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportTexPixFormat_Color8_0\" value=\"32bppBGRA\"/>\r\n  <entry key=\"MvsMeshExportNormalsAllowed\" value=\"-1\"/>\r\n  <entry key=\"MvsMeshExportNumberFormatAllowed\" value=\"-1\"/>\r\n  <entry key=\"MvsExportMoveZ\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportMoveX\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportMoveY\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportNormalRange\" value=\"ZeroToOne\"/>\r\n  <entry key=\"MvsMeshExportInfoFile\" value=\"true\"/>\r\n  <entry key=\"MvsMeshExportByParts\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportClassificationAllowed\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportNumberFormat\" value=\"5\"/>\r\n  <entry key=\"MvsExportRotationY\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportNormalFlipZ\" value=\"false\"/>\r\n  <entry key=\"MvsExportRotationX\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportNormalFlipY\" value=\"false\"/>\r\n  <entry key=\"MvsMeshExportCamerasAllowed\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportColors\" value=\"true\"/>\r\n  <entry key=\"MvsExportNormalSpace\" value=\"Mikktspace\"/>\r\n  <entry key=\"MvsExportNormalFlipX\" value=\"false\"/>\r\n  <entry key=\"MvsExportTransformationPreset\" value=\"[[Default]]\"/>\r\n  <entry key=\"MvsExportRotationZ\" value=\"0.0\"/>\r\n  <entry key=\"MvsMeshExportFileTypeSelectionDisplay\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportTexOneFile\" value=\"0\"/>\r\n  <entry key=\"MvsMeshExportTexturing\" value=\"-1\"/>\r\n  <entry key=\"MvsMeshExportEmbeddTxrsAllowed\" value=\"0\"/>\r\n</Configuration>\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/fixup.bat",
    "content": "::CapturingReality\n\n:: switch off console output\n::@echo off\n@echo on\nset RootFolder=%1\n\n:: path to RealityCapture application\nset RealityCaptureExe=\"C:\\Program Files\\Capturing Reality\\RealityCapture\\RealityCapture.exe\"\n\n:: variable storing path to images for texturing model\nset Project=\"%RootFolder%\\rcProj\\RCproject.rcproj\"\n\n:: run RealityCapture\n:: test and fix video import when RC working again\n\n%RealityCaptureExe% -load %Project% ^\n        -selectAllImages ^\n        -enableAlignment false ^\n        -selectImage *test_* ^\n        -enableAlignment true ^\n       \n        \n\n\n\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/processRCSteps.json",
    "content": "{\n    \"steps\" : [\n        {\n            \"if\": \"${car_data}\",\n            \"name\": \"car_data_process\",\n            \"function\": \"rc_tools.car_data_process\",\n            \"function_args\": {\n                \"path\": \"${path}\"\n            }\n        },\n        {\n            \"name\": \"preprocess_for_rc\",\n            \"function\": \"rc_tools.preprocess_for_rc\",\n            \"function_args\": {\n                \"path\": \"${path}\",\n                \"video_name\": \"${video_name}\",\n                \"do_validation_split\": \"${do_validation_split}\",\n                \"valid_skip\": \"${valid_skip}\"\n            }\n        },\n        {\n            \"if\" : \"${calib_only}\", \n            \"name\": \"calib_only\",\n            \"app\": \"RC\",\n            \"optional_arg1\": [ \n                \"${do_train}\", \n                \"-addFolder\", \"${path}/input/train/\" \n            ],\n            \"optional_arg2\": [ \"${do_validation}\", \n                \"-addFolder\", \"${path}/input/validation/\"\n            ],\n            \"optional_arg3\": [ \"${do_video}\", \n                \"-importVideo\", \"${video_filename}\", \"${path}/input/test_video_frames/\", \"${one_over_fps}\" \n            ],\n            \"optional_arg4\": [ \"${do_test}\", \n                \"-addFolder\", \"${path}/input/test/\"\n            ],\n            \"command_args\": [\n                \"-align\" ,\n                \"-selectMaximalComponent\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*validation_*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-exportRegistration\", \"${path}/rcScene/validation_cameras/bundle.out\", \"${config_folder}/registrationConfig.xml\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*${path_prefix}*\",\n                \"-enableAlignment\", \"true\" ,\n                \"-exportRegistration\", \"${path}/rcScene/test_path_cameras/bundle.out\", \"${config_folder}/registrationConfig.xml\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*train_*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-exportRegistration\", \"${path}/rcScene/train_cameras/bundle.out\", \"${config_folder}/registrationConfig.xml\" ,\n                \"-save\", \"${path}/rcProj/RCproject.rcproj\" ,\n                \"-quit\"\n            ]\n        },\n        {\n            \"if\": \"${video_only}\",\n            \"name\": \"fix_video_only\",\n            \"function\": \"rc_tools.fix_video_only\",\n            \"function_args\": {\n                \"path\": \"${path}\"\n             }\n        },\n        {\n            \"if\" : \"${do_mvs}\", \n            \"name\": \"run_rc\",\n            \"app\": \"RC\",\n            \"optional_arg1\": [ \n                \"${do_train}\", \n                \"-addFolder\", \"${path}/input/train/\" \n            ],\n            \"optional_arg2\": [ \"${do_validation}\", \n                \"-addFolder\", \"${path}/input/validation/\"\n            ],\n            \"optional_arg3\": [ \"${do_video}\", \n                \"-importVideo\", \"${video_filename}\", \"${path}/input/test_video_frames/\", \"${one_over_fps}\" \n            ],\n            \"optional_arg4\": [ \"${do_test}\", \n                \"-addFolder\", \"${path}/input/test/\"\n            ],\n            \"command_args\": [\n                \"-align\" ,\n                \"-selectMaximalComponent\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*validation_*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-exportRegistration\", \"${path}/rcScene/validation_cameras/bundle.out\", \"${config_folder}/registrationConfig.xml\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*${path_prefix}*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-exportRegistration\", \"${path}/rcScene/test_path_cameras/bundle.out\", \"${config_folder}/registrationConfig.xml\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*train_*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-exportRegistration\", \"${path}/rcScene/train_cameras/bundle.out\", \"${config_folder}/registrationConfig.xml\",\n                \"-setReconstructionRegionAuto\" ,\n                \"-scaleReconstructionRegion\", \"1.4\", \"1.4\", \"2.5\", \"center\", \"factor\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*${path_prefix}*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-save\", \"${path}/rcProj/RCproject.rcproj\" \n            ],\n            \"optional_final_arg\": [ \"${auto_recon_area}\",\n                \"-quit\"\n            ]\n        },\n        {\n            \"if\" : \"${do_mvs}\", \n            \"name\": \"run_rc_mvs\",\n            \"app\": \"RC\",\n            \"command_args\": [ \n                \"-load\", \"${path}/rcProj/RCproject.rcproj\" ,\n                \"-selectMaximalComponent\" ,\n                \"-selectAllImages\" ,\n                \"-enableAlignment\", \"false\" ,\n                \"-selectImage\", \"*train_*\" ,\n                \"-enableAlignment\", \"true\" ,\n                \"-calculateNormalModel\" ,\n                \"-calculateTexture\" ,\n                \"-selectMarginalTriangles\" ,\n                \"-removeSelectedTriangles\" ,\n                \"-save\", \"${path}/rcProj/RCproject.rcproj\" ,\n                \"-renameSelectedModel\", \"${model_name}\" ,\n                \"-exportModel\", \"${model_name}\", \"${mesh_obj_filename}\", \"${config_folder}/exportModel.xml\" ,\n                \"-deselectModelTriangles\" ,\n                \"-exportModel\", \"${model_name}\", \"${mesh_ply_filename}\", \"${config_folder}/exportModel.xml\" ,\n                \"-quit\"\n            ]\n        },\n        {\n            \"name\": \"densify_mesh\",\n            \"function\": \"rc_tools.densify_mesh\",\n            \"function_args\": {\n                \"mesh_path\": \"${path}/rcScene/meshes/mesh.obj\"\n            }\n        },\n        {\n            \"name\": \"dense_mesh\",\n            \"app\": \"RC\",\n            \"command_args\": [\n                \"-load\", \"${path}/rcProj/RCProject.rcproj\",\n                \"-selectMaximalComponent\",\n                \"-importModel\" , \"${path}/rcScene/meshes/dense_mesh.obj\",\n                \"-renameSelectedModel\",  \"RCTest\",\n                \"-exportModel\",  \"RCTest\",  \"${path}/rcScene/meshes/dense_point_cloud.xyz\", \"${config_folder}/exportModel.xml\" ,\n                \"-quit\"\n            ]\n        },\n        {\n            \"name\": \"rc_to_colmap_validation_cameras\",\n            \"function\": \"rc_tools.rc_to_colmap\",\n            \"function_args\": {\n                \"rc_path\": \"${path}/rcScene/validation_cameras\",\n                \"out_path\": \"${path}/colmap_1000/validation_colmap\",\n                \"create_colmap\": \"0\",\n                \"target_width\": \"${target_width}\"\n            }\n        },\n        {\n            \"name\": \"rc_to_colmap_path_cameras\",\n            \"function\": \"rc_tools.rc_to_colmap\",\n            \"function_args\": {\n                \"rc_path\": \"${path}/rcScene/test_path_cameras\",\n                \"out_path\": \"${path}/colmap_1000/test_path_colmap\",\n                \"create_colmap\": \"0\",\n                \"target_width\": \"${target_width}\"\n            }\n        },\n        {\n            \"name\": \"crop_cameras\",\n            \"function\": \"rc_tools.crop_images\",\n            \"function_args\": {\n                \"path_data\": \"${path}/rcScene/train_cameras/\",\n                \"path_dest\": \"${path}/rcScene/cropped_train_cameras/\"\n            }\n        },\n        {\n            \"name\": \"rc_to_colmap_1000_cropped_cameras\",\n            \"function\": \"rc_tools.rc_to_colmap\",\n            \"function_args\": {\n                \"rc_path\": \"${path}/rcScene/cropped_train_cameras\",\n                \"out_path\": \"${path}/colmap_1000/colmap\",\n                \"create_colmap\": \"1\",\n                \"target_width\": \"${target_width}\"\n            }\n        },\n        {\n            \"name\": \"rc_to_colmap_cropped_cameras\",\n            \"function\": \"rc_tools.rc_to_colmap\",\n            \"function_args\": {\n                \"rc_path\": \"${path}/rcScene/cropped_train_cameras\",\n                \"out_path\": \"${path}/sibr/colmap\",\n                \"create_colmap\": \"1\"\n            }\n        },\n        {\n            \"name\": \"create_nerf\",\n            \"function\": \"colmap2nerf.createNerf\",\n            \"function_args\": {\n                \"path\": \"${path}\"\n            }\n        },\n        {\n            \"if\": \"${hires_nerf}\",\n            \"name\": \"create_hi_nerf\",\n            \"function\": \"colmap2nerf.createNerf\",\n            \"function_args\": {\n                \"path\": \"${path}\",\n                \"hires\": \"True\"\n            }\n        },\n        {\n            \"name\": \"convert_sibr_mesh\",\n            \"function\": \"rc_tools.convert_sibr_mesh\",\n            \"function_args\": {\n                \"path\": \"${path}\"\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/rc_tools.py",
    "content": "#\r\n# RealityCapture tools\r\n#\r\nimport os\r\nimport os.path\r\nimport sys\r\nimport argparse\r\nimport shutil\r\nimport sqlite3\r\nimport read_write_model as rwm\r\nimport pymeshlab\r\n\r\n\r\nimport cv2\r\nprint(cv2.__version__)\r\n\r\n\r\n\"\"\" @package dataset_tools_preprocess\r\nLibrary for RealityCapture treatment\r\n\r\n\r\n\"\"\"\r\n\r\nimport bundle\r\nimport os, sys, shutil\r\nimport json\r\nimport argparse\r\nimport scipy\r\nimport numpy as np\r\nfrom scipy.spatial.transform import Rotation as R\r\nfrom utils.paths import getBinariesPath, getColmapPath, getMeshlabPath\r\nfrom utils.commands import  getProcess, getColmap, getRCprocess, runCommand\r\n\r\ndef preprocess_for_rc(path, video_name='default', do_validation_split=True, valid_skip='10'):\r\n    # create train/validation split (every 10 images by default now)\r\n    print(\"VALID SKIP \", valid_skip)\r\n    int_valid_skip = int(valid_skip)\r\n\r\n    # Should exist\r\n    rawpath = os.path.join(path, \"raw\")\r\n    if not os.path.exists(rawpath):\r\n        os.makedirs(os.path.join(path, \"raw\"))\r\n\r\n    imagespath = os.path.abspath(os.path.join(rawpath, \"images\"))\r\n    testpath = os.path.abspath(os.path.join(rawpath, \"test\"))\r\n    videopath = os.path.abspath(os.path.join(rawpath, \"videos\"))\r\n    do_test = False\r\n    inputpath = os.path.join(path, \"input\")\r\n\r\n    # If not, move around\r\n    if not os.path.exists(imagespath):\r\n        if os.path.exists(os.path.join(path, \"images\")):\r\n            shutil.move(os.path.join(path, \"images\"), imagespath)\r\n        elif not os.path.exists(os.path.join(path, \"videos\")) and not os.path.exists(videopath):\r\n            print(\"ERROR: No images nor video, exiting. Images should be in $path/raw/images\")\r\n            exit(-1)\r\n        # videos are optional\r\n        if os.path.exists(os.path.join(path, \"videos\")):\r\n            shutil.move(os.path.join(path, \"videos\"), videopath)\r\n        # test images (stills for path)\r\n        test_orig = os.path.join(path, \"test\")\r\n#        print(\"TEST \", test_orig, \" \" , os.path.exists(test_orig) , \" > \", testpath)\r\n        if os.path.exists(test_orig):\r\n            do_test = True\r\n            shutil.move(test_orig, testpath)\r\n    else:\r\n        print(\"Found images {}\".format(imagespath))\r\n        if os.path.exists(videopath):\r\n            print(\"Found video {}\".format(videopath))\r\n        if os.path.exists(testpath):\r\n            print(\"Found test {}\".format(testpath))\r\n            do_test = True\r\n\r\n    cnt = 0\r\n    validation_path = os.path.abspath(os.path.join(inputpath, \"validation\"))\r\n    train_path = os.path.abspath(os.path.join(inputpath, \"train\"))\r\n    if not os.path.exists(train_path):\r\n        os.makedirs(train_path)\r\n    if not os.path.exists(validation_path):\r\n        os.makedirs(validation_path)\r\n    input_test_path = os.path.abspath(os.path.join(inputpath, \"test\"))\r\n    if not os.path.exists(input_test_path):\r\n        os.makedirs(input_test_path)\r\n\r\n    # rcScene -- will contain full bundle files from RC\r\n    rcscenepath = os.path.join(path, \"rcScene\")\r\n    if not os.path.exists(rcscenepath):\r\n        os.makedirs(rcscenepath)\r\n\r\n    # rcProj -- RC project save\r\n    rcprojpath = os.path.join(path, \"rcProj\")\r\n    if not os.path.exists(rcprojpath):\r\n        os.makedirs(rcprojpath)\r\n\r\n\r\n    # sibr -- will contain full size colmap\r\n    sibrpath = os.path.join(path, \"sibr\")\r\n    if not os.path.exists(sibrpath):\r\n        os.makedirs(sibrpath)\r\n        caprealpath = os.path.join(sibrpath, \"capreal\")\r\n        os.makedirs(caprealpath)\r\n\r\n    # BUG: do_validation_split is a string\r\n    if do_validation_split != 'False':\r\n        print(\"Train/Validation\", train_path , \" : \", validation_path)\r\n        for filename in os.listdir(imagespath):\r\n            ext = os.path.splitext(filename)[1]\r\n            if ext == \".JPG\" or ext == \".jpg\" or ext == \".PNG\" or ext == \".png\" :\r\n                image = os.path.join(imagespath, filename) \r\n#            print(\"IM \", image)\r\n                if not(cnt % int_valid_skip ):\r\n                    filename = \"validation_\"+filename\r\n                    fname = os.path.join(validation_path, filename)\r\n#                print(\"Copying \", image, \" to \", fname , \" in validation\")\r\n                    shutil.copyfile(image, fname)\r\n                else:\r\n                    filename = \"train_\"+filename\r\n                    fname = os.path.join(train_path, filename)\r\n#                print(\"Copying \", image, \" to \", fname , \" in train\")\r\n                    shutil.copyfile(image, fname)\r\n\r\n            cnt = cnt + 1\r\n    else:\r\n        print(\"Not doing validation\")\r\n        for filename in os.listdir(imagespath):\r\n            ext = os.path.splitext(filename)[1]\r\n            if ext == \".JPG\" or ext == \".jpg\" or ext == \".PNG\" or ext == \".png\" :\r\n                image = os.path.join(imagespath, filename) \r\n#            print(\"IM \", image)\r\n          \r\n                filename = \"train_\"+filename\r\n                fname = os.path.join(train_path, filename)\r\n#                print(\"Copying \", image, \" to \", fname , \" in train\")\r\n                shutil.copyfile(image, fname)\r\n\r\n            cnt = cnt + 1\r\n\r\n    if do_test:\r\n        for filename in os.listdir(testpath):\r\n            ext = os.path.splitext(filename)[1]\r\n            if ext == \".JPG\" or ext == \".jpg\" or ext == \".PNG\" or ext == \".jpg\" :\r\n                image = os.path.join(testpath, filename) \r\n                filename = \"test_\"+filename\r\n                fname = os.path.join(input_test_path, filename)\r\n#                print(\"Copying \", image, \" to \", fname , \" in test\")\r\n                shutil.copyfile(image, fname)\r\n    else:\r\n        print (\"****************** NOT DOING TEST !!!\")\r\n\r\n\r\n    # extract video name -- if not given, take first\r\n    if video_name == 'default':\r\n        if os.path.exists(videopath):\r\n            for filename in os.listdir(videopath):\r\n#            print(\"Checking \", filename)\r\n                if (\"MP4\" in filename) or (\"mp4\" in filename):\r\n                    video_name = filename\r\n    video_filename = os.path.join(path, os.path.join(\"raw\", os.path.join(\"videos\", video_name)))\r\n    print(\"Full video path:\", video_filename)\r\n\r\n    return \"video_filename\", video_filename\r\n\r\ndef convert_sibr_mesh(path):\r\n    ms = pymeshlab.MeshSet()\r\n    mesh_path = os.path.join(os.path.join(os.path.join(path, \"rcScene\"), \"meshes\"), \"mesh.obj\")\r\n    print(\"Loading mesh (slow...)\", mesh_path)\r\n    ms.load_new_mesh(mesh_path)\r\n    meshply_path = out_mesh_path = os.path.join(os.path.join(os.path.join(path, \"sibr\"), \"capreal\"), \"mesh.ply\")\r\n    print(\"Saving mesh (slow...)\", out_mesh_path)\r\n    ms.save_current_mesh(out_mesh_path, save_wedge_texcoord=False, binary=False)\r\n    print(\"Done saving mesh (slow...)\", out_mesh_path)\r\n    texture_path = os.path.join(os.path.join(os.path.join(path, \"sibr\"), \"capreal\"), \"mesh_u1_v1.png\")\r\n    out_texture_path = os.path.join(os.path.join(os.path.join(path, \"sibr\"), \"capreal\"), \"texture.png\")\r\n    print(\"Copying (to allow meshlab to work) {} to {}\".format(texture_path, out_texture_path))\r\n    shutil.copyfile(texture_path, out_texture_path)\r\n    out_mesh_path = os.path.join(os.path.join(os.path.join(os.path.join(path, \"sibr\"), \"colmap\"), \"stereo\"), \"meshed-delaunay.ply\")\r\n    print(\"Copying {} to {}\".format(meshply_path, out_mesh_path))\r\n    shutil.copyfile(meshply_path, out_mesh_path)\r\n\r\n\r\ndef densify_mesh(mesh_path):\r\n    ms = pymeshlab.MeshSet()\r\n    subdiv_threshold = pymeshlab.Percentage(0.09)\r\n    ms.load_new_mesh(mesh_path)\r\n    print(\"Loaded mesh \", mesh_path, \" Subdividing (this can take some time)...\")\r\n    ms.subdivision_surfaces_butterfly_subdivision(threshold=subdiv_threshold)\r\n    path_split = os.path.split(mesh_path)\r\n    dense_mesh_fname = \"dense_\" + path_split[1]\r\n    fname, fname_ext = os.path.splitext(dense_mesh_fname)\r\n    dense_mesh_fname = fname + \".obj\"\r\n    dense_mesh_path = os.path.join(path_split[0], dense_mesh_fname)\r\n    print(\"Writing dense mesh \", dense_mesh_path)\r\n    ms.save_current_mesh(dense_mesh_path)\r\n\r\ndef rc_to_colmap(rc_path, out_path, create_colmap=False, target_width=-1):\r\n\r\n    input_bundle = bundle.Bundle(os.path.join(rc_path , \"bundle.out\"))\r\n    input_bundle.generate_list_of_images_file (os.path.join(rc_path , \"list_images.txt\"))\r\n\r\n    dst_image_path = os.path.join(out_path, \"images\")\r\n\r\n    # create entire colmap structure\r\n    if create_colmap:\r\n        dir_name = os.path.join(out_path, \"stereo\")\r\n        if not os.path.exists(dir_name):\r\n            os.makedirs(dir_name)\r\n\r\n        stereo_stereo_dir = os.path.join(dir_name, \"stereo\")\r\n        if not os.path.exists(stereo_stereo_dir):\r\n            os.makedirs(stereo_stereo_dir)\r\n\r\n        dst_image_path = os.path.join(dir_name, \"images\")\r\n\r\n        sparse_stereo_dir = dir_name = os.path.join(dir_name, \"sparse\")\r\n        if not os.path.exists(dir_name):\r\n            os.makedirs(dir_name)\r\n\r\n\r\n    else:\r\n        sparse_stereo_dir = out_path\r\n\r\n    if not os.path.exists(dst_image_path):\r\n        os.makedirs(dst_image_path)\r\n\r\n    # create cameras.txt\r\n    fname = os.path.join(sparse_stereo_dir, \"cameras.txt\")\r\n    print(\"Creating \", fname)\r\n    numcams = len(input_bundle.list_of_input_images)\r\n\r\n    camera_id = 1\r\n    scale = 1.\r\n    with open(fname, 'w') as outfile:\r\n        outfile.write(\"# Camera list with one line of data per camera:\\n\")\r\n        outfile.write(\"#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]\\n\")\r\n        outfile.write(\"# Number of cameras: {}\\n\".format(numcams))\r\n        for im in input_bundle.list_of_input_images:\r\n            width = im.resolution[0]\r\n            height = im.resolution[1]\r\n            focal_length = input_bundle.list_of_cameras[camera_id-1].focal_length\r\n\r\n            # resize images if required\r\n            if target_width != -1:\r\n                orig_width = width\r\n                width = float(target_width)\r\n                scale = float(target_width) / orig_width \r\n                aspect = height / orig_width\r\n                height = width * aspect\r\n                focal_length = scale * focal_length\r\n               \r\n            outfile.write(\"{} PINHOLE {} {} {} {} {} {}\\n\".format(camera_id, int(width), int(height), focal_length, focal_length, width/2.0, height/2.0))\r\n            camera_id = camera_id + 1\r\n        outfile.close()\r\n\r\n    # create images.txt\r\n    fname = os.path.join(sparse_stereo_dir, \"images.txt\")\r\n\r\n    print(\"Creating \", fname)\r\n    camera_id = 1\r\n    with open(fname, 'w') as outfile:\r\n        outfile.write( \"# Image list with two lines of data per image:\\n\" )\r\n        outfile.write( \"#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME\\n\" )\r\n        outfile.write( \"#   POINTS2D[] as (X, Y, POINT3D_ID)\\n\" )\r\n        point2d_index = 0\r\n        for cam in input_bundle.list_of_cameras:\r\n            in_im = input_bundle.list_of_input_images[camera_id-1]\r\n            imname = in_im.path\r\n            name = os.path.basename(imname)\r\n            im = cv2.imread(imname, cv2.IMREAD_UNCHANGED)\r\n            w = im.shape[1]\r\n            h = im.shape[0]\r\n\r\n            # to sibr internal\r\n            br = np.matrix(cam.rotation).transpose()\r\n            t = -np.matmul(br , np.matrix([cam.translation[0], cam.translation[1], cam.translation[2]]).transpose())\r\n         \r\n            # sibr save to colmap\r\n            br = np.matmul(br, np.matrix([[1, 0, 0], [0, -1, 0], [0, 0, -1]]))\r\n            br = br.transpose()\r\n\r\n            sci_rot = R.from_matrix(br)\r\n            sci_quat = sci_rot.as_quat()\r\n\r\n            t = -np.matmul(br, t)\r\n\r\n            outfile.write(\"{} {} {} {} {} {} {} {} {} {}\\n\".format(camera_id, -sci_quat[3], -sci_quat[0], -sci_quat[1], -sci_quat[2], t[0,0], t[1,0], t[2,0], camera_id, name))\r\n            # write out points\r\n            first = False\r\n            scale = 1.0\r\n            if target_width !=1 :\r\n                scale = float(target_width) / float(in_im.resolution[0])\r\n            for p in cam.list_of_feature_points:\r\n                for v in p.view_list:\r\n                    if v[0] == camera_id-1:\r\n                        outfile.write( str(scale*(2.*v[2]+w)) + \" \" + str(scale*(2.*v[3]+h))+ \" -1\" ) # TODO: not sure about this, seems to be -1 in all existing files\r\n                        if not first:\r\n                            outfile.write(\" \")\r\n                        else:\r\n                            first = False\r\n\r\n                        p.point2d_index[v[0]] = point2d_index\r\n                        point2d_index = point2d_index + 1\r\n\r\n            outfile.write(\"\\n\")\r\n            camera_id = camera_id + 1\r\n    outfile.close()\r\n\r\n    # create points3D.txt\r\n    fname = os.path.join(sparse_stereo_dir, \"points3D.txt\")\r\n\r\n    print(\"Creating \", fname)\r\n    camera_id = 1\r\n    with open(fname, 'w') as outfile:\r\n        num_points = len(input_bundle.list_of_feature_points)\r\n#  FIX mean_track_length = sum((len(pt.image_ids) for _, pt in points3D.items()))/len(points3D)\r\n        mean_track_length = 10 # 10 is a placeholder value\r\n        outfile.write(\"# 3D point list with one line of data per point:\\n\" )\r\n        outfile.write(\"#   POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)\\n\")\r\n        outfile.write(\"# Number of points: {}, mean track length: {}\\n\".format(num_points, mean_track_length))\r\n        for p in input_bundle.list_of_feature_points:\r\n            # error set to 0.1 for all\r\n            outfile.write(str(p.id+1)+ \" \" + str(p.position[0]) + \" \" + str(p.position[1]) + \" \" + str(p.position[2]) + \" \" + str( p.color[0])+  \" \" + str( p.color[1])+  \" \" + str( p.color[2])+ \" 0.1\")\r\n            for v in p.view_list:\r\n#                print(\"Cam id \", v[0], \" P= \", p.id+1 , \" p2dind \" , p.point2d_index )\r\n                outfile.write(\" \" + str(v[0]+1)+ \" \" + str(p.point2d_index[v[0]])  )\r\n            outfile.write(\"\\n\")\r\n\r\n\r\n    if create_colmap:\r\n        fname = os.path.join(stereo_stereo_dir, \"fusion.cfg\")\r\n        outfile_fusion = open(fname, 'w') \r\n        fname = os.path.join(stereo_stereo_dir, \"patch-match.cfg\")\r\n        outfile_patchmatch = open(fname, 'w') \r\n        outdir = os.path.join(stereo_stereo_dir, \"normal_maps\")\r\n        if not os.path.exists(outdir):\r\n            os.makedirs(outdir)\r\n        outdir = os.path.join(stereo_stereo_dir, \"depth_maps\")\r\n        if not os.path.exists(outdir):\r\n            os.makedirs(outdir)\r\n        outdir = os.path.join(stereo_stereo_dir, \"consistency_graphs\")\r\n        if not os.path.exists(outdir):\r\n            os.makedirs(outdir)\r\n\r\n    # copy images\r\n    for fname in os.listdir(rc_path):\r\n        if fname.endswith(\".jpg\") or fname.endswith(\".JPG\") or fname.endswith(\".png\") or fname.endswith(\".PNG\") :\r\n            src_image_fname = os.path.join(rc_path, fname)\r\n            dst_image_fname = os.path.join(dst_image_path, os.path.basename(fname))\r\n#            print(\"Copying \", src_image_fname, \"to \", dst_image_fname)\r\n\r\n            if create_colmap:\r\n                  outfile_fusion.write(fname+\"\\n\")\r\n                  outfile_patchmatch.write(fname+\"\\n\")\r\n                  outfile_patchmatch.write(\"__auto__, 20\\n\")\r\n\r\n            # resize if necessary\r\n            if target_width != -1:\r\n                im = cv2.imread(src_image_fname, cv2.IMREAD_UNCHANGED)\r\n                orig_width = im.shape[1]\r\n                orig_height = im.shape[0]\r\n                width = float(target_width)\r\n                scale = float(target_width)/ orig_width \r\n                aspect = orig_height / orig_width\r\n                height = width * aspect\r\n                dim = (int(width), int(height))\r\n                im = cv2.resize(im, dim, interpolation = cv2.INTER_AREA)\r\n                cv2.imwrite(dst_image_fname, im)\r\n            else:\r\n                shutil.copyfile(src_image_fname, dst_image_fname)\r\n\r\n    # copy mesh; fake it\r\n    if create_colmap:\r\n        outfile_patchmatch.close()\r\n        outfile_fusion.close()\r\n\r\n# taken from ibr_preprocess_rc_to_sibr\r\n# TODO: pretty ugly needs rethink and cleanup\r\ndef crop_images(path_data, path_dest):\r\n    # open calibration data\r\n    input_bundle = bundle.Bundle(os.path.join(path_data , \"bundle.out\"))\r\n    # query current average resolution of these cameras\r\n    avg_resolution = input_bundle.get_avg_resolution()\r\n    print(\"AVG resolution \", avg_resolution)\r\n\r\n    # special case: validation_cameras take size/crop data from train cameras so they are all the same\r\n    if \"validation_\" not in path_data:\r\n\r\n        # generate resolutions.txt and put it in the current dataset folder\r\n        resolutions_txt_path = os.path.join(path_data, \"resolutions.txt\")\r\n        input_bundle.generate_list_of_images_file(resolutions_txt_path)\r\n\r\n        # setup avg_resolution parameters for distordCrop\r\n        print(\"Command: run distordCrop ARGS: \", \"--path\", path_data, \"--ratio\",  \"0.3\", \"--avg_width\", str(avg_resolution[0]), \"--avg_height\", str(avg_resolution[1]), \")\")\r\n        retcode = runCommand(getProcess(\"distordCrop\"), [ \"--path\", path_data, \"--ratio\",  \"0.3\", \"--avg_width\", str(avg_resolution[0]), \"--avg_height\", str(avg_resolution[1]) ])\r\n        if retcode.returncode != 0:\r\n            print(\"Command: distordCrop failed, exiting (ARGS: \", \"--path\", path_data, \"--ratio\",  \"0.3\", \"--avg_width\", str(avg_resolution[0]), \"--avg_height\", str(avg_resolution[1]), \")\")\r\n            #exit(1)\r\n\r\n        # read new proposed resolution and check if images were discarded\r\n        exclude = []\r\n        path_to_exclude_images_txt = os.path.join(path_data, \"exclude_images.txt\")\r\n        if (os.path.exists(path_to_exclude_images_txt)):\r\n            # list of excluded cameras (one line having all the camera ids to exclude)\r\n            exclusion_file = open(path_to_exclude_images_txt, \"r\")\r\n            line = exclusion_file.readline()\r\n            tokens = line.split()\r\n\r\n            for cam_id in tokens:\r\n                exclude.append(int(cam_id))\r\n            exclusion_file.close()\r\n\r\n        # exclude cams from bundle file\r\n        if len(exclude) > 0:\r\n            print(\"Excluding \", exclude)\r\n            input_bundle.exclude_cams (exclude)\r\n\r\n        # read proposed cropped resolution\r\n        path_to_crop_new_size_txt = os.path.join(path_data, \"cropNewSize.txt\")\r\n    else:\r\n        train_path_data = str.replace(path_data, \"validation_\", \"\")\r\n        path_to_crop_new_size_txt = os.path.join(train_path_data, \"cropNewSize.txt\")\r\n        print(\"Reading crop size from \", path_to_crop_new_size_txt )\r\n\r\n    with open(path_to_crop_new_size_txt) as crop_size_file:\r\n        line = crop_size_file.readline()\r\n        tokens = line.split()\r\n        new_width   = int(tokens[0])\r\n        new_height  = int(tokens[1])\r\n        proposed_res = [new_width, new_height]\r\n\r\n    print(\"Crop size found:\", proposed_res)\r\n    # generate file with list of current selected images to process\r\n\r\n    path_to_transform_list_txt = os.path.join (path_data, \"toTransform.txt\")\r\n    input_bundle.generate_list_of_images_file(path_to_transform_list_txt)\r\n\r\n    if not os.path.exists(path_dest):\r\n        os.makedirs(path_dest)\r\n\r\n    \r\n    path_to_output_bundle = os.path.join (path_dest, \"bundle.out\")\r\n    # write bundle file in output cameras folder\r\n    new_width = None\r\n    input_bundle.save(path_to_output_bundle, proposed_res)\r\n\r\n    # setup avg_resolution and proposed_resolution parameters for distordCrop\r\n    print(\"Command: run cropFromCenter ARGS:\", \"--inputFile\", path_to_transform_list_txt, \"--outputPath\", path_dest, \"--avgResolution\", str(avg_resolution[0]), str(avg_resolution[1]), \"--cropResolution\", str(proposed_res[0]), str(proposed_res[1]))\r\n    retcode = runCommand(getProcess(\"cropFromCenter\"), [ \"--inputFile\", path_to_transform_list_txt, \"--outputPath\", path_dest, \"--avgResolution\", str(avg_resolution[0]), str(avg_resolution[1]), \"--cropResolution\", str(proposed_res[0]), str(proposed_res[1]) ])\r\n    if retcode.returncode != 0:\r\n        print(\"Command: cropFromCenter failed, exiting (ARGS:\", \"--inputFile\", path_to_transform_list_txt, \"--outputPath\", path_dest, \"--avgResolution\", str(avg_resolution[0]), str(avg_resolution[1]), \"--cropResolution\", str(proposed_res[0]), str(proposed_res[1]))\r\n        exit(1)\r\n\r\n\r\ndef fix_video_only(path):\r\n    # TODO: currently only works for video_only + calib_only; doesnt do video only with MVS\r\n    # verify that train is actually empty\r\n    train_dir = os.path.join(path, os.path.join(\"rcScene\", \"train_cameras\"))\r\n    test_dir = os.path.join(path, os.path.join(\"rcScene\", \"test_path_cameras\"))\r\n    files = os.listdir(train_dir)\r\n    if len(files) == 1: # empty bundle file\r\n        shutil.move(train_dir, train_dir+\"_save\")\r\n        print(\"MOVING {} to {}\".format(test_dir, train_dir))\r\n        shutil.move(test_dir, train_dir)\r\n    else:\r\n        print(\"FATAL ERROR: trying to overwrite existing train images\")\r\n        exit(1)\r\n\r\ndef car_data_process(path):\r\n    # Contains: CAM_{BACK,FRONT}[_]{LEFT, RIGHT}\r\n    rawpath = os.path.join(path, \"raw\")\r\n    if not os.path.exists(rawpath):\r\n        os.makedirs(rawpath)\r\n\r\n    imagespath = os.path.abspath(os.path.join(rawpath, \"images\"))\r\n    if not os.path.exists(imagespath):\r\n        os.makedirs(imagespath)\r\n\r\n    # read all the sets of cameras\r\n\r\n    dirlist = [ \"CAM_BACK\", \"CAM_BACK_LEFT\", \"CAM_BACK_RIGHT\", \"CAM_FRONT\", \"CAM_FRONT_LEFT\", \"CAM_FRONT_RIGHT\" ]\r\n    imlists = {}\r\n    global_im_counter = 0\r\n    \r\n    for dirname in dirlist:\r\n        campath = os.path.join(path, dirname)\r\n        first = True\r\n# basic version\r\n        for filename in os.listdir(campath):\r\n            shutil.copyfile(os.path.join(campath, filename), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+\".jpg\"))\r\n            global_im_counter += 1\r\n\r\n\"\"\"\r\n# code below useless\r\n        for filename in os.listdir(campath):\r\n            ext = os.path.splitext(filename)[1]\r\n            if ext == \".JPG\" or ext == \".jpg\" or ext == \".PNG\" or ext == \".png\" :\r\n                if first:\r\n                    imlists[dirname] = [filename]\r\n                    first = False\r\n                else:\r\n                    imlists[dirname].append(filename)\r\n\r\n#                print(\"Adding \", filename , \" to list \" , dirname)\r\n    for i in range(len(imlists[\"CAM_BACK\"])):\r\n        imname = imlists[ \"CAM_BACK_LEFT\"][i] \r\n        shutil.copyfile(os.path.join(path, os.path.join( \"CAM_BACK_LEFT\", imname)), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+\".jpg\"))\r\n        global_im_counter += 1\r\n        imname = imlists[ \"CAM_FRONT_LEFT\"][i] \r\n        shutil.copyfile(os.path.join(path, os.path.join( \"CAM_FRONT_LEFT\", imname)), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+\".jpg\"))\r\n        global_im_counter += 1\r\n        if i > 2:\r\n            imname = imlists[ \"CAM_FRONT\"][i-2] \r\n            shutil.copyfile(os.path.join(path, os.path.join( \"CAM_FRONT\", imname)), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+\".jpg\"))\r\n            global_im_counter += 1\r\n\r\n    for i in range(len(imlists[\"CAM_BACK\"])):\r\n        imname = imlists[ \"CAM_FRONT_RIGHT\"][i] \r\n        shutil.copyfile(os.path.join(path, os.path.join( \"CAM_FRONT_RIGHT\", imname)), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+ \".jpg\"))\r\n        global_im_counter += 1\r\n        imname = imlists[ \"CAM_BACK_RIGHT\"][i] \r\n        shutil.copyfile(os.path.join(path, os.path.join( \"CAM_BACK_RIGHT\", imname)), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+ \".jpg\"))\r\n        global_im_counter += 1\r\n        if i < len(imlists[\"CAM_BACK\"])-2:\r\n            imname = imlists[ \"CAM_BACK\"][i+2] \r\n            shutil.copyfile(os.path.join(path, os.path.join( \"CAM_BACK\", imname)), os.path.join(imagespath, \"{:06d}\".format(global_im_counter)+ \".jpg\"))\r\n            global_im_counter += 1\r\n\"\"\"\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/registrationConfig.xml",
    "content": "<Configuration id=\"{2D5793BC-A65D-4318-A1B9-A05044608385}\">\r\n  <entry key=\"calexUndistResMode\" value=\"2\"/>\r\n  <entry key=\"calexFolder\" value=\"C:\\Users\\gdrett.AD\\Documents\\\"/>\r\n  <entry key=\"calexUndistortNaming\" value=\"1\"/>\r\n  <entry key=\"calexTrans\" value=\"1\"/>\r\n  <entry key=\"calexUndistortPixelFormat\" value=\"32bppBGRA\"/>\r\n  <entry key=\"calexHasDisabled\" value=\"0x0\"/>\r\n  <entry key=\"calexRequiresUndistortPrincipal\" value=\"0x1\"/>\r\n  <entry key=\"calexExportImages\" value=\"true\"/>\r\n  <entry key=\"calexUndistortImageFormat\" value=\"png\"/>\r\n  <entry key=\"MvsExportScaleZ\" value=\"1.0\"/>\r\n  <entry key=\"MvsExportIsGeoreferenced\" value=\"0x1\"/>\r\n  <entry key=\"MvsExportIsModelCoordinates\" value=\"0\"/>\r\n  <entry key=\"calexRequiresColorCorrection\" value=\"0x0\"/>\r\n  <entry key=\"MvsExportScaleY\" value=\"1.0\"/>\r\n  <entry key=\"calexDownscale\" value=\"0x1\"/>\r\n  <entry key=\"calexRequiresEqualResolution\" value=\"0x0\"/>\r\n  <entry key=\"calexUndistMaxPixels\" value=\"0\"/>\r\n  <entry key=\"calexInputHasLayers\" value=\"0\"/>\r\n  <entry key=\"MvsExportScaleX\" value=\"1.0\"/>\r\n  <entry key=\"calexUndistFitMode\" value=\"0\"/>\r\n  <entry key=\"MvsExportRotationY\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportcoordinatesystemtype\" value=\"0\"/>\r\n  <entry key=\"MvsExportNormalFlipZ\" value=\"false\"/>\r\n  <entry key=\"MvsExportRotationX\" value=\"0.0\"/>\r\n  <entry key=\"hasCalexFilePath\" value=\"1\"/>\r\n  <entry key=\"MvsExportNormalFlipY\" value=\"false\"/>\r\n  <entry key=\"MvsExportNormalSpace\" value=\"Mikktspace\"/>\r\n  <entry key=\"calexHasUndistort\" value=\"2\"/>\r\n  <entry key=\"MvsExportNormalFlipX\" value=\"false\"/>\r\n  <entry key=\"MvsExportRotationZ\" value=\"0.0\"/>\r\n  <entry key=\"calexFileFormat\" value=\"Bundler v0.3 (negative z)\"/>\r\n  <entry key=\"MvsExportMoveZ\" value=\"0.0\"/>\r\n  <entry key=\"calexFileFormatId\" value=\"{648CB940-4126-48C3-A96A-B3601D03A6A0}\"/>\r\n  <entry key=\"hasCalexFileName\" value=\"1\"/>\r\n  <entry key=\"calexUndistCutOut\" value=\"1.0\"/>\r\n  <entry key=\"calexHasImageExport\" value=\"1\"/>\r\n  <entry key=\"calexUndistBackColor\" value=\"0\"/>\r\n  <entry key=\"MvsExportMoveX\" value=\"0.0\"/>\r\n  <entry key=\"MvsExportNormalRange\" value=\"ZeroToOne\"/>\r\n  <entry key=\"MvsExportMoveY\" value=\"0.0\"/>\r\n</Configuration>\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/realityCaptureTools/runRC.bat",
    "content": "::CapturingReality\r\n\r\n:: switch off console output\r\n::@echo off\r\n@echo on\r\nset RootFolder=%1\r\nset Video=\"%RootFolder%\\videos\\video.mp4\"\r\nset FPS=%2\r\n\r\nset ConfigFolder=D:\\Users\\gdrett\\src\\sibr_core\\install\\scripts\r\n:: path to RealityCapture application\r\nset RealityCaptureExe=\"C:\\Program Files\\Capturing Reality\\RealityCapture\\RealityCapture.exe\"\r\n\r\n:: variable storing path to images for creating model\r\nset Images=\"%RootFolder%\\images\"\r\nset TestImages=\"%RootFolder%\\test\"\r\nset TrainImages=\"%RootFolder%\\train\"\r\nset PathImages=\"%RootFolder%\\train\"\r\n\r\n:: set a new name for calculated model\r\nset ModelName=\"RCTest\"\r\n\r\n:: set the path, where model is going to be saved, and its name\r\nset ModelObj=\"%RootFolder%\\rcScene\\meshes\\mesh.obj\"\r\nset ModelXYZ=\"%RootFolder%\\rcScene\\meshes\\point_cloud.xyz\"\r\n\r\n:: variable storing path to images for texturing model\r\nset Project=\"%RootFolder%\\rcproj\\mesh.rcproj\"\r\n\r\n:: run RealityCapture\r\n:: test and fix video import when RC working again\r\n\r\necho %@Images%\r\n\r\n%RealityCaptureExe% -addFolder %TrainImages% ^\r\n        -addFolder %TestImages% ^\r\n        -importVideo %Video% %RootFolder%\\video_frames\\ %FPS% ^\r\n        -align ^\r\n        -selectMaximalComponent ^\r\n        -selectAllImages ^\r\n        -enableAlignment false ^\r\n        -selectImage *test_* ^\r\n        -enableAlignment true ^\r\n        -exportRegistration %RootFolder%\\rcScene\\test_cameras\\bundle.out %ConfigFolder%\\registrationConfig.xml ^\r\n        -selectAllImages ^\r\n        -enableAlignment false ^\r\n        -selectImage *frame* ^\r\n        -enableAlignment true ^\r\n        -exportRegistration %RootFolder%\\rcScene\\path_cameras\\bundle.out %ConfigFolder%\\registrationConfig.xml ^\r\n        -selectAllImages ^\r\n        -enableAlignment false ^\r\n        -selectImage *train_* ^\r\n        -enableAlignment true ^\r\n        -exportRegistration %RootFolder%\\rcScene\\cameras\\bundle.out %ConfigFolder%\\registrationConfig.xml ^\r\n        -setReconstructionRegionAuto ^\r\n        -scaleReconstructionRegion 1.4 1.4 2.5 center factor ^\r\n        -calculateNormalModel ^\r\n        -selectMarginalTriangles ^\r\n        -removeSelectedTriangles ^\r\n        -calculateTexture ^\r\n        -save %Project% ^\r\n        -renameSelectedModel %ModelName% ^\r\n        -exportModel %ModelName% %ModelObj% ^\r\n        -exportModel %ModelName% %ModelXYZ% ^\r\n        -quit\r\n       \r\n        \r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/textureMesh/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(textureMesh)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n\tsibr_system\n\tsibr_assets\n    sibr_graphics\n\tsibr_raycaster\n\tsibr_imgproc\n\tsibr_view\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/textureMesh/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"core/system/CommandLineArgs.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include \"core/graphics/Image.hpp\"\n#include \"core/graphics/Mesh.hpp\"\n#include \"core/imgproc/MeshTexturing.hpp\"\n#include \"core/scene/BasicIBRScene.hpp\"\n\nusing namespace sibr;\n\n\nstruct TexturingAppArgs : virtual BasicIBRAppArgs {\n\tArg<std::string> meshPath = { \"mesh\", \"\" };\n\tRequiredArg<std::string> output_path = { \"output\", \"output texture path\" };\n\tArg<int> output_size = { \"size\", 8192, \"texture side\" };\n\tArg<bool> flood_fill = { \"flood\", \"perform flood fill\" };\n\tArg<bool> poisson_fill = { \"poisson\", \"perform Poisson filling (slow on large images)\" };\n\tArg<float> samples = { \"samples\", 1.0, \"%ge of total samples to be used for texturing\" };\n};\n\nint main(int ac, char** av) {\n\n\t// Parse Command-line Args\n\tsibr::CommandLineArgs::parseMainArgs(ac, av);\n\n\tTexturingAppArgs args;\n\n\t// Display help.\n\tif(!args.dataset_path.isInit() || !args.output_path.isInit()) {\n\t\tstd::cout << \"Usage: \" << std::endl;\n\t\tstd::cout << \"\\tRequired: --path path/to/dataset --output path/to/output/file.png\" << std::endl;\n\t\tstd::cout << \"\\tOptional: --size 8192 --flood (flood fill) --poisson (poisson fill)\" << std::endl;\n\t\treturn 0;\n\t}\n\n\tBasicIBRScene::SceneOptions opts;\n\topts.renderTargets = false;\n\tif (!args.meshPath.get().empty()) {\n\t\topts.mesh = false;\n\t}\n\topts.texture = false;\n\n\tSIBR_LOG << \"[Texturing] Loading data...\" << std::endl;\n\n\tBasicIBRScene scene(args, opts);\n\t\n\tif (!scene.proxies()->hasProxy()) {\n\t\tsibr::Mesh::Ptr customMesh;\n\t\tcustomMesh.reset(new Mesh());\n\t\tcustomMesh->load(args.meshPath);\n\t\tscene.proxies()->replaceProxyPtr(customMesh);\n\t}\n\n\tMeshTexturing texturer(args.output_size);\n\ttexturer.setMesh(scene.proxies()->proxyPtr());\n\ttexturer.reproject(scene.cameras()->inputCameras(), scene.images()->inputImages(), args.samples);\n\n\t// Export options.\n\t// UVs start at the bottom of the image, we have to flip.\n\tuint options = MeshTexturing::FLIP_VERTICAL;\n\tif (args.flood_fill) {\n\t\toptions = options | MeshTexturing::FLOOD_FILL;\n\t}\n\tif (args.poisson_fill) {\n\t\toptions = options | MeshTexturing::POISSON_FILL;\n\t}\n\n\tsibr::ImageRGB::Ptr result = texturer.getTexture(options);\n\n\tresult->save(args.output_path);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/tonemapper/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(tonemapper)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n\tsibr_system\n\tsibr_assets\n    sibr_graphics\n\tsibr_imgproc\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/tonemapper/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include \"core/system/CommandLineArgs.hpp\"\n#include \"core/assets/InputCamera.hpp\"\n#include \"core/graphics/Image.hpp\"\n#include \"core/graphics/Mesh.hpp\"\n#include \"core/imgproc/MeshTexturing.hpp\"\n#include \"core/scene/BasicIBRScene.hpp\"\n\nusing namespace sibr;\n\n\nstruct TonemapperAppArgs : virtual AppArgs {\n\tRequiredArg<std::string> path = { \"path\", \"path to the EXR images directory\" };\n\tArg<std::string> output = { \"output\", \"\", \"output directory path\" };\n\tArg<std::string> outputExtension = { \"ext\", \"png\", \"output files extension\" };\n\tArg<float> exposure = { \"exposure\", 1.0f, \"exposure value\" };\n\tArg<float> gamma = { \"gamma\", 2.2f, \"gamma value\" };\n};\n\nvoid tonemap(const sibr::ImageRGB32F& hdrImg, sibr::ImageRGB& ldrImg, float exposure, float gamma) {\n\tconst cv::Mat & tonemaped = hdrImg.toOpenCV();\n\tconst cv::Mat exposed = -exposure * tonemaped;\n\tcv::Mat tonemaped2;\n\tcv::exp(exposed, tonemaped2);\n\ttonemaped2 = cv::Scalar(1.0f, 1.0f, 1.0f) - tonemaped2;\n\tif (gamma > 0.0f) {\n\t\tcv::pow(tonemaped2, 1.0f / gamma, tonemaped2);\n\t}\n\tcv::Mat tonemapedRGB;\n\ttonemaped2.convertTo(tonemapedRGB, CV_8UC3, 255.0f);\n\tldrImg.fromOpenCV(tonemapedRGB);\n}\n\nint main(int ac, char** av) {\n\n\t// Parse Command-line Args\n\tsibr::CommandLineArgs::parseMainArgs(ac, av);\n\n\tTonemapperAppArgs args;\n\n\t// Add the extension dot if needed.\n\tstd::string extension = args.outputExtension;\n\tif (!extension.empty() && extension[0] != '.') {\n\t\textension = \".\" + extension;\n\t}\n\t\n\t// Input/output paths.\n\tconst std::string inputPath = args.path;\n\tstd::string outputPath = args.output;\n\t// If we output in the same dir, we want to avoid collisions.\n\tif (outputPath.empty()) {\n\t\toutputPath = inputPath;\n\t\textension = \"_ldr\" + extension;\n\t} else {\n\t\tsibr::makeDirectory(outputPath);\n\t}\n\n\tconst auto files = sibr::listFiles(inputPath, false, false, { \"exr\" });\n\n\tfor (const auto& file : files) {\n\t\tconst std::string src = inputPath + \"/\" + file;\n\t\tconst std::string dst = outputPath + \"/\" + sibr::removeExtension(file) + extension;\n\n\t\tsibr::ImageRGB32F hdrImg;\n\t\tsibr::ImageRGB ldrImg;\n\t\thdrImg.load(src);\n\t\ttonemap(hdrImg, ldrImg, args.exposure, args.gamma);\n\t\tldrImg.save(dst);\n\t}\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/unwrapMesh/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(unwrapMesh)\n\n# Define build output for project\nadd_executable(${PROJECT_NAME} main.cpp)\n\ntarget_link_libraries(${PROJECT_NAME}\n    ${Boost_LIBRARIES}\n\tsibr_system\n\tsibr_assets\n    sibr_graphics\n)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/unwrapMesh/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n\n#include <core/system/Config.hpp>\n#include <core/graphics/Mesh.hpp>\n#include <core/assets/UVUnwrapper.hpp>\n#include <core/system/CommandLineArgs.hpp>\n\n\nusing namespace sibr;\n\n/** Options for mesh unwrapping. */\nstruct UVMapperArgs : public AppArgs {\n\tRequiredArg<std::string> path = { \"path\", \"path to the mesh\" };\n\tArg<std::string> output = { \"output\", \"\", \"path to the output mesh\" };\n\tArg<int> size = { \"size\", 4096, \"target UV map width (approx.)\" };\n\tArg<bool> visu = { \"visu\", \"save visualisation\" };\n\tArg<std::string> textureName = { \"texture-name\", \"TEXTURE_NAME_TO_PUT_IN_THE_FILE\", \"name of the texture to reference in the output mesh (Meshlab compatible)\" };\n};\n\nint main(int ac, char ** av){\n\n\tCommandLineArgs::parseMainArgs(ac, av);\n\tUVMapperArgs args;\n\tstd::string outputFile = args.output;\n\tif(outputFile.empty()) {\n\t\toutputFile = sibr::removeExtension(args.path.get()) + \"_output.obj\";\n\t}\n\tsibr::makeDirectory(sibr::parentDirectory(outputFile));\n\n\t// Load object file.\n\tMesh mesh(false);\n\tif(sibr::getExtension(args.path) == \"xml\") {\n\t\tmesh.loadMtsXML(args.path);\n\t} else {\n\t\tmesh.load(args.path);\n\t}\n\n\tUVUnwrapper unwrapper(mesh, uint32_t(args.size));\n\tauto finalMesh = unwrapper.unwrap();\n\tfinalMesh->save(outputFile, true, args.textureName);\n\t\n\t// Output debug vis.\n\tif (args.visu) {\n\t\tconst std::string baseName = sibr::removeExtension(outputFile);\n\t\tconst auto visuImgs = unwrapper.atlasVisualization();\n\t\tfor (uint32_t i = 0; i < visuImgs.size(); i++) {\n\t\t\tconst std::string fileName = baseName + \"_charts_atlas_\" + std::to_string(i) + \".png\";\n\t\t\tvisuImgs[i]->save(fileName);\n\t\t}\n\t}\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nproject(dataset_tools_utils)\n\nfile(GLOB SCRIPTS \"*.py\")\n\nadd_custom_target(${PROJECT_NAME} ALL)\n\ninclude(install_runtime)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/dataset_tools/preprocess\")\nibr_install_rsc(${PROJECT_NAME} TYPE \"scripts\" FOLDER \"utils\" FILES ${SCRIPTS})\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/TaskPipeline.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\nimport subprocess\nimport os, sys\nimport re\nimport shutil\nfrom importlib import import_module\nfrom utils.convert import updateStringFromDict\nfrom utils.commands import runCommand\n\nclass TaskPipeline:\n    def __init__(self, args, steps, programs):\n        self.args = args\n        self.steps = steps\n        self.programs = programs\n\n    def isExpressionValid(self, expression):\n        if not re.match(r\"^((?:not|and|or|is|in|\\$\\{\\w+\\})+\\s*)+$\", expression):\n            print(\"Invalid expression '%s'.\" % expression)\n\n        return eval(updateStringFromDict(expression, self.args))\n\n    def runProcessSteps(self):\n        for step in self.steps:\n#            print(\"RUN STEP \", step)\n            if \"if\" in step and not self.isExpressionValid(step[\"if\"]):\n                print(\"Nothing to do on step %s. Skipping.\" % (step[\"name\"]))\n                continue\n\n            print(\"Running step %s...\" % step[\"name\"])\n            command_args = []\n            for i in range(5):\n                if \"app\" in step and \"optional_arg\"+str(i) in step and self.isExpressionValid(step[\"optional_arg\"+str(i)][0]):\n                    optional_arg = []\n                    for optional_arg in step[\"optional_arg\"+str(i)][1:]:\n#                        print(\"Parsing... \", optional_arg, \" \", updateStringFromDict(optional_arg, self.args))\n                        command_args.append(updateStringFromDict(optional_arg, self.args))\n\n            if \"app\" in step:\n#                print(\"Parsing command args...\")\n                for command_arg in step[\"command_args\"]:\n#                    print(\"Parsing... \", command_arg, \" \", updateStringFromDict(command_arg, self.args))\n                    command_args.append(updateStringFromDict(command_arg, self.args))\n\n                # for optionally quitting\n                if \"app\" in step and \"optional_final_arg\" in step and self.isExpressionValid(step[\"optional_final_arg\"][0]):\n                    for command_arg in step[\"optional_final_arg\"][1:]:\n#                        print(\"Parsing... \", command_arg, \" \", updateStringFromDict(command_arg, self.args))\n                        command_args.append(updateStringFromDict(command_arg, self.args))\n\n                if self.args[\"dry_run\"]:\n                    success = True\n                else:\n                    completedProcess = runCommand(self.programs[step[\"app\"]][\"path\"], command_args)\n                    success = completedProcess.returncode == 0\n\n            elif \"function\" in step:\n                if '.' in step[\"function\"]:\n                    currentModuleName, currentFunctionName = step[\"function\"].rsplit('.', 1)\n                    currentFunction = getattr(import_module(currentModuleName), currentFunctionName)\n                else:\n                    print(\"Missing module name for function %s. Aborting.\" % (step[\"function\"]))\n                    sys.exit(1)\n\n                if self.args[\"dry_run\"]:\n                    print('function : %s(%s)' % (step[\"function\"], ', '.join([ \"%s=%s\" % (key, ([updateStringFromDict(item, self.args) for item in val]\n                                                                                                 if type(val) is list else\n                                                                                                 updateStringFromDict(val, self.args)))\n                                                                                                    for key, val in step[\"function_args\"].items()])))\n                else:\n                    ret = currentFunction(**{ key: ([updateStringFromDict(item, self.args) for item in val]\n                                                if type(val) is list else\n                                                updateStringFromDict(val, self.args))\n                                                    for key, val in step[\"function_args\"].items() })\n                    if ret != None:\n                        self.args[ret[0]] = ret[1]\n                        print (\"After step {}: Setting args[{}]={}\".format( step[\"function\"], ret[0] , ret[1], ret[0], self.args[ret[0]]))\n\n                success = True\n            else:\n                print(\"Nothing to do on step %s. Skipping.\" % (step[\"name\"]))\n                continue\n\n            if success:\n                print(\"Step %s successful.\" % (step[\"name\"]))\n            else:\n                sys.stdout.flush()\n                sys.stderr.flush()\n                print(\"Error on step %s. Aborting.\" % (step[\"name\"]))\n                sys.exit(1)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/color.py",
    "content": "# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n\r\n\r\nimport os\r\nos.system(\"color\")\r\n\r\nCOLOR = {\r\n    \"HEADER\": \"\\033[95m\",\r\n    \"BLUE\": \"\\033[94m\",\r\n    \"GREEN\": \"\\033[92m\",\r\n    \"RED\": \"\\033[91m\",\r\n    \"ENDC\": \"\\033[0m\",\r\n}\r\n\r\n#print(COLOR[\"GREEN\"], \"Testing Green!!\", COLOR[\"ENDC\"])\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/commands.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\nimport subprocess\nimport os, sys\nfrom shutil import which\nfrom utils.paths import getBinariesPath, getColmapPath, getMeshlabPath, getRCPath\n\ndef getProcess(programName, binaryPath = getBinariesPath()):\n    suffixes = [ '', '_msr', '_rwdi', '_d']\n\n    print(\"BINARIES \", binaryPath)\n    for suffix in suffixes:\n        binary = os.path.join(binaryPath, programName + suffix + (\".exe\" if os.name == 'nt' else ''))\n\n        if os.path.isfile(binary) or which(binary) is not None:\n            print(\"Program '%s' found in '%s'.\" % (programName, binary))\n            return binary\n\ndef getRCprocess(binaryPath = getRCPath()):\n    programName = \"RealityCapture\"\n    binary = os.path.join(binaryPath, programName + \".exe\")\n\n    if os.path.isfile(binary):\n        print(\"Program '%s' found in '%s'.\" % (programName, binary))\n        return binary\n\n\ndef runCommand(binary, command_args):\n#    print(\"Running process '%s'\" % (' '.join([binary, *command_args])))\n    sys.stdout.flush()\n    completedProcess = subprocess.run([binary, *command_args])\n\n    if completedProcess.returncode == 0:\n        print(\"Process %s completed.\" % binary)\n    else:\n        sys.stdout.flush()\n        sys.stderr.flush()\n        print(\"Process %s failed with code %d.\" % (binary, completedProcess.returncode))\n\n    return completedProcess\n\ndef getColmap(colmapPath = getColmapPath()):\n    colmapBinary = os.path.join(colmapPath, \"COLMAP.bat\" if os.name == 'nt' else 'colmap')\n\n    if os.path.isfile(colmapBinary) or which(colmapBinary) is not None:\n        print(\"Program '%s' found in '%s'.\" % (colmapBinary, colmapPath))\n        return colmapBinary\n    else:\n        print(\"Program '%s' not found in '%s'. Aborting.\" % (colmapBinary, colmapPath))\n        return None\n\ndef getMeshlabServer(meshlabPath = getMeshlabPath()):\n    meshlabserverBinary = os.path.join(meshlabPath, \"meshlabserver\" + ('.exe' if os.name == 'nt' else ''))\n\n    if os.path.isfile(meshlabserverBinary) or which(meshlabserverBinary) is not None:\n        print(\"Program '%s' found in '%s'.\" % (meshlabserverBinary, meshlabPath))\n        return meshlabserverBinary\n    else:\n        print(\"Program '%s' not found in '%s'. Aborting.\" % (meshlabserverBinary, meshlabPath))\n        return None\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/convert.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\nimport os\nimport re\n\ndef updateStringFromDict(string, map, format='${%s}', fix_paths=True):\n    newstring = string\n\n    for keyword, value in map.items():\n        newstring = newstring.replace(format % keyword, str(value))\n        # if it's a path, get absolute path\n        if fix_paths and re.match(r\"^(?:\\w:[\\\\\\/]*|[@A-Za-z_.0-9-]*[\\\\\\/]+|\\.{1,2}[\\\\\\/])(?:[\\\\\\/]|[@A-Za-z_.0-9-]+)*$\", newstring):\n            newstring = os.path.abspath(newstring)\n\n    return newstring\n\ndef fixMeshEol(meshPath, newMeshPath):\n    with open(meshPath,\"rb\") as meshFile, open(newMeshPath, \"wb\") as newMeshFile:\n        meshBytes = meshFile.read()\n        endBytes = b\"end_header\"\n        badEol = b\"\\r\\n\"\n        newEol = b\"\\n\"\n\n        index = meshBytes.find(endBytes) + len(endBytes) + len(badEol)\n\n        newMeshBytes = meshBytes[0:index].replace(badEol, newEol)\n        newMeshBytes += meshBytes[index:]\n\n        newMeshFile.write(newMeshBytes)"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/datasets.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\nimport os\nfrom enum import Enum, unique\n\n@unique\nclass DatasetType(Enum):\n    SIBR = 'sibr'\n    COLMAP = 'colmap'\n    CAPREAL = 'capreal'\n\ndatasetStructure = { \n    \"colmap\": [ \"colmap\", \"colmap/stereo\", \"colmap/sparse\" ],\n    \"capreal\": [ \"capreal\", \"capreal/undistorted\" ],\n    \"sibr\": [ \"cameras\", \"images\", \"meshes\" ]\n}\n\ndef buildDatasetStructure(path, types):\n    for folder in [folder for type in types for folder in datasetStructure[type]]:\n        new_folder = os.path.abspath(os.path.join(path, folder))\n        print(\"Creating folder %s...\" % new_folder)\n        os.makedirs(new_folder, exist_ok=True)"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/preprocess/utils/paths.py",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n#!/usr/bin/env python\n#! -*- encoding: utf-8 -*-\n\nimport os\n\ndef getBinariesPath():\n    if os.path.exists(os.path.join(os.path.dirname(__file__), \"../../bin\")):\n        return os.path.abspath(os.path.join(os.path.dirname(__file__), \"../../bin\"))\n    else: \n        return os.path.abspath(os.path.join(os.path.dirname(__file__), \"../../../../../install/bin\"))\n\ndef getColmapPath():\n    return os.environ['COLMAP_PATH'] if 'COLMAP_PATH' in os.environ else (\"C:\\\\Program Files\\\\Colmap\" if os.name == 'nt' else '')\n    \ndef getMeshlabPath():\n    return os.environ['MESHLAB_PATH'] if 'MESHLAB_PATH' in os.environ else (\"C:\\\\Program Files\\\\VCG\\\\Meshlab\" if os.name == 'nt' else '')\n\ndef getRCPath():\n    return os.environ['RC_PATH'] if 'RC_PATH' in os.environ else \"C:\\\\Program Files\\\\Capturing Reality\\\\RealityCapture\\\\\"\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/dataset_tools/scripts/processRC.py",
    "content": "\r\n# Copyright (C) 2020, Inria\r\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n# All rights reserved.\r\n# \r\n# This software is free for non-commercial, research and evaluation use \r\n# under the terms of the LICENSE.md file.\r\n# \r\n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n#\r\n\r\n\r\n#!/usr/bin/env python\r\n#! -*- encoding: utf-8 -*-\r\n\r\n\"\"\" @package dataset_tools_preprocess\r\nThis script processes images and creates an RealityCapture (RC) reconstruction, then creates a colmap version using the RC camera registration\r\n\r\nParameters: -h help,\r\n            -path <path to your dataset folder>,\r\n\r\nUsage: python processRC.py -path <path to your dataset folder>\r\n\r\n\"\"\"\r\n\r\nimport os, sys, shutil\r\nos.sys.path.append('../preprocess/')\r\nos.sys.path.append('../preprocess/realityCaptureTools')\r\nos.sys.path.append('../preprocess/fullColmapProcess')\r\nos.sys.path.append('../preprocess/converters')\r\n\r\nimport json\r\nimport argparse\r\nfrom utils.paths import getBinariesPath, getColmapPath, getMeshlabPath\r\nfrom utils.commands import  getProcess, getColmap, getRCprocess\r\nfrom utils.TaskPipeline import TaskPipeline\r\nimport rc_tools\r\nimport colmap2nerf\r\nimport selective_colmap_process\r\n\r\ndef find_file(filename):\r\n    fname = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)\r\n    if not os.path.exists(fname):\r\n        fname = os.path.join(\"../preprocess/fullColmapProcess\", filename)\r\n    if not os.path.exists(fname):\r\n        fname = os.path.join(\"../preprocess/realityCaptureTools\", filename)\r\n    if not os.path.exists(fname):\r\n        fname = os.path.join(\"../preprocess/converters\", filename)\r\n    return fname\r\n    \r\n\r\ndef main():\r\n    parser = argparse.ArgumentParser()\r\n\r\n    # common arguments\r\n    parser.add_argument(\"--sibrBinariesPath\", type=str, default=getBinariesPath(), help=\"binaries directory of SIBR\")\r\n    parser.add_argument(\"--colmapPath\", type=str, default=getColmapPath(), help=\"path to directory colmap.bat / colmap.bin directory\")\r\n    parser.add_argument(\"--quality\", type=str, default='default', choices=['default', 'low', 'medium', 'average', 'high', 'extreme'],\r\n        help=\"quality of the reconstruction\")\r\n    parser.add_argument(\"--path\", type=str, required=True, help=\"path to your dataset folder\")\r\n    parser.add_argument(\"--dry_run\", action='store_true', help=\"run without calling commands\")\r\n    parser.add_argument(\"--rc_path\", type=str, required=False, help=\"path to rc dataset, containing bundle.out and images\")\r\n    parser.add_argument(\"--out_path\", type=str, required=False, help = \"output path \")\r\n    parser.add_argument(\"--video_name\", type=str, default='default', required=False, help = \"name of video file to load\")\r\n    parser.add_argument(\"--create_colmap\", action='store_true', help=\"create colmap hierarchy\")\r\n    parser.add_argument(\"--target_width\", type=str, default='default', help=\"colmap_target_width\")\r\n    parser.add_argument(\"--from_step\", type=str, default='default', help=\"Run from this step to --to_step (or end if no to_step\")\r\n    parser.add_argument(\"--to_step\", type=str, default='default', help=\"up to but *excluding* this step (from --from_step); must be unique steps\")\r\n\r\n    # RC arguments\r\n    parser.add_argument(\"--do_mvs\", action='store_false', help=\"use train folder\")\r\n    parser.add_argument(\"--calib_only\", action='store_true', help=\"only do calibration\")\r\n    parser.add_argument(\"--hires_nerf\", action='store_true', help=\"create hi res nerf\")\r\n    parser.add_argument(\"--car_data\", action='store_true', help=\"pre(pre)process car camera data \")\r\n    parser.add_argument(\"--do_train\", action='store_false', help=\"use train folder\")\r\n    parser.add_argument(\"--do_validation\", action='store_false', help=\"use validation folder\")\r\n    parser.add_argument(\"--no_validation_split\", action='store_true', help=\"dont do validation split\")\r\n    parser.add_argument(\"--do_video\", action='store_true', help=\"use video folder (mp4)\")\r\n    parser.add_argument(\"--do_test\", action='store_true', help=\"use test folder (stills path)\")\r\n    parser.add_argument(\"--auto_recon_area\", action='store_true', help=\"automatically set recon area (no user intervention)\")\r\n\r\n    parser.add_argument(\"--config_folder\", type=str, default='default', help=\"folder containing configuration files; usually cwd\")\r\n    parser.add_argument(\"--model_name\", type=str, default='default', help=\"Internal name of RC model\")\r\n    parser.add_argument(\"--path_prefix\", type=str, default='default', help=\"Internal prefix of path images\")\r\n    parser.add_argument(\"--one_over_fps\", type=str, default='default', help=\"Sampling rate for the video\")\r\n    parser.add_argument(\"--valid_skip\", type=str, default='default', help=\"skip every nth image for validation\")\r\n    # \"presets\"\r\n    parser.add_argument(\"--images_only\", action='store_false', help=\"just process images: no validation, no test\")\r\n    parser.add_argument(\"--video_only\", action='store_true', help=\"just process video: no photos, no test\")\r\n\r\n    parser.add_argument(\"--no_refl\", action='store_true', help=\"dont densify mesh, dont convert_sibr, dont create nerf (def: false)\")\r\n\r\n\r\n    # needed to avoid parsing issue for passing arguments to next command (TODO)\r\n    parser.add_argument(\"--video_filename\", type=str, default='default', help=\"full path of video file (internal argument; do not set)\")\r\n    parser.add_argument(\"--mesh_obj_filename\", type=str, default='default', help=\"full path of obj mesh file (internal argument; do not set)\")\r\n    parser.add_argument(\"--mesh_xyz_filename\", type=str, default='default', help=\"full path of xyz point cloud file (internal argument; do not set)\")\r\n    parser.add_argument(\"--mesh_ply_filename\", type=str, default='default', help=\"full path of ply mesh file (internal argument; do not set)\")\r\n\r\n    # colmap\r\n    #colmap performance arguments\r\n    parser.add_argument(\"--numGPUs\", type=int, default=2, help=\"number of GPUs allocated to Colmap\")\r\n\r\n    # Patch match stereo\r\n    parser.add_argument(\"--PatchMatchStereo.max_image_size\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotMaxImageSize\")\r\n    parser.add_argument(\"--PatchMatchStereo.window_radius\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotWindowRadius\")\r\n    parser.add_argument(\"--PatchMatchStereo.window_step\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotWindowStep\")\r\n    parser.add_argument(\"--PatchMatchStereo.num_samples\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotNumSamples\")\r\n    parser.add_argument(\"--PatchMatchStereo.num_iterations\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotNumIterations\")\r\n    parser.add_argument(\"--PatchMatchStereo.geom_consistency\", type=int, dest=\"patchMatchStereo_PatchMatchStereoDotGeomConsistency\")\r\n\r\n    # Stereo fusion\r\n    parser.add_argument(\"--StereoFusion.check_num_images\", type=int, dest=\"stereoFusion_CheckNumImages\")\r\n    parser.add_argument(\"--StereoFusion.max_image_size\", type=int, dest=\"stereoFusion_MaxImageSize\")\r\n\r\n\r\n    args = vars(parser.parse_args())\r\n\r\n    from_step = args[\"from_step\"]\r\n    to_step = args[\"to_step\"]\r\n\r\n    # Update args with quality values\r\n    fname = find_file(\"ColmapQualityParameters.json\")\r\n    with open(fname, \"r\") as qualityParamsFile:\r\n        qualityParams = json.load(qualityParamsFile)\r\n\r\n        for key, value in qualityParams.items():\r\n            if not key in args or args[key] is None:\r\n                args[key] = qualityParams[key][args[\"quality\"]] if args[\"quality\"] in qualityParams[key] else qualityParams[key][\"default\"]\r\n\r\n    # Get process steps\r\n    fname = find_file(\"processRCSteps.json\")\r\n    with open(fname, \"r\") as processStepsFile:\r\n        steps = json.load(processStepsFile)[\"steps\"]\r\n\r\n    # Fixing path values\r\n    args[\"path\"] = os.path.abspath(args[\"path\"])\r\n    args[\"sibrBinariesPath\"] = os.path.abspath(args[\"sibrBinariesPath\"])\r\n    args[\"colmapPath\"] = os.path.abspath(args[\"colmapPath\"])\r\n    args[\"gpusIndices\"] = ','.join([str(i) for i in range(args[\"numGPUs\"])])\r\n\r\n    args[\"mesh_obj_filename\"] = os.path.join(args[\"path\"], os.path.join(\"rcScene\", os.path.join(\"meshes\", \"mesh.obj\")))\r\n    args[\"mesh_xyz_filename\"] = os.path.join(args[\"path\"], os.path.join(\"rcScene\", os.path.join(\"meshes\", \"point_cloud.xyz\")))\r\n    args[\"mesh_ply_filename\"] = os.path.join(args[\"path\"], os.path.join(\"sibr\", os.path.join(\"capreal\", \"mesh.ply\")))\r\n\r\n    args[\"path_prefix\"] = \"test_\"\r\n\r\n    # fixed in preprocess\r\n    args[\"video_filename\"] = os.path.join(args[\"path\"], os.path.join(\"raw\", os.path.join(\"videos\", \"XXX.mp4\")))\r\n    if args[\"config_folder\"] == 'default':\r\n        if os.path.exists(\"registrationConfig.xml\"):\r\n            args[\"config_folder\"] = \".\"\r\n        elif os.path.exists(\"../preprocess/realityCaptureTools/registrationConfig.xml\"):\r\n            args[\"config_folder\"] = \"../preprocess/realityCaptureTools/\"\r\n\r\n    if args[\"valid_skip\"] == 'default' :\r\n        args[\"valid_skip\"] = \"10\"\r\n        \r\n    if args[\"one_over_fps\"] == 'default':\r\n        args[\"one_over_fps\"] = \"0.02\"\r\n\r\n    if args[\"target_width\"] == 'default':\r\n        args[\"target_width\"] = \"1000\"\r\n    print(\"TARGET WIDTH \", args[\"target_width\"])\r\n\r\n    if args[\"no_validation_split\"]:\r\n        args[\"do_validation_split\"] = False\r\n    else:\r\n        args[\"do_validation_split\"] = True\r\n\r\n    # presets\r\n\r\n    exclude_steps = []\r\n\r\n    if args[\"no_refl\"] == True:\r\n        exclude_steps = [ \"densify_mesh\", \"dense_mesh\", \"create_nerf\", \"convert_sibr_mesh\" ] \r\n        print(\"No densification, no sibr, no nerf, exclude:\", exclude_steps)\r\n\r\n    if args[\"car_data\"]:\r\n        print(\"Doing car data\")\r\n    else:\r\n        print(\"No car data\")\r\n\r\n    if args[\"calib_only\"]:\r\n        to_step = \"colmap_patch_match_stereo\"\r\n        args[\"do_mvs\"] = False\r\n        exclude_steps = [ \"densify_mesh\", \"dense_mesh\" ] \r\n\r\n    # either do video or do_test\r\n    if args[\"do_video\"]:\r\n        args[\"path_prefix\"] = \"frame\"       \r\n\r\n    if args[\"video_only\"]:\r\n        args[\"do_train\"] = False\r\n        args[\"do_validation\"] = False\r\n        args[\"do_test\"] = False\r\n        args[\"do_video\"] = True\r\n\r\n    if args[\"do_test\"]:\r\n        args[\"path_prefix\"] = \"test_\"       \r\n        args[\"do_video\"] = False\r\n\r\n\r\n    if args[\"video_only\"] and args[\"calib_only\"]:\r\n        exclude_steps = [ \"densify_mesh\", \"dense_mesh\",  \"rc_to_colmap_path_cameras\", \"rc_to_colmap_validation_cameras\" ]\r\n\r\n    programs = {\r\n        \"colmap\": {\r\n            \"path\": getColmap(args[\"colmapPath\"])\r\n        },\r\n        \"RC\": {\r\n            \"path\": getRCprocess()\r\n        }\r\n    }\r\n\r\n    # TODO: move to generic taskpipeline code; \r\n    if( from_step != 'default' or to_step != 'default' or exclude_steps != []):\r\n        # check if to_step exists\r\n        # select steps\r\n        newsteps = []\r\n        if from_step != 'default':\r\n            adding_steps = False\r\n        else:\r\n            adding_steps = True\r\n                \r\n        for s in steps:\r\n            if s['name'] == from_step :\r\n                adding_steps = True\r\n            if s['name'] == to_step :\r\n                break\r\n            if adding_steps and (not (s['name'] in exclude_steps)):\r\n                newsteps.append(s)\r\n\r\n        steps = newsteps\r\n\r\n    pipeline = TaskPipeline(args, steps, programs)\r\n\r\n    pipeline.runProcessSteps()\r\n    \r\n    print(\"selectiveColmapProcess has finished successfully.\")\r\n    sys.exit(0)\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\nproject(sibr_gaussian_all)\n\nadd_subdirectory(apps)\nadd_subdirectory(renderer)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/gaussian\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/apps/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_gaussian_apps)\n\nadd_subdirectory(gaussianViewer/)"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/apps/gaussianViewer/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_gaussianViewer_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\tsibr_gaussian\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/gaussian/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"gaussian\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/apps/gaussianViewer/main.cpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n#include <fstream>\n\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <core/system/String.hpp>\n#include \"projects/gaussianviewer/renderer/GaussianView.hpp\" \n\n#include <core/renderer/DepthRenderer.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n#include <algorithm>\n#include <boost/filesystem.hpp>\n#include <regex>\n#include <imgui/imgui_internal.h>\n\nnamespace fs = boost::filesystem;\n\nstd::string findLargestNumberedSubdirectory(const std::string& directoryPath) {\n\tfs::path dirPath(directoryPath);\n\tif (!fs::exists(dirPath) || !fs::is_directory(dirPath)) {\n\t\tstd::cerr << \"Invalid directory: \" << directoryPath << std::endl;\n\t\treturn \"\";\n\t}\n\n\tstd::regex regexPattern(R\"_(iteration_(\\d+))_\");\n\tstd::string largestSubdirectory;\n\tint largestNumber = -1;\n\n\tfor (const auto& entry : fs::directory_iterator(dirPath)) {\n\t\tif (fs::is_directory(entry)) {\n\t\t\tstd::string subdirectory = entry.path().filename().string();\n\t\t\tstd::smatch match;\n\n\t\t\tif (std::regex_match(subdirectory, match, regexPattern)) {\n\t\t\t\tint number = std::stoi(match[1]);\n\n\t\t\t\tif (number > largestNumber) {\n\t\t\t\t\tlargestNumber = number;\n\t\t\t\t\tlargestSubdirectory = subdirectory;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn largestSubdirectory;\n}\n\n\n#define PROGRAM_NAME \"sibr_3Dgaussian\"\nusing namespace sibr;\n\nstd::pair<int, int> findArg(const std::string& line, const std::string& name)\n{\n\tint start = line.find(name, 0);\n\tstart = line.find(\"=\", start);\n\tstart += 1;\n\tint end = line.find_first_of(\",)\", start);\n\treturn std::make_pair(start, end);\n}\n\nstatic void* User_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n\treturn (void*)0x1;\n}\n\nstatic void User_ReadLine(ImGuiContext*, ImGuiSettingsHandler* handler, void*, const char* line)\n{\n\tint i;\n\tif (sscanf(line, \"DontShow=%d\", &i) == 1)\n\t\tif (i)\n\t\t{\n\t\t\t*((bool*)handler->UserData) = true;\n\t\t\treturn;\n\t\t}\n\t*((bool*)handler->UserData) = false;\n}\n\nstatic void User_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n\t// Write a buffer\n\t// If a window wasn't opened in this session we preserve its settings\n\tbuf->reserve(buf->size() + 96); // ballpark reserve\n\tbuf->appendf(\"[UserData][UserData]\\nDontShow=%d\\n\", *((bool*)handler->UserData) ? 1 : 0);\n\tbuf->appendf(\"\\n\");\n}\n\nint main(int ac, char** av) \n{\n\t// Parse Command-line Args\n\tCommandLineArgs::parseMainArgs(ac, av);\n\tGaussianAppArgs myArgs;\n\tmyArgs.displayHelpIfRequired();\n\t\n\tif(!myArgs.modelPath.isInit() && myArgs.modelPathShort.isInit())\n\t\tmyArgs.modelPath = myArgs.modelPathShort.get();\n\tif(!myArgs.dataset_path.isInit() && myArgs.pathShort.isInit())\n\t\tmyArgs.dataset_path = myArgs.pathShort.get();\n\n\tint device = myArgs.device;\n\n\t// rendering size\n\tuint rendering_width = myArgs.rendering_size.get()[0];\n\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\n\t// window size\n\tuint win_width = rendering_width; // myArgs.win_width;\n\tuint win_height = rendering_height; // myArgs.win_height;\n\n\tconst char* toload = myArgs.modelPath.get().c_str();\n\n\t// Window setup\n\tsibr::Window\t\twindow(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/gaussians/\" + PROGRAM_NAME + \".ini\");\n\n\tbool messageRead = false;\n\tImGuiSettingsHandler ini_handler;\n\tini_handler.TypeName = \"UserData\";\n\tini_handler.UserData = &messageRead;\n\tini_handler.TypeHash = ImHash(\"UserData\", 0, 0);\n\tini_handler.ReadOpenFn = User_ReadOpen;\n\tini_handler.ReadLineFn = User_ReadLine;\n\tini_handler.WriteAllFn = User_WriteAll;\n\tImGui::GetCurrentContext()->SettingsHandlers.push_back(ini_handler);\n\twindow.loadSettings();\n\n\tstd::string cfgLine;\n\tstd::ifstream cfgFile(myArgs.modelPath.get() + \"/cfg_args\");\n\tif (!cfgFile.good())\n\t{\n\t\tSIBR_ERR << \"Could not find config file 'cfg_args' at \" << myArgs.modelPath.get();\n\t}\n\tstd::getline(cfgFile, cfgLine);\n\n\tif (!myArgs.dataset_path.isInit())\n\t{\n\t\tauto rng = findArg(cfgLine, \"source_path\");\n\t\tmyArgs.dataset_path = cfgLine.substr(rng.first + 1, rng.second - rng.first - 2);\n\t}\n\n\tauto rng = findArg(cfgLine, \"sh_degree\");\n\tint sh_degree = std::stoi(cfgLine.substr(rng.first, rng.second - rng.first));\n\n\trng = findArg(cfgLine, \"white_background\");\n\tbool white_background = cfgLine.substr(rng.first, rng.second - rng.first).find(\"True\") != -1;\n\n\tBasicIBRScene::SceneOptions myOpts;\n\tmyOpts.renderTargets = myArgs.loadImages;\n\tmyOpts.mesh = true;\n\tmyOpts.images = myArgs.loadImages;\n\tmyOpts.cameras = true;\n\tmyOpts.texture = false;\n\n\tBasicIBRScene::Ptr scene;\n\ttry\n\t{\n\t\tscene.reset(new BasicIBRScene(myArgs, myOpts));\n\t}\n\tcatch (...)\n\t{\n\t\tSIBR_LOG << \"Did not find specified input folder, loading from model path\" << std::endl;\n\t\tmyArgs.dataset_path = myArgs.modelPath.get();\n\t\tscene.reset(new BasicIBRScene(myArgs, myOpts));\n\t}\n\n\tstd::string plyfile = myArgs.modelPath.get();\n\tif (plyfile.back() != '/')\n\t\tplyfile += \"/\";\n\tplyfile += \"point_cloud\";\n\tif (!myArgs.iteration.isInit())\n\t{\n\t\tplyfile += \"/\" + findLargestNumberedSubdirectory(plyfile) + \"/point_cloud.ply\";\n\t}\n\telse\n\t{\n\t\tplyfile += \"/iteration_\" + myArgs.iteration.get() + \"/point_cloud.ply\";\n\t}\n\n\t// Setup the scene: load the proxy, create the texture arrays.\n\tconst uint flags = SIBR_GPU_LINEAR_SAMPLING | SIBR_FLIP_TEXTURE;\n\n\t// Fix rendering aspect ratio if user provided rendering size\n\tuint scene_width = scene->cameras()->inputCameras()[0]->w();\n\tuint scene_height = scene->cameras()->inputCameras()[0]->h();\n\tfloat scene_aspect_ratio = scene_width * 1.0f / scene_height;\n\tfloat rendering_aspect_ratio = rendering_width * 1.0f / rendering_height;\n\n\trendering_width = (rendering_width <= 0) ? std::min(1200U, scene_width) : rendering_width;\n\trendering_height = (rendering_height <= 0) ? std::min(1200U, scene_width) / scene_aspect_ratio : rendering_height;\n\tif ((rendering_width > 0) && !myArgs.force_aspect_ratio ) {\n\t\tif (abs(scene_aspect_ratio - rendering_aspect_ratio) > 0.001f) {\n\t\t\tif (scene_width > scene_height) {\n\t\t\t\trendering_height = rendering_width / scene_aspect_ratio;\n\t\t\t} \n\t\t\telse {\n\t\t\t\trendering_width = rendering_height * scene_aspect_ratio;\n\t\t\t}\n\t\t}\n\t}\n\tVector2u usedResolution(rendering_width, rendering_height);\n\n\tconst unsigned int sceneResWidth = usedResolution.x();\n\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\t// Create the ULR view.\n\tGaussianView::Ptr\tgaussianView(new GaussianView(scene, sceneResWidth, sceneResHeight, plyfile.c_str(), &messageRead, sh_degree, white_background, !myArgs.noInterop, device));\n\n\t// Raycaster.\n\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\traycaster->init();\n\traycaster->addMesh(scene->proxies()->proxy());\n\n\t// Camera handler for main view.\n\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), nullptr);\n\n\t// Add views to mvm.\n\tMultiViewManager        multiViewManager(window, false);\n\n\tif (myArgs.rendering_mode == 1) \n\t\tmultiViewManager.renderingMode(IRenderingMode::Ptr(new StereoAnaglyphRdrMode()));\n\t\n\tmultiViewManager.addIBRSubView(\"Point view\", gaussianView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide | ImGuiWindowFlags_NoBringToFrontOnFocus);\n\tmultiViewManager.addCameraForView(\"Point view\", generalCamera);\n\n\t// Top view\n\tconst std::shared_ptr<sibr::SceneDebugView> topView(new sibr::SceneDebugView(scene, generalCamera, myArgs, myArgs.imagesPath.get()));\n\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\tCHECK_GL_ERROR;\n\ttopView->active(false);\n\n\t// save images\n\tgeneralCamera->getCameraRecorder().setViewPath(gaussianView, myArgs.dataset_path.get());\n\tif (myArgs.pathFile.get() !=  \"\" ) \n\t{\n\t\tstd::cout << \"myArgs \" <<   myArgs<<std::endl;\n\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"Point view\"), \"\");\n\t\tif( !myArgs.noExit )\n\t\t\texit(0);\n\t}\n\n\n\t// Main looooooop.\n\twhile (window.isOpened()) \n\t{\t\n\t\tstd::cout << \" test \" << std::endl;\n\t\tsibr::Input::poll();\n\t\twindow.makeContextCurrent();\n\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\n\t\t\twindow.close();\n\t\t}\n\n\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\tmultiViewManager.onRender(window);\n\n\t\twindow.swapBuffer();\n\t\tCHECK_GL_ERROR;\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\nset(SIBR_PROJECT \"gaussian\")\nproject(sibr_${SIBR_PROJECT} LANGUAGES CXX)\n\nsibr_gitlibrary(TARGET CudaRasterizer\n    GIT_REPOSITORY \t\"https://github.com/graphdeco-inria/diff-gaussian-rasterization.git\"\n    GIT_TAG\t\t\t\"3509be80f83ee30599b23bb3542d45aea2174a03\"\n)\n\nfind_package(CUDAToolkit REQUIRED)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS} .)\nif (WIN32)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n\tCUDA::cudart\n\tCudaRasterizer\n)\nelse()\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n\tCUDA::cudart\n\tCudaRasterizer\n)\nendif()\n\nadd_definitions( -DSIBR_EXP_ULR_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/renderer\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS ${SHADERS}\n\tRSC_FOLDER ${SIBR_PROJECT}\n\n    #STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/Config.hpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n# include <core/system/CommandLineArgs.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_EXP_ULR_EXPORT\n#      ifdef SIBR_EXP_ULR_EXPORTS\n/* We are building this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllexport)\n#      else\n/* We are using this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_EXP_ULR_EXPORT\n# endif\n\nnamespace sibr {\n\n\t/// Arguments for all ULR applications.\n\tstruct GaussianAppArgs :\n\t\tvirtual BasicIBRAppArgs {\n\t\tRequiredArg<std::string> modelPath = { \"model-path\", \"Model directory\" };\n\t\tRequiredArg<std::string> modelPathShort = { \"m\", \"Model directory\" };\n\t\tRequiredArg<std::string> iteration = { \"iteration\", \"Iteration to load from model\" };\n\t\tRequiredArg<std::string> pathShort = {\"s\", \"path to the dataset root\"};\n\t\tArg<int> device = {\"device\", 0, \"CUDA device index\"};\n\t\tArg<bool> loadImages = { \"load_images\", \"Whether or not to load images for scene overview.\"};\n\t\tArg<bool> noInterop = { \"no_interop\", \"Don't try to use interop (may be required for unconventional OpenGL setups, like WSL)\" };\n\t\tArg<std::string> imagesPath = { \"images-path\", \"path to the dataset images\" };\n\t};\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/GaussianSurfaceRenderer.cpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n\n#include \"core/graphics/Texture.hpp\"\n#include \"GaussianSurfaceRenderer.hpp\"\n\nnamespace sibr { \n\n\tGaussianData::GaussianData(int num_gaussians, float* mean_data, float* rot_data, float* scale_data, float* alpha_data, float* color_data)\n\t{\n\t\t_num_gaussians = num_gaussians;\n\t\tglCreateBuffers(1, &meanBuffer);\n\t\tglCreateBuffers(1, &rotBuffer);\n\t\tglCreateBuffers(1, &scaleBuffer);\n\t\tglCreateBuffers(1, &alphaBuffer);\n\t\tglCreateBuffers(1, &colorBuffer);\n\t\tglNamedBufferStorage(meanBuffer, num_gaussians * 3 * sizeof(float), mean_data, 0);\n\t\tglNamedBufferStorage(rotBuffer, num_gaussians * 4 * sizeof(float), rot_data, 0);\n\t\tglNamedBufferStorage(scaleBuffer, num_gaussians * 3 * sizeof(float), scale_data, 0);\n\t\tglNamedBufferStorage(alphaBuffer, num_gaussians * sizeof(float), alpha_data, 0);\n\t\tglNamedBufferStorage(colorBuffer, num_gaussians * sizeof(float) * 48, color_data, 0);\n\t}\n\n\tvoid GaussianData::render(int G) const\n\t{\n\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, meanBuffer);\n\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, rotBuffer);\n\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, scaleBuffer);\n\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, alphaBuffer);\n\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, colorBuffer);\n\t\tglDrawArraysInstanced(GL_TRIANGLES, 0, 36, G);\n\t}\n\n\tGaussianSurfaceRenderer::GaussianSurfaceRenderer( void )\n\t{\n\t\t_shader.init(\"GaussianSurface\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"gaussian\") + \"/gaussian_surface.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"gaussian\") + \"/gaussian_surface.frag\"));\n\n\t\t_paramCamPos.init(_shader, \"rayOrigin\");\n\t\t_paramMVP.init(_shader,\"MVP\");\n\t\t_paramLimit.init(_shader, \"alpha_limit\");\n\t\t_paramStage.init(_shader, \"stage\");\n\n\t\tglCreateTextures(GL_TEXTURE_2D, 1, &idTexture);\n\t\tglTextureParameteri(idTexture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n\t\tglTextureParameteri(idTexture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n\t\tglCreateTextures(GL_TEXTURE_2D, 1, &colorTexture);\n\t\tglTextureParameteri(colorTexture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n\t\tglTextureParameteri(colorTexture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n\t\tglCreateFramebuffers(1, &fbo);\n\t\tglCreateRenderbuffers(1, &depthBuffer);\n\n\t\tmakeFBO(800, 800);\n\n\t\tclearProg = glCreateProgram();\n\t\tconst char* clearShaderSrc = R\"(\n\t\t\t#version 430\n\n\t\t\tlayout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n\n\t\t\tlayout(std430, binding = 0) buffer IntArray {\n\t\t\t\tint arr[];\n\t\t\t};\n\n\t\t\tlayout(location = 0) uniform int size;\n\n\t\t\tvoid main() {\n\t\t\t\tuint index = gl_GlobalInvocationID.x;\n\t\t\t\tif (index < size) {\n\t\t\t\t\tarr[index] = 0;\n\t\t\t\t}\n\t\t\t} \n\t\t\t)\";\n\t\tclearShader = glCreateShader(GL_COMPUTE_SHADER);\n\t\tglShaderSource(clearShader, 1, &clearShaderSrc, nullptr);\n\t\tglAttachShader(clearProg, clearShader);\n\t\tglLinkProgram(clearProg);\n\t}\n\n\tvoid GaussianSurfaceRenderer::makeFBO(int w, int h)\n\t{\n\t\tresX = w;\n\t\tresY = h;\n\n\t\tglBindTexture(GL_TEXTURE_2D, idTexture);\n\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, resX, resY, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);\n\n\t\tglBindTexture(GL_TEXTURE_2D, colorTexture);\n\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, resX, resY, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);\n\t\tglBindTexture(GL_TEXTURE_2D, 0);\n\n\t\tglNamedRenderbufferStorage(depthBuffer, GL_DEPTH_COMPONENT, resX, resY);\n\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, fbo);\n\t\tglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);\n\t\tglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, idTexture, 0);\n\t\tglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);\n\t}\n\n\tint\tGaussianSurfaceRenderer::process(int G, const GaussianData& mesh, const Camera& eye, IRenderTarget& target, float limit, sibr::Mesh::RenderMode mode, bool backFaceCulling)\n\t{\n\t\tglBindFramebuffer(GL_FRAMEBUFFER, fbo);\n\n\t\tglClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n\n\t\tif (target.w() != resX || target.h() != resY)\n\t\t{\n\t\t\tmakeFBO(target.w(), target.h());\n\t\t}\n\n\t\t// Solid pass\n\t\tGLuint drawBuffers[2];\n\t\tdrawBuffers[0] = GL_COLOR_ATTACHMENT0;\n\t\tdrawBuffers[1] = GL_COLOR_ATTACHMENT1;\n\t\tglDrawBuffers(2, drawBuffers);\n\n\t\tglEnable(GL_DEPTH_TEST);\n\t\tglDisable(GL_BLEND);\n\t\t_shader.begin();\n\t\t_paramMVP.set(eye.viewproj());\n\t\t_paramCamPos.set(eye.position());\n\t\t_paramLimit.set(limit);\n\t\t_paramStage.set(0);\n\t\tmesh.render(G);\n\n\t\t// Simple additive blendnig (no order)\n\t\tglDrawBuffers(1, drawBuffers);\n\t\tglDepthMask(GL_FALSE);\n\t\tglEnable(GL_BLEND);\n\t\tglBlendEquation(GL_FUNC_ADD);\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE);\n\t\t_paramStage.set(1);\n\t\tmesh.render(G);\n\n\t\tglDepthMask(GL_TRUE);\n\t\tglDisable(GL_BLEND);\n\n\t\t_shader.end();\n\n\t\tglReadBuffer(GL_COLOR_ATTACHMENT0);\n\t\tglBlitNamedFramebuffer(\n\t\t\tfbo, target.fbo(),\n\t\t\t0, 0, resX, resY,\n\t\t\t0, 0, resX, resY,\n\t\t\tGL_COLOR_BUFFER_BIT, GL_NEAREST);\n\n\t\treturn 0;\n\t}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/GaussianSurfaceRenderer.hpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/graphics/Camera.hpp>\n\n# include <core/renderer/Config.hpp>\n\nnamespace sibr { \n\n\tclass GaussianData\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<GaussianData>\tPtr;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tGaussianData(int num_gaussians, float* mean_data, float* rot_data, float* scale_data, float* alpha_data, float* color_data);\n\n\t\tvoid render(int G) const;\n\n\tprivate:\n\n\t\tint _num_gaussians;\n\t\tGLuint meanBuffer;\n\t\tGLuint rotBuffer;\n\t\tGLuint scaleBuffer;\n\t\tGLuint alphaBuffer;\n\t\tGLuint colorBuffer;\n\t};\n\n\t/** Render a mesh colored using the per-vertex color attribute.\n\t\\ingroup sibr_renderer\n\t*/\n\tclass GaussianSurfaceRenderer\n\t{\n\tpublic:\n\t\ttypedef std::shared_ptr<GaussianSurfaceRenderer>\tPtr;\n\n\tpublic:\n\n\t\t/// Constructor.\n\t\tGaussianSurfaceRenderer( void );\n\n\t\t/** Render the mesh using its vertices colors, interpolated over triangles.\n\t\t\\param mesh the mesh to render\n\t\t\\param eye the viewpoint to use\n\t\t\\param dst the destination rendertarget\n\t\t\\param mode the rendering mode of the mesh\n\t\t\\param backFaceCulling should backface culling be performed\n\t\t*/\n\t\tint\tprocess(\n\t\t\tint G,\n\t\t\t/*input*/\tconst GaussianData& mesh,\n\t\t\t/*input*/\tconst Camera& eye,\n\t\t\t/*output*/\tIRenderTarget& dst,\n\t\t\tfloat alphaLimit,\n\t\t\t/*mode*/    sibr::Mesh::RenderMode mode = sibr::Mesh::FillRenderMode,\n\t\t\t/*BFC*/     bool backFaceCulling = true);\n\n\t\tvoid makeFBO(int w, int h);\n\n\tprivate:\n\n\t\tGLuint idTexture;\n\t\tGLuint colorTexture;\n\t\tGLuint depthBuffer;\n\t\tGLuint fbo;\n\t\tint resX, resY;\n\n\t\tGLShader\t\t\t_shader; ///< Color shader.\n\t\tGLParameter\t\t\t_paramMVP; ///< MVP uniform.\n\t\tGLParameter\t\t\t_paramCamPos;\n\t\tGLParameter\t\t\t_paramLimit;\n\t\tGLParameter\t\t\t_paramStage;\n\t\tGLuint clearProg;\n\t\tGLuint clearShader;\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/GaussianView.cpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n#include <projects/gaussianviewer/renderer/GaussianView.hpp>\n#include <core/graphics/GUI.hpp>\n#include <thread>\n#include <boost/asio.hpp>\n#include <rasterizer.h>\n#include <imgui_internal.h>\n\n// Define the types and sizes that make up the contents of each Gaussian \n// in the trained model.\ntypedef sibr::Vector3f Pos;\ntemplate<int D>\nstruct SHs\n{\n\tfloat shs[(D+1)*(D+1)*3];\n};\nstruct Scale\n{\n\tfloat scale[3];\n};\nstruct Rot\n{\n\tfloat rot[4];\n};\ntemplate<int D>\nstruct RichPoint\n{\n\tPos pos;\n\tfloat n[3];\n\tSHs<D> shs;\n\tfloat opacity;\n\tScale scale;\n\tRot rot;\n};\n\nfloat sigmoid(const float m1)\n{\n\treturn 1.0f / (1.0f + exp(-m1));\n}\n\nfloat inverse_sigmoid(const float m1)\n{\n\treturn log(m1 / (1.0f - m1));\n}\n\n# define CUDA_SAFE_CALL_ALWAYS(A) \\\nA; \\\ncudaDeviceSynchronize(); \\\nif (cudaPeekAtLastError() != cudaSuccess) \\\nSIBR_ERR << cudaGetErrorString(cudaGetLastError());\n\n#if DEBUG || _DEBUG\n# define CUDA_SAFE_CALL(A) CUDA_SAFE_CALL_ALWAYS(A)\n#else\n# define CUDA_SAFE_CALL(A) A\n#endif\n\n// Load the Gaussians from the given file.\ntemplate<int D>\nint loadPly(const char* filename,\n\tstd::vector<Pos>& pos,\n\tstd::vector<SHs<3>>& shs,\n\tstd::vector<float>& opacities,\n\tstd::vector<Scale>& scales,\n\tstd::vector<Rot>& rot,\n\tsibr::Vector3f& minn,\n\tsibr::Vector3f& maxx)\n{\n\tstd::ifstream infile(filename, std::ios_base::binary);\n\n\tif (!infile.good())\n\t\tSIBR_ERR << \"Unable to find model's PLY file, attempted:\\n\" << filename << std::endl;\n\n\t// \"Parse\" header (it has to be a specific format anyway)\n\tstd::string buff;\n\tstd::getline(infile, buff);\n\tstd::getline(infile, buff);\n\n\tstd::string dummy;\n\tstd::getline(infile, buff);\n\tstd::stringstream ss(buff);\n\tint count;\n\tss >> dummy >> dummy >> count;\n\n\t// Output number of Gaussians contained\n\tSIBR_LOG << \"Loading \" << count << \" Gaussian splats\" << std::endl;\n\n\twhile (std::getline(infile, buff))\n\t\tif (buff.compare(\"end_header\") == 0)\n\t\t\tbreak;\n\n\t// Read all Gaussians at once (AoS)\n\tstd::vector<RichPoint<D>> points(count);\n\tinfile.read((char*)points.data(), count * sizeof(RichPoint<D>));\n\n\t// Resize our SoA data\n\tpos.resize(count);\n\tshs.resize(count);\n\tscales.resize(count);\n\trot.resize(count);\n\topacities.resize(count);\n\n\t// Gaussians are done training, they won't move anymore. Arrange\n\t// them according to 3D Morton order. This means better cache\n\t// behavior for reading Gaussians that end up in the same tile \n\t// (close in 3D --> close in 2D).\n\tminn = sibr::Vector3f(FLT_MAX, FLT_MAX, FLT_MAX);\n\tmaxx = -minn;\n\tfor (int i = 0; i < count; i++)\n\t{\n\t\tmaxx = maxx.cwiseMax(points[i].pos);\n\t\tminn = minn.cwiseMin(points[i].pos);\n\t}\n\tstd::vector<std::pair<uint64_t, int>> mapp(count);\n\tfor (int i = 0; i < count; i++)\n\t{\n\t\tsibr::Vector3f rel = (points[i].pos - minn).array() / (maxx - minn).array();\n\t\tsibr::Vector3f scaled = ((float((1 << 21) - 1)) * rel);\n\t\tsibr::Vector3i xyz = scaled.cast<int>();\n\n\t\tuint64_t code = 0;\n\t\tfor (int i = 0; i < 21; i++) {\n\t\t\tcode |= ((uint64_t(xyz.x() & (1 << i))) << (2 * i + 0));\n\t\t\tcode |= ((uint64_t(xyz.y() & (1 << i))) << (2 * i + 1));\n\t\t\tcode |= ((uint64_t(xyz.z() & (1 << i))) << (2 * i + 2));\n\t\t}\n\n\t\tmapp[i].first = code;\n\t\tmapp[i].second = i;\n\t}\n\tauto sorter = [](const std::pair < uint64_t, int>& a, const std::pair < uint64_t, int>& b) {\n\t\treturn a.first < b.first;\n\t};\n\tstd::sort(mapp.begin(), mapp.end(), sorter);\n\n\t// Move data from AoS to SoA\n\tint SH_N = (D + 1) * (D + 1);\n\tfor (int k = 0; k < count; k++)\n\t{\n\t\tint i = mapp[k].second;\n\t\tpos[k] = points[i].pos;\n\n\t\t// Normalize quaternion\n\t\tfloat length2 = 0;\n\t\tfor (int j = 0; j < 4; j++)\n\t\t\tlength2 += points[i].rot.rot[j] * points[i].rot.rot[j];\n\t\tfloat length = sqrt(length2);\n\t\tfor (int j = 0; j < 4; j++)\n\t\t\trot[k].rot[j] = points[i].rot.rot[j] / length;\n\n\t\t// Exponentiate scale\n\t\tfor(int j = 0; j < 3; j++)\n\t\t\tscales[k].scale[j] = exp(points[i].scale.scale[j]);\n\n\t\t// Activate alpha\n\t\topacities[k] = sigmoid(points[i].opacity);\n\n\t\tshs[k].shs[0] = points[i].shs.shs[0];\n\t\tshs[k].shs[1] = points[i].shs.shs[1];\n\t\tshs[k].shs[2] = points[i].shs.shs[2];\n\t\tfor (int j = 1; j < SH_N; j++)\n\t\t{\n\t\t\tshs[k].shs[j * 3 + 0] = points[i].shs.shs[(j - 1) + 3];\n\t\t\tshs[k].shs[j * 3 + 1] = points[i].shs.shs[(j - 1) + SH_N + 2];\n\t\t\tshs[k].shs[j * 3 + 2] = points[i].shs.shs[(j - 1) + 2 * SH_N + 1];\n\t\t}\n\t}\n\treturn count;\n}\n\nvoid savePly(const char* filename,\n\tconst std::vector<Pos>& pos,\n\tconst std::vector<SHs<3>>& shs,\n\tconst std::vector<float>& opacities,\n\tconst std::vector<Scale>& scales,\n\tconst std::vector<Rot>& rot,\n\tconst sibr::Vector3f& minn,\n\tconst sibr::Vector3f& maxx)\n{\n\t// Read all Gaussians at once (AoS)\n\tint count = 0;\n\tfor (int i = 0; i < pos.size(); i++)\n\t{\n\t\tif (pos[i].x() < minn.x() || pos[i].y() < minn.y() || pos[i].z() < minn.z() ||\n\t\t\tpos[i].x() > maxx.x() || pos[i].y() > maxx.y() || pos[i].z() > maxx.z())\n\t\t\tcontinue;\n\t\tcount++;\n\t}\n\tstd::vector<RichPoint<3>> points(count);\n\n\t// Output number of Gaussians contained\n\tSIBR_LOG << \"Saving \" << count << \" Gaussian splats\" << std::endl;\n\n\tstd::ofstream outfile(filename, std::ios_base::binary);\n\n\toutfile << \"ply\\nformat binary_little_endian 1.0\\nelement vertex \" << count << \"\\n\";\n\n\tstd::string props1[] = { \"x\", \"y\", \"z\", \"nx\", \"ny\", \"nz\", \"f_dc_0\", \"f_dc_1\", \"f_dc_2\"};\n\tstd::string props2[] = { \"opacity\", \"scale_0\", \"scale_1\", \"scale_2\", \"rot_0\", \"rot_1\", \"rot_2\", \"rot_3\" };\n\n\tfor (auto s : props1)\n\t\toutfile << \"property float \" << s << std::endl;\n\tfor (int i = 0; i < 45; i++)\n\t\toutfile << \"property float f_rest_\" << i << std::endl;\n\tfor (auto s : props2)\n\t\toutfile << \"property float \" << s << std::endl;\n\toutfile << \"end_header\" << std::endl;\n\n\tcount = 0;\n\tfor (int i = 0; i < pos.size(); i++)\n\t{\n\t\tif (pos[i].x() < minn.x() || pos[i].y() < minn.y() || pos[i].z() < minn.z() ||\n\t\t\tpos[i].x() > maxx.x() || pos[i].y() > maxx.y() || pos[i].z() > maxx.z())\n\t\t\tcontinue;\n\t\tpoints[count].pos = pos[i];\n\t\tpoints[count].rot = rot[i];\n\t\t// Exponentiate scale\n\t\tfor (int j = 0; j < 3; j++)\n\t\t\tpoints[count].scale.scale[j] = log(scales[i].scale[j]);\n\t\t// Activate alpha\n\t\tpoints[count].opacity = inverse_sigmoid(opacities[i]);\n\t\tpoints[count].shs.shs[0] = shs[i].shs[0];\n\t\tpoints[count].shs.shs[1] = shs[i].shs[1];\n\t\tpoints[count].shs.shs[2] = shs[i].shs[2];\n\t\tfor (int j = 1; j < 16; j++)\n\t\t{\n\t\t\tpoints[count].shs.shs[(j - 1) + 3] = shs[i].shs[j * 3 + 0];\n\t\t\tpoints[count].shs.shs[(j - 1) + 18] = shs[i].shs[j * 3 + 1];\n\t\t\tpoints[count].shs.shs[(j - 1) + 33] = shs[i].shs[j * 3 + 2];\n\t\t}\n\t\tcount++;\n\t}\n\toutfile.write((char*)points.data(), sizeof(RichPoint<3>) * points.size());\n}\n\nnamespace sibr\n{\n\t// A simple copy renderer class. Much like the original, but this one\n\t// reads from a buffer instead of a texture and blits the result to\n\t// a render target. \n\tclass BufferCopyRenderer\n\t{\n\n\tpublic:\n\n\t\tBufferCopyRenderer()\n\t\t{\n\t\t\t_shader.init(\"CopyShader\",\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"gaussian\") + \"/copy.vert\"),\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"gaussian\") + \"/copy.frag\"));\n\n\t\t\t_flip.init(_shader, \"flip\");\n\t\t\t_width.init(_shader, \"width\");\n\t\t\t_height.init(_shader, \"height\");\n\t\t}\n\n\t\tvoid process(uint bufferID, IRenderTarget& dst, int width, int height, bool disableTest = true)\n\t\t{\n\t\t\tif (disableTest)\n\t\t\t\tglDisable(GL_DEPTH_TEST);\n\t\t\telse\n\t\t\t\tglEnable(GL_DEPTH_TEST);\n\n\t\t\t_shader.begin();\n\t\t\t_flip.send();\n\t\t\t_width.send();\n\t\t\t_height.send();\n\n\t\t\tdst.clear();\n\t\t\tdst.bind();\n\n\t\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, bufferID);\n\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t\tdst.unbind();\n\t\t\t_shader.end();\n\t\t}\n\n\t\t/** \\return option to flip the texture when copying. */\n\t\tbool& flip() { return _flip.get(); }\n\t\tint& width() { return _width.get(); }\n\t\tint& height() { return _height.get(); }\n\n\tprivate:\n\n\t\tGLShader\t\t\t_shader; \n\t\tGLuniform<bool>\t\t_flip = false; ///< Flip the texture when copying.\n\t\tGLuniform<int>\t\t_width = 1000;\n\t\tGLuniform<int>\t\t_height = 800;\n\t};\n}\n\nstd::function<char* (size_t N)> resizeFunctional(void** ptr, size_t& S) {\n\tauto lambda = [ptr, &S](size_t N) {\n\t\tif (N > S)\n\t\t{\n\t\t\tif (*ptr)\n\t\t\t\tCUDA_SAFE_CALL(cudaFree(*ptr));\n\t\t\tCUDA_SAFE_CALL(cudaMalloc(ptr, 2 * N));\n\t\t\tS = 2 * N;\n\t\t}\n\t\treturn reinterpret_cast<char*>(*ptr);\n\t};\n\treturn lambda;\n}\n\nsibr::GaussianView::GaussianView(const sibr::BasicIBRScene::Ptr & ibrScene, uint render_w, uint render_h, const char* file, bool* messageRead, int sh_degree, bool white_bg, bool useInterop, int device) :\n\t_scene(ibrScene),\n\t_dontshow(messageRead),\n\t_sh_degree(sh_degree),\n\tsibr::ViewBase(render_w, render_h)\n{\n\tint num_devices;\n\tCUDA_SAFE_CALL_ALWAYS(cudaGetDeviceCount(&num_devices));\n\t_device = device;\n\tif (device >= num_devices)\n\t{\n\t\tif (num_devices == 0)\n\t\t\tSIBR_ERR << \"No CUDA devices detected!\";\n\t\telse\n\t\t\tSIBR_ERR << \"Provided device index exceeds number of available CUDA devices!\";\n\t}\n\tCUDA_SAFE_CALL_ALWAYS(cudaSetDevice(device));\n\tcudaDeviceProp prop;\n\tCUDA_SAFE_CALL_ALWAYS(cudaGetDeviceProperties(&prop, device));\n\tif (prop.major < 7)\n\t{\n\t\tSIBR_ERR << \"Sorry, need at least compute capability 7.0+!\";\n\t}\n\n\t_pointbasedrenderer.reset(new PointBasedRenderer());\n\t_copyRenderer = new BufferCopyRenderer();\n\t_copyRenderer->flip() = true;\n\t_copyRenderer->width() = render_w;\n\t_copyRenderer->height() = render_h;\n\n\tstd::vector<uint> imgs_ulr;\n\tconst auto & cams = ibrScene->cameras()->inputCameras();\n\tfor(size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif(cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n\n\t// Load the PLY data (AoS) to the GPU (SoA)\n\tstd::vector<Pos> pos;\n\tstd::vector<Rot> rot;\n\tstd::vector<Scale> scale;\n\tstd::vector<float> opacity;\n\tstd::vector<SHs<3>> shs;\n\tif (sh_degree == 0)\n\t{\n\t\tcount = loadPly<0>(file, pos, shs, opacity, scale, rot, _scenemin, _scenemax);\n\t}\n\telse if (sh_degree == 1)\n\t{\n\t\tcount = loadPly<1>(file, pos, shs, opacity, scale, rot, _scenemin, _scenemax);\n\t}\n\telse if (sh_degree == 2)\n\t{\n\t\tcount = loadPly<2>(file, pos, shs, opacity, scale, rot, _scenemin, _scenemax);\n\t}\n\telse if (sh_degree == 3)\n\t{\n\t\tcount = loadPly<3>(file, pos, shs, opacity, scale, rot, _scenemin, _scenemax);\n\t}\n\n\t_boxmin = _scenemin;\n\t_boxmax = _scenemax;\n\n\tint P = count;\n\n\t// Allocate and fill the GPU data\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&pos_cuda, sizeof(Pos) * P));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(pos_cuda, pos.data(), sizeof(Pos) * P, cudaMemcpyHostToDevice));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&rot_cuda, sizeof(Rot) * P));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(rot_cuda, rot.data(), sizeof(Rot) * P, cudaMemcpyHostToDevice));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&shs_cuda, sizeof(SHs<3>) * P));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(shs_cuda, shs.data(), sizeof(SHs<3>) * P, cudaMemcpyHostToDevice));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&opacity_cuda, sizeof(float) * P));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(opacity_cuda, opacity.data(), sizeof(float) * P, cudaMemcpyHostToDevice));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&scale_cuda, sizeof(Scale) * P));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(scale_cuda, scale.data(), sizeof(Scale) * P, cudaMemcpyHostToDevice));\n\n\t// Create space for view parameters\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&view_cuda, sizeof(sibr::Matrix4f)));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&proj_cuda, sizeof(sibr::Matrix4f)));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&cam_pos_cuda, 3 * sizeof(float)));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&background_cuda, 3 * sizeof(float)));\n\tCUDA_SAFE_CALL_ALWAYS(cudaMalloc((void**)&rect_cuda, 2 * P * sizeof(int)));\n\n\tfloat bg[3] = { white_bg ? 1.f : 0.f, white_bg ? 1.f : 0.f, white_bg ? 1.f : 0.f };\n\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(background_cuda, bg, 3 * sizeof(float), cudaMemcpyHostToDevice));\n\n\tgData = new GaussianData(P, \n\t\t(float*)pos.data(),\n\t\t(float*)rot.data(),\n\t\t(float*)scale.data(),\n\t\topacity.data(),\n\t\t(float*)shs.data());\n\n\t_gaussianRenderer = new GaussianSurfaceRenderer();\n\n\t// Create GL buffer ready for CUDA/GL interop\n\tglCreateBuffers(1, &imageBuffer);\n\tglNamedBufferStorage(imageBuffer, render_w * render_h * 3 * sizeof(float), nullptr, GL_DYNAMIC_STORAGE_BIT);\n\n\tif (useInterop)\n\t{\n\t\tif (cudaPeekAtLastError() != cudaSuccess)\n\t\t{\n\t\t\tSIBR_ERR << \"A CUDA error occurred in setup:\" << cudaGetErrorString(cudaGetLastError()) << \". Please rerun in Debug to find the exact line!\";\n\t\t}\n\t\tcudaGraphicsGLRegisterBuffer(&imageBufferCuda, imageBuffer, cudaGraphicsRegisterFlagsWriteDiscard);\n\t\tuseInterop &= (cudaGetLastError() == cudaSuccess);\n\t}\n\tif (!useInterop)\n\t{\n\t\tfallback_bytes.resize(render_w * render_h * 3 * sizeof(float));\n\t\tcudaMalloc(&fallbackBufferCuda, fallback_bytes.size());\n\t\t_interop_failed = true;\n\t}\n\n\tgeomBufferFunc = resizeFunctional(&geomPtr, allocdGeom);\n\tbinningBufferFunc = resizeFunctional(&binningPtr, allocdBinning);\n\timgBufferFunc = resizeFunctional(&imgPtr, allocdImg);\n}\n\nvoid sibr::GaussianView::setScene(const sibr::BasicIBRScene::Ptr & newScene)\n{\n\t_scene = newScene;\n\n\t// Tell the scene we are a priori using all active cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto & cams = newScene->cameras()->inputCameras();\n\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif (cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n}\n\nvoid sibr::GaussianView::onRenderIBR(sibr::IRenderTarget & dst, const sibr::Camera & eye)\n{\n\tif (currMode == \"Ellipsoids\")\n\t{\n\t\t_gaussianRenderer->process(count, *gData, eye, dst, 0.2f);\n\t}\n\telse if (currMode == \"Initial Points\")\n\t{\n\t\t_pointbasedrenderer->process(_scene->proxies()->proxy(), eye, dst);\n\t}\n\telse\n\t{\n\t\t// Convert view and projection to target coordinate system\n\t\tauto view_mat = eye.view();\n\t\tauto proj_mat = eye.viewproj();\n\t\tview_mat.row(1) *= -1;\n\t\tview_mat.row(2) *= -1;\n\t\tproj_mat.row(1) *= -1;\n\n\t\t// Compute additional view parameters\n\t\tfloat tan_fovy = tan(eye.fovy() * 0.5f);\n\t\tfloat tan_fovx = tan_fovy * eye.aspect();\n\n\t\t// Copy frame-dependent data to GPU\n\t\tCUDA_SAFE_CALL(cudaMemcpy(view_cuda, view_mat.data(), sizeof(sibr::Matrix4f), cudaMemcpyHostToDevice));\n\t\tCUDA_SAFE_CALL(cudaMemcpy(proj_cuda, proj_mat.data(), sizeof(sibr::Matrix4f), cudaMemcpyHostToDevice));\n\t\tCUDA_SAFE_CALL(cudaMemcpy(cam_pos_cuda, &eye.position(), sizeof(float) * 3, cudaMemcpyHostToDevice));\n\n\t\tfloat* image_cuda = nullptr;\n\t\tif (!_interop_failed)\n\t\t{\n\t\t\t// Map OpenGL buffer resource for use with CUDA\n\t\t\tsize_t bytes;\n\t\t\tCUDA_SAFE_CALL(cudaGraphicsMapResources(1, &imageBufferCuda));\n\t\t\tCUDA_SAFE_CALL(cudaGraphicsResourceGetMappedPointer((void**)&image_cuda, &bytes, imageBufferCuda));\n\t\t}\n\t\telse\n\t\t{\n\t\t\timage_cuda = fallbackBufferCuda;\n\t\t}\n\n\t\t// Rasterize\n\t\tint* rects = _fastCulling ? rect_cuda : nullptr;\n\t\tfloat* boxmin = _cropping ? (float*)&_boxmin : nullptr;\n\t\tfloat* boxmax = _cropping ? (float*)&_boxmax : nullptr;\n\t\tCudaRasterizer::Rasterizer::forward(\n\t\t\tgeomBufferFunc,\n\t\t\tbinningBufferFunc,\n\t\t\timgBufferFunc,\n\t\t\tcount, _sh_degree, 16,\n\t\t\tbackground_cuda,\n\t\t\t_resolution.x(), _resolution.y(),\n\t\t\tpos_cuda,\n\t\t\tshs_cuda,\n\t\t\tnullptr,\n\t\t\topacity_cuda,\n\t\t\tscale_cuda,\n\t\t\t_scalingModifier,\n\t\t\trot_cuda,\n\t\t\tnullptr,\n\t\t\tview_cuda,\n\t\t\tproj_cuda,\n\t\t\tcam_pos_cuda,\n\t\t\ttan_fovx,\n\t\t\ttan_fovy,\n\t\t\tfalse,\n\t\t\timage_cuda,\n\t\t\tnullptr,\n\t\t\trects,\n\t\t\tboxmin,\n\t\t\tboxmax\n\t\t);\n\n\t\tif (!_interop_failed)\n\t\t{\n\t\t\t// Unmap OpenGL resource for use with OpenGL\n\t\t\tCUDA_SAFE_CALL(cudaGraphicsUnmapResources(1, &imageBufferCuda));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCUDA_SAFE_CALL(cudaMemcpy(fallback_bytes.data(), fallbackBufferCuda, fallback_bytes.size(), cudaMemcpyDeviceToHost));\n\t\t\tglNamedBufferSubData(imageBuffer, 0, fallback_bytes.size(), fallback_bytes.data());\n\t\t}\n\t\t// Copy image contents to framebuffer\n\t\t_copyRenderer->process(imageBuffer, dst, _resolution.x(), _resolution.y());\n\t}\n\n\tif (cudaPeekAtLastError() != cudaSuccess)\n\t{\n\t\tSIBR_ERR << \"A CUDA error occurred during rendering:\" << cudaGetErrorString(cudaGetLastError()) << \". Please rerun in Debug to find the exact line!\";\n\t}\n}\n\nvoid sibr::GaussianView::onUpdate(Input & input)\n{\n}\n\nvoid sibr::GaussianView::onGUI()\n{\n\t// Generate and update UI elements\n\tconst std::string guiName = \"3D Gaussians\";\n\tif (ImGui::Begin(guiName.c_str())) \n\t{\n\t\tif (ImGui::BeginCombo(\"Render Mode\", currMode.c_str()))\n\t\t{\n\t\t\tif (ImGui::Selectable(\"Splats\"))\n\t\t\t\tcurrMode = \"Splats\";\n\t\t\tif (ImGui::Selectable(\"Initial Points\"))\n\t\t\t\tcurrMode = \"Initial Points\";\n\t\t\tif (ImGui::Selectable(\"Ellipsoids\"))\n\t\t\t\tcurrMode = \"Ellipsoids\";\n\t\t\tImGui::EndCombo();\n\t\t}\n\t}\n\tif (currMode == \"Splats\")\n\t{\n\t\tImGui::SliderFloat(\"Scaling Modifier\", &_scalingModifier, 0.001f, 1.0f);\n\t}\n\tImGui::Checkbox(\"Fast culling\", &_fastCulling);\n\n\tImGui::Checkbox(\"Crop Box\", &_cropping);\n\tif (_cropping)\n\t{\n\t\tImGui::SliderFloat(\"Box Min X\", &_boxmin.x(), _scenemin.x(), _scenemax.x());\n\t\tImGui::SliderFloat(\"Box Min Y\", &_boxmin.y(), _scenemin.y(), _scenemax.y());\n\t\tImGui::SliderFloat(\"Box Min Z\", &_boxmin.z(), _scenemin.z(), _scenemax.z());\n\t\tImGui::SliderFloat(\"Box Max X\", &_boxmax.x(), _scenemin.x(), _scenemax.x());\n\t\tImGui::SliderFloat(\"Box Max Y\", &_boxmax.y(), _scenemin.y(), _scenemax.y());\n\t\tImGui::SliderFloat(\"Box Max Z\", &_boxmax.z(), _scenemin.z(), _scenemax.z());\n\t\tImGui::InputText(\"File\", _buff, 512);\n\t\tif (ImGui::Button(\"Save\"))\n\t\t{\n\t\t\tstd::vector<Pos> pos(count);\n\t\t\tstd::vector<Rot> rot(count);\n\t\t\tstd::vector<float> opacity(count);\n\t\t\tstd::vector<SHs<3>> shs(count);\n\t\t\tstd::vector<Scale> scale(count);\n\t\t\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(pos.data(), pos_cuda, sizeof(Pos) * count, cudaMemcpyDeviceToHost));\n\t\t\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(rot.data(), rot_cuda, sizeof(Rot) * count, cudaMemcpyDeviceToHost));\n\t\t\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(opacity.data(), opacity_cuda, sizeof(float) * count, cudaMemcpyDeviceToHost));\n\t\t\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(shs.data(), shs_cuda, sizeof(SHs<3>) * count, cudaMemcpyDeviceToHost));\n\t\t\tCUDA_SAFE_CALL_ALWAYS(cudaMemcpy(scale.data(), scale_cuda, sizeof(Scale) * count, cudaMemcpyDeviceToHost));\n\t\t\tsavePly(_buff, pos, shs, opacity, scale, rot, _boxmin, _boxmax);\n\t\t}\n\t}\n\n\tImGui::End();\n\n\tif(!*_dontshow && !accepted && _interop_failed)\n\t\tImGui::OpenPopup(\"Error Using Interop\");\n\n\tif (!*_dontshow && !accepted && _interop_failed && ImGui::BeginPopupModal(\"Error Using Interop\", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {\n\t\tImGui::SetItemDefaultFocus();\n\t\tImGui::SetWindowFontScale(2.0f);\n\t\tImGui::Text(\"This application tries to use CUDA/OpenGL interop.\\n\"\\\n\t\t\t\" It did NOT work for your current configuration.\\n\"\\\n\t\t\t\" For highest performance, OpenGL and CUDA must run on the same\\n\"\\\n\t\t\t\" GPU on an OS that supports interop.You can try to pass a\\n\"\\\n\t\t\t\" non-zero index via --device on a multi-GPU system, and/or try\\n\" \\\n\t\t\t\" attaching the monitors to the main CUDA card.\\n\"\\\n\t\t\t\" On a laptop with one integrated and one dedicated GPU, you can try\\n\"\\\n\t\t\t\" to set the preferred GPU via your operating system.\\n\\n\"\\\n\t\t\t\" FALLING BACK TO SLOWER RENDERING WITH CPU ROUNDTRIP\\n\");\n\n\t\tImGui::Separator();\n\n\t\tif (ImGui::Button(\"  OK  \")) {\n\t\t\tImGui::CloseCurrentPopup();\n\t\t\taccepted = true;\n\t\t}\n\t\tImGui::SameLine();\n\t\tImGui::Checkbox(\"Don't show this message again\", _dontshow);\n\t\tImGui::EndPopup();\n\t}\n}\n\nsibr::GaussianView::~GaussianView()\n{\n\t// Cleanup\n\tcudaFree(pos_cuda);\n\tcudaFree(rot_cuda);\n\tcudaFree(scale_cuda);\n\tcudaFree(opacity_cuda);\n\tcudaFree(shs_cuda);\n\n\tcudaFree(view_cuda);\n\tcudaFree(proj_cuda);\n\tcudaFree(cam_pos_cuda);\n\tcudaFree(background_cuda);\n\tcudaFree(rect_cuda);\n\n\tif (!_interop_failed)\n\t{\n\t\tcudaGraphicsUnregisterResource(imageBufferCuda);\n\t}\n\telse\n\t{\n\t\tcudaFree(fallbackBufferCuda);\n\t}\n\tglDeleteBuffers(1, &imageBuffer);\n\n\tif (geomPtr)\n\t\tcudaFree(geomPtr);\n\tif (binningPtr)\n\t\tcudaFree(binningPtr);\n\tif (imgPtr)\n\t\tcudaFree(imgPtr);\n\n\tdelete _copyRenderer;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/GaussianView.hpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/renderer/RenderMaskHolder.hpp>\n# include <core/scene/BasicIBRScene.hpp>\n# include <core/system/SimpleTimer.hpp>\n# include <core/system/Config.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/view/ViewBase.hpp>\n# include <core/renderer/CopyRenderer.hpp>\n# include <core/renderer/PointBasedRenderer.hpp>\n# include <memory>\n# include <core/graphics/Texture.hpp>\n#include <cuda_runtime.h>\n#include <cuda_gl_interop.h>\n#include <functional>\n# include \"GaussianSurfaceRenderer.hpp\"\n\nnamespace CudaRasterizer\n{\n\tclass Rasterizer;\n}\n\nnamespace sibr { \n\n\tclass BufferCopyRenderer;\n\tclass BufferCopyRenderer2;\n\n\t/**\n\t * \\class RemotePointView\n\t * \\brief Wrap a ULR renderer with additional parameters and information.\n\t */\n\tclass SIBR_EXP_ULR_EXPORT GaussianView : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(GaussianView);\n\n\tpublic:\n\n\t\t/**\n\t\t * Constructor\n\t\t * \\param ibrScene The scene to use for rendering.\n\t\t * \\param render_w rendering width\n\t\t * \\param render_h rendering height\n\t\t */\n\t\tGaussianView(const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h, const char* file, bool* message_read, int sh_degree, bool white_bg = false, bool useInterop = true, int device = 0);\n\n\t\t/** Replace the current scene.\n\t\t *\\param newScene the new scene to render */\n\t\tvoid setScene(const sibr::BasicIBRScene::Ptr & newScene);\n\n\t\t/**\n\t\t * Perform rendering. Called by the view manager or rendering mode.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param eye The novel viewpoint.\n\t\t */\n\t\tvoid onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye) override;\n\n\t\t/**\n\t\t * Update inputs (do nothing).\n\t\t * \\param input The inputs state.\n\t\t */\n\t\tvoid onUpdate(Input& input) override;\n\n\t\t/**\n\t\t * Update the GUI.\n\t\t */\n\t\tvoid onGUI() override;\n\n\t\t/** \\return a reference to the scene */\n\t\tconst std::shared_ptr<sibr::BasicIBRScene> & getScene() const { return _scene; }\n\n\t\tvirtual ~GaussianView() override;\n\n\t\tbool* _dontshow;\n\n\tprotected:\n\n\t\tstd::string currMode = \"Splats\";\n\n\t\tbool _cropping = false;\n\t\tsibr::Vector3f _boxmin, _boxmax, _scenemin, _scenemax;\n\t\tchar _buff[512] = \"cropped.ply\";\n\n\t\tbool _fastCulling = true;\n\t\tint _device = 0;\n\t\tint _sh_degree = 3;\n\n\t\tint count;\n\t\tfloat* pos_cuda;\n\t\tfloat* rot_cuda;\n\t\tfloat* scale_cuda;\n\t\tfloat* opacity_cuda;\n\t\tfloat* shs_cuda;\n\t\tint* rect_cuda;\n\n\t\tGLuint imageBuffer;\n\t\tcudaGraphicsResource_t imageBufferCuda;\n\n\t\tsize_t allocdGeom = 0, allocdBinning = 0, allocdImg = 0;\n\t\tvoid* geomPtr = nullptr, * binningPtr = nullptr, * imgPtr = nullptr;\n\t\tstd::function<char* (size_t N)> geomBufferFunc, binningBufferFunc, imgBufferFunc;\n\n\t\tfloat* view_cuda;\n\t\tfloat* proj_cuda;\n\t\tfloat* cam_pos_cuda;\n\t\tfloat* background_cuda;\n\n\t\tfloat _scalingModifier = 1.0f;\n\t\tGaussianData* gData;\n\n\t\tbool _interop_failed = false;\n\t\tstd::vector<char> fallback_bytes;\n\t\tfloat* fallbackBufferCuda = nullptr;\n\t\tbool accepted = false;\n\n\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene; ///< The current scene.\n\t\tPointBasedRenderer::Ptr _pointbasedrenderer;\n\t\tBufferCopyRenderer* _copyRenderer;\n\t\tGaussianSurfaceRenderer* _gaussianRenderer;\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/shaders/copy.frag",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 450\n\nlayout(location = 0) out vec4 out_color;\n\nlayout(std430, binding = 0) buffer colorLayout\n{\n    float data[];\n} source;\n\nuniform bool flip = false;\nuniform int width = 1000;\nuniform int height = 800;\n\nin vec4 texcoord;\n\nvoid main(void)\n{\n\tint x = int(texcoord.x * width);\n\tint y;\n\t\n\tif(flip)\n\t\ty = height - 1 - int(texcoord.y * height);\n\telse\n\t\ty = int(texcoord.y * height);\n\t\n\tfloat r = source.data[0 * width * height + (y * width + x)];\n\tfloat g = source.data[1 * width * height + (y * width + x)];\n\tfloat b = source.data[2 * width * height + (y * width + x)];\n    vec4 color   = vec4(r, g, b, 1);\n    out_color    = color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/shaders/copy.vert",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/** \\file ibr.vp\n *\n * Vertex shader WITHOUT projection and modelview transformations.\n */\n\n#version 450\n\nlayout(location = 0) in vec4 in_vertex;   /**< Input vertex coordinates */\nlayout(location = 1) in vec4 in_texcoord; /**< Input texture coordinates */\nlayout(location = 2) in vec4 in_color;    /**< Input colour value */\n\nout vec4 texcoord;                        /**< Output texture coordinates */\nout vec4 color;                           /**< Output color value */\n\nvoid main(void) {\n  gl_Position = in_vertex;\n  texcoord    = in_texcoord;\n  color       = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/shaders/gaussian_surface.frag",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n#version 430\n\nuniform mat4 MVP;\nuniform float alpha_limit;\nuniform int stage;\nuniform vec3 rayOrigin;\n\nin vec3 worldPos;\nin vec3 ellipsoidCenter;\nin vec3 ellipsoidScale;\nin mat3 ellipsoidRotation;\nin vec3 colorVert;\nin float alphaVert;\nin flat int boxID;\n\nlayout (location = 0) out vec4 out_color;\nlayout (location = 1) out uint out_id;\n\nvec3 closestEllipsoidIntersection(vec3 rayDirection, out vec3 normal) {\n  // Convert ray to ellipsoid space\n  dvec3 localRayOrigin = (rayOrigin - ellipsoidCenter) * ellipsoidRotation;\n  dvec3 localRayDirection = normalize(rayDirection * ellipsoidRotation);\n\n  dvec3 oneover = double(1) / dvec3(ellipsoidScale);\n  \n  // Compute coefficients of quadratic equation\n  double a = dot(localRayDirection * oneover, localRayDirection * oneover);\n  double b = 2.0 * dot(localRayDirection * oneover, localRayOrigin * oneover);\n  double c = dot(localRayOrigin * oneover, localRayOrigin * oneover) - 1.0;\n  \n  // Compute discriminant\n  double discriminant = b * b - 4.0 * a * c;\n  \n  // If discriminant is negative, there is no intersection\n  if (discriminant < 0.0) {\n    return vec3(0.0);\n  }\n  \n  // Compute two possible solutions for t\n  float t1 = float((-b - sqrt(discriminant)) / (2.0 * a));\n  float t2 = float((-b + sqrt(discriminant)) / (2.0 * a));\n  \n  // Take the smaller positive solution as the closest intersection\n  float t = min(t1, t2);\n  \n  // Compute intersection point in ellipsoid space\n  vec3 localIntersection = vec3(localRayOrigin + t * localRayDirection);\n\n  // Compute normal vector in ellipsoid space\n  vec3 localNormal = normalize(localIntersection / ellipsoidScale);\n  \n  // Convert normal vector to world space\n  normal = normalize(ellipsoidRotation * localNormal);\n  \n  // Convert intersection point back to world space\n  vec3 intersection = ellipsoidRotation * localIntersection + ellipsoidCenter;\n  \n  return intersection;\n}\n\nvoid main(void) {\n\tvec3 dir = normalize(worldPos - rayOrigin);\n\n\tvec3 normal;\n\tvec3 intersection = closestEllipsoidIntersection(dir, normal);\n\tfloat align = max(0.4, dot(-dir, normal));\n\t\n\tout_color = vec4(1, 0, 0, 1);\n\t\n\tif(intersection == vec3(0))\n\t\tdiscard;\n\n\tvec4 newPos = MVP * vec4(intersection, 1);\n\tnewPos /= newPos.w;\n\n\tgl_FragDepth = newPos.z;\n\n\tfloat a = stage == 0 ? 1.0 : 0.05f;\n\n\tout_color = vec4(align * colorVert, a);\n\tout_id = boxID;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/gaussianviewer/renderer/shaders/gaussian_surface.vert",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 430\n\nuniform mat4 MVP;\nuniform float alpha_limit;\nuniform int stage;\n\nlayout (std430, binding = 0) buffer BoxCenters {\n    float centers[];\n};\nlayout (std430, binding = 1) buffer Rotations {\n    vec4 rots[];\n};\nlayout (std430, binding = 2) buffer Scales {\n    float scales[];\n};\nlayout (std430, binding = 3) buffer Alphas {\n    float alphas[];\n};\nlayout (std430, binding = 4) buffer Colors {\n    float colors[];\n};\n\nmat3 quatToMat3(vec4 q) {\n  float qx = q.y;\n  float qy = q.z;\n  float qz = q.w;\n  float qw = q.x;\n\n  float qxx = qx * qx;\n  float qyy = qy * qy;\n  float qzz = qz * qz;\n  float qxz = qx * qz;\n  float qxy = qx * qy;\n  float qyw = qy * qw;\n  float qzw = qz * qw;\n  float qyz = qy * qz;\n  float qxw = qx * qw;\n\n  return mat3(\n    vec3(1.0 - 2.0 * (qyy + qzz), 2.0 * (qxy - qzw), 2.0 * (qxz + qyw)),\n    vec3(2.0 * (qxy + qzw), 1.0 - 2.0 * (qxx + qzz), 2.0 * (qyz - qxw)),\n    vec3(2.0 * (qxz - qyw), 2.0 * (qyz + qxw), 1.0 - 2.0 * (qxx + qyy))\n  );\n}\n\nconst vec3 boxVertices[8] = vec3[8](\n    vec3(-1, -1, -1),\n    vec3(-1, -1,  1),\n    vec3(-1,  1, -1),\n    vec3(-1,  1,  1),\n    vec3( 1, -1, -1),\n    vec3( 1, -1,  1),\n    vec3( 1,  1, -1),\n    vec3( 1,  1,  1)\n);\n\nconst int boxIndices[36] = int[36](\n    0, 1, 2, 1, 3, 2,\n    4, 6, 5, 5, 6, 7,\n    0, 2, 4, 4, 2, 6,\n    1, 5, 3, 5, 7, 3,\n    0, 4, 1, 4, 5, 1,\n    2, 3, 6, 3, 7, 6\n);\n\nout vec3 worldPos;\nout vec3 ellipsoidCenter;\nout vec3 ellipsoidScale;\nout mat3 ellipsoidRotation;\nout vec3 colorVert;\nout float alphaVert;\nout flat int boxID;\n\nvoid main() {\n\tboxID = gl_InstanceID;\n    ellipsoidCenter = vec3(centers[3 * boxID + 0], centers[3 * boxID + 1], centers[3 * boxID + 2]);\n    float a = alphas[boxID];\n\talphaVert = a;\n\tellipsoidScale = vec3(scales[3 * boxID + 0], scales[3 * boxID + 1], scales[3 * boxID + 2]);\n\tellipsoidScale = 2 * ellipsoidScale;\n\n\tvec4 q = rots[boxID];\n\tellipsoidRotation = transpose(quatToMat3(q));\n\n    int vertexIndex = boxIndices[gl_VertexID];\n    worldPos = ellipsoidRotation * (ellipsoidScale * boxVertices[vertexIndex]);\n    worldPos += ellipsoidCenter;\n\n\tfloat r = colors[boxID * 48 + 0] * 0.2 + 0.5;\n\tfloat g = colors[boxID * 48 + 1] * 0.2 + 0.5;\n\tfloat b = colors[boxID * 48 + 2] * 0.2 + 0.5;\n\n\tcolorVert = vec3(r, g, b);\n\t\n\tif((stage == 0 && a < alpha_limit) || (stage == 1 && a >= alpha_limit))\n\t \tgl_Position = vec4(0,0,0,0);\n\telse\n    \tgl_Position = MVP * vec4(worldPos, 1);\n}"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\nproject(sibr_gaussian_hierarchy_all)\n\nadd_subdirectory(apps)\nadd_subdirectory(renderer)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/hierarchyviewer\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/apps/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_gaussian_hierarchy_apps)\n\nadd_subdirectory(gaussianHierarchyViewer/)"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/apps/gaussianHierarchyViewer/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_gaussianHierarchyViewer_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\tsibr_hierarchyviewer\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/hierarchyviewer/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"hierarchyviewer\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/apps/gaussianHierarchyViewer/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <core/system/String.hpp>\n#include \"projects/hierarchyviewer/renderer/HierarchyView.hpp\" \n\n#include <core/renderer/DepthRenderer.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n\n// for http server\n#include <thread>\n#include <core/view/httplib.h>\n#include <core/view/InteractiveCameraHandler.hpp>\n#include <core/assets/CameraRecorder.hpp>\n\n#define PROGRAM_NAME \"sibr_3Dhierarchy\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\"Usage: \" PROGRAM_NAME \" -path <dataset-path>\"    \t                                \"\\n\"\n;\n\ncv::Mat renderImage(const std::string& cameraParams, const std::string& imageParams) {\n    // 解析并使用cameraParams和imageParams，进行相应的渲染\n    // 这里只是示例，你可以调用现有的3D渲染函数\n    // TODO: 实现实际渲染逻辑\n\n    // 示例：创建一个简单的RGB图像\n    cv::Mat img(500, 500, CV_8UC3, cv::Scalar(255, 0, 0));  // 创建一个红色图像\n    cv::putText(img, \"Rendering based on camera & image params\", cv::Point(50, 250),\n                cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2);\n    return img;\n}\n\nint main(int ac, char** av) {\n\n\t// Parse Command-line Args\n\tCommandLineArgs::parseMainArgs(ac, av);\n\tGaussianAppArgs myArgs;\n\tmyArgs.displayHelpIfRequired();\n\n\t//const bool doVSync = !myArgs.vsync;\n\tmyArgs.vsync = false;\n\t// rendering size\n\tuint rendering_width = myArgs.rendering_size.get()[0];\n\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\n\t// window size\n\tuint win_width = rendering_width; // myArgs.win_width;\n\tuint win_height = rendering_height; // myArgs.win_height;\n\n\tconst char* toload = myArgs.modelPath.get().c_str();\n\tconst char* scaffold = myArgs.scaffoldPath.get().c_str();\n\n\n\t// std::cout << \"Model path      : \" << toload   << std::endl;\n\t// std::cout << \"Scaffold path   : \" << scaffold << std::endl;\n\n\t// // 如果还想看渲染/窗口大小等：\n\t// auto rend_size = myArgs.rendering_size.get();\n\t// std::cout << \"Rendering size  : \" << rend_size[0] << \" x \" << rend_size[1] << std::endl;\n\t// std::cout << \"VSync           : \" << (myArgs.vsync ? \"ON\" : \"OFF\") << std::endl;\n\n\n\n\t// Window setup\n\tsibr::Window\t\twindow(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/hierarchy/\" + PROGRAM_NAME + \".ini\");\n\n\tsibr::BasicIBRScene::SceneOptions opts;\n\topts.cameras = true;\n\topts.images = false;\n\topts.mesh = true;\n\topts.renderTargets = false;\n\topts.texture = false;\n\t// std::cout << \" myArgs : \" << myArgs << std::endl;\n\tBasicIBRScene::Ptr\t\tscene(new BasicIBRScene(myArgs, opts));\n\n\t// Setup the scene: load the proxy, create the texture arrays.\n\tconst uint flags = SIBR_GPU_LINEAR_SAMPLING | SIBR_FLIP_TEXTURE;\n\n\t// Fix rendering aspect ratio if user provided rendering size\n\tuint scene_width = scene->cameras()->inputCameras()[0]->w();\n\tuint scene_height = scene->cameras()->inputCameras()[0]->h();\n\tfloat scene_aspect_ratio = scene_width * 1.0f / scene_height;\n\tfloat rendering_aspect_ratio = rendering_width * 1.0f / rendering_height;\n\n\tif ((rendering_width > 0) && !myArgs.force_aspect_ratio ) {\n\t\tif (abs(scene_aspect_ratio - rendering_aspect_ratio) > 0.001f) {\n\t\t\tif (scene_width > scene_height) {\n\t\t\t\trendering_height = rendering_width / scene_aspect_ratio;\n\t\t\t}\n\t\t\telse {\n\t\t\t\trendering_width = rendering_height * scene_aspect_ratio;\n\t\t\t}\n\t\t}\n\t}\n\n\t\n\t// check rendering size\n\trendering_width = (rendering_width <= 0) ? scene->cameras()->inputCameras()[0]->w() : rendering_width;\n\trendering_height = (rendering_height <= 0) ? scene->cameras()->inputCameras()[0]->h() : rendering_height;\n\tVector2u usedResolution(rendering_width, rendering_height);\n\tstd::cerr << \" USED RES \" << usedResolution << \" scene w h \" << scene_width << \" : \" << scene_height <<  \n\t\t \" NAME \" << scene->cameras()->inputCameras()[0]->name() << std::endl;\n\n\n\n\tconst unsigned int sceneResWidth = usedResolution.x();\n\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\tHierarchyView::Ptr\tpointBasedView(new HierarchyView(scene, sceneResWidth, sceneResHeight, toload, scaffold, myArgs.budget.get()));\n\n\t// Raycaster.\n\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\traycaster->init();\n\traycaster->addMesh(scene->proxies()->proxy());\n\n\t// Camera handler for main view.\n\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster, { -1.0f,-1.0f });\n\n\t// Add views to mvm.\n\tMultiViewManager        multiViewManager(window, false);\n\n\tif (myArgs.rendering_mode == 1) \n\t\tmultiViewManager.renderingMode(IRenderingMode::Ptr(new StereoAnaglyphRdrMode()));\n\t\n\tmultiViewManager.addIBRSubView(\"Point view\", pointBasedView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\tmultiViewManager.addCameraForView(\"Point view\", generalCamera);\n\n\t// Top view\n\tconst std::shared_ptr<sibr::SceneDebugView> topView(new sibr::SceneDebugView(scene, generalCamera, myArgs,  myArgs.imagesPath.get().c_str()));\n\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\ttopView->active(false);\n\n\tCHECK_GL_ERROR;\n\n\t// save images\n\tgeneralCamera->getCameraRecorder().setViewPath(pointBasedView, myArgs.dataset_path.get());\n\tif (myArgs.pathFile.get() !=  \"\" ) {\n\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"Point view\"), \"\");\n\t\tif( !myArgs.noExit )\n\t\t\texit(0);\n\t}\n\n\n\t// for http server\n    httplib::Server svr;\n    svr.Get(\"/\", [](const httplib::Request&, httplib::Response& res) {\n        res.set_content(\"Hello, this is a 3D viewer HTTP server!\", \"text/plain\");\n    });\n    svr.Post(\"/render\", [&](const httplib::Request& req, httplib::Response& res) {\n        // 检查是否提供了camera和image参数\n        if (!req.has_param(\"camera\") || !req.has_param(\"image\")) {\n            res.status = 400;  // 错误请求\n            res.set_content(\"Missing camera or image parameter\", \"text/plain\");\n            return;\n        }\n\n        // 获取camera和image参数\n        std::string cameraParams = req.get_param_value(\"camera\");\n        std::string imageParams = req.get_param_value(\"image\");\n\t\tstd::string pathParams = req.get_param_value(\"path\");\n\t\t// std::cout << \"cameraParams: \" << cameraParams << std::endl;\n\t\tgeneralCamera->getCameraRecorder().loadColmapFromHttp(cameraParams, imageParams, pathParams);\n\t\tstd::cout << \" Image saved to ./ImageOutput successfully!\" << std::endl;\n        // // 调用渲染函数生成图像\n        // cv::Mat renderedImage = renderImage(cameraParams, imageParams);\n\n        // // 将图像保存为PNG格式并发送\n        // std::vector<uchar> buf;\n        // cv::imencode(\".png\", renderedImage, buf);\n\n        // res.set_content(reinterpret_cast<const char*>(buf.data()), buf.size(), \"image/png\");\n\t\tres.set_content(\"Image has been saved to ./ImageOutput successfully!\", \"text/plain\");\n    });\n\n    std::thread server_thread([&]() {\n        std::cout << \"Starting HTTP server on port 18080...\" << std::endl;\n        svr.listen(\"0.0.0.0\", 18080); \n\t\t\n    });\n\n\n\t// Main looooooop.\n\twhile (window.isOpened()) {\n\n\t\tsibr::Input::poll();\n\t\twindow.makeContextCurrent();\n\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\n\t\t\twindow.close();\n\t\t}\n\n\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\tmultiViewManager.onRender(window);\n\n\t\twindow.swapBuffer();\n\t\tCHECK_GL_ERROR;\n\t}\n\n\t// for http server\n    svr.stop();\n    server_thread.join();\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/json.hpp",
    "content": "/*\n    __ _____ _____ _____\n __|  |   __|     |   | |  JSON for Modern C++\n|  |  |__   |  |  | | | |  version 3.10.4\n|_____|_____|_____|_|___|  https://github.com/nlohmann/json\n\nLicensed under the MIT License <http://opensource.org/licenses/MIT>.\nSPDX-License-Identifier: MIT\nCopyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.\n\nPermission is hereby  granted, free of charge, to any  person obtaining a copy\nof this software and associated  documentation files (the \"Software\"), to deal\nin the Software  without restriction, including without  limitation the rights\nto  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell\ncopies  of  the Software,  and  to  permit persons  to  whom  the Software  is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE  IS PROVIDED \"AS  IS\", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR\nIMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,\nFITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE\nAUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER\nLIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3\n#define NLOHMANN_JSON_VERSION_MINOR 10\n#define NLOHMANN_JSON_VERSION_PATCH 4\n\n#include <algorithm> // all_of, find, for_each\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#ifndef JSON_NO_IO\n    #include <iosfwd> // istream, ostream\n#endif  // JSON_NO_IO\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n// #include <nlohmann/adl_serializer.hpp>\n\n\n#include <type_traits>\n#include <utility>\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n\n#include <algorithm> // transform\n#include <array> // array\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n#include <vector> // vector\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n#include <string> // string\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    binary,           ///< binary array (ordered collection of bytes)\n    discarded         ///< discarded by the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string < binary\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n- binary is represented as a b\"\" string in python and directly comparable to a\n  string; however, making a binary array directly comparable with a string would\n  be surprising behavior in a JSON file.\n\n@since version 1.0.0\n*/\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    static constexpr std::array<std::uint8_t, 9> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,\n            6 /* binary */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n\n#include <string>\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n#include <utility> // declval, pair\n// #include <nlohmann/thirdparty/hedley/hedley.hpp>\n\n\n/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>.\n * SPDX-License-Identifier: CC0-1.0\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 15\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_CONCAT3_EX)\n    #undef JSON_HEDLEY_CONCAT3_EX\n#endif\n#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c\n\n#if defined(JSON_HEDLEY_CONCAT3)\n    #undef JSON_HEDLEY_CONCAT3\n#endif\n#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(JSON_HEDLEY_MSVC_VERSION)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)\n    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if \\\n    defined(__TI_COMPILER_VERSION__) && \\\n    ( \\\n      defined(__TMS470__) || defined(__TI_ARM__) || \\\n      defined(__MSP430__) || \\\n      defined(__TMS320C2000__) \\\n    )\n#if (__TI_COMPILER_VERSION__ >= 16000000)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)\n    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #undef JSON_HEDLEY_TI_CL430_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)\n    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))\n    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)\n    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)\n    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION\n#endif\n#if defined(__LCC__) && defined(__LCC_MINOR__)\n    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_CRAY_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \\\n    !defined(__COMPCERT__) && \\\n    !defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if \\\n  defined(__has_attribute) && \\\n  ( \\\n    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \\\n  )\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_IAR_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#    if JSON_HEDLEY_HAS_WARNING(\"-Wc++17-extensions\")\n#      if JSON_HEDLEY_HAS_WARNING(\"-Wc++1z-extensions\")\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++1z-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      else\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      endif\n#    else\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    endif\n#  endif\n#endif\n#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wold-style-cast\")\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wold-style-cast\\\"\") \\\n    ((T) (expr)) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"diag_suppress=Pe137\") \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))\n#  endif\n#else\n#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1216,1444,1445\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097,1098\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress=Pe1097\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunused-function\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"clang diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"GCC diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"diag_suppress 3142\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif \\\n    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))\n#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif defined(JSON_HEDLEY_ASSUME)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n#if !defined(JSON_HEDLEY_ASSUME)\n    #if defined(JSON_HEDLEY_UNREACHABLE)\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)\n    #endif\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #if  \\\n        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))\n    #else\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n    #endif\n#else\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))\n#endif\n#if \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))\n#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )\n#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )\n#elif \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \\\n  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n#  define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \\\n    )\n#  define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n#  define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \\\n    )\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define JSON_HEDLEY_PRIVATE\n#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n#  if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    ( \\\n      defined(__TI_EABI__) && \\\n      ( \\\n        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \\\n      ) \\\n    ) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n#  else\n#    define JSON_HEDLEY_PRIVATE\n#    define JSON_HEDLEY_PUBLIC\n#  endif\n#  define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       ( \\\n          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \\\n          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \\\n          !defined(JSON_HEDLEY_PGI_VERSION) && \\\n          !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \\\n       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \\\n       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING(\"-Wbitfield-enum-conversion\"))\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#else\n    #define JSON_HEDLEY_FLAGS\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if \\\n    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n\n#include <type_traits>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate<typename ...Ts> using void_t = typename make_void<Ts...>::type;\n} // namespace detail\n}  // namespace nlohmann\n\n\n// https://en.cppreference.com/w/cpp/experimental/is_detected\nnamespace nlohmann\n{\nnamespace detail\n{\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate<class Default,\n         class AlwaysVoid,\n         template<class...> class Op,\n         class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate<class Default, template<class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate<template<class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate<template<class...> class Op, class... Args>\nstruct is_detected_lazy : is_detected<Op, Args...> { };\n\ntemplate<template<class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate<class Expected, template<class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate<class To, template<class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n}  // namespace detail\n}  // namespace nlohmann\n\n\n// This file contains all internal macro definitions\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// C++ language standard detection\n// if the user manually specified the used c++ version this is skipped\n#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)\n    #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)\n        #define JSON_HAS_CPP_20\n        #define JSON_HAS_CPP_17\n        #define JSON_HAS_CPP_14\n    #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n        #define JSON_HAS_CPP_17\n        #define JSON_HAS_CPP_14\n    #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n        #define JSON_HAS_CPP_14\n    #endif\n    // the cpp 11 flag is always specified because it is the minimal required version\n    #define JSON_HAS_CPP_11\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wdocumentation\"\n    #pragma clang diagnostic ignored \"-Wdocumentation-unknown-command\"\n#endif\n\n// allow to disable exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #include <cstdlib>\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n// allow to override assert\n#if !defined(JSON_ASSERT)\n    #include <cassert> // assert\n    #define JSON_ASSERT(x) assert(x)\n#endif\n\n// allow to access some private functions (needed by the test suite)\n#if defined(JSON_TESTS_PRIVATE)\n    #define JSON_PRIVATE_UNLESS_TESTED public\n#else\n    #define JSON_PRIVATE_UNLESS_TESTED private\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \\\n    template<typename BasicJsonType>                                                            \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \\\n        {                                                                                       \\\n            return ej_pair.first == e;                                                          \\\n        });                                                                                     \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \\\n    }                                                                                           \\\n    template<typename BasicJsonType>                                                            \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                       \\\n            return ej_pair.second == j;                                                         \\\n        });                                                                                     \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer,     \\\n             class BinaryType>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer, BinaryType>\n\n// Macros to simplify conversion from/to types\n\n#define NLOHMANN_JSON_EXPAND( x ) x\n#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME\n#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\\n        NLOHMANN_JSON_PASTE64, \\\n        NLOHMANN_JSON_PASTE63, \\\n        NLOHMANN_JSON_PASTE62, \\\n        NLOHMANN_JSON_PASTE61, \\\n        NLOHMANN_JSON_PASTE60, \\\n        NLOHMANN_JSON_PASTE59, \\\n        NLOHMANN_JSON_PASTE58, \\\n        NLOHMANN_JSON_PASTE57, \\\n        NLOHMANN_JSON_PASTE56, \\\n        NLOHMANN_JSON_PASTE55, \\\n        NLOHMANN_JSON_PASTE54, \\\n        NLOHMANN_JSON_PASTE53, \\\n        NLOHMANN_JSON_PASTE52, \\\n        NLOHMANN_JSON_PASTE51, \\\n        NLOHMANN_JSON_PASTE50, \\\n        NLOHMANN_JSON_PASTE49, \\\n        NLOHMANN_JSON_PASTE48, \\\n        NLOHMANN_JSON_PASTE47, \\\n        NLOHMANN_JSON_PASTE46, \\\n        NLOHMANN_JSON_PASTE45, \\\n        NLOHMANN_JSON_PASTE44, \\\n        NLOHMANN_JSON_PASTE43, \\\n        NLOHMANN_JSON_PASTE42, \\\n        NLOHMANN_JSON_PASTE41, \\\n        NLOHMANN_JSON_PASTE40, \\\n        NLOHMANN_JSON_PASTE39, \\\n        NLOHMANN_JSON_PASTE38, \\\n        NLOHMANN_JSON_PASTE37, \\\n        NLOHMANN_JSON_PASTE36, \\\n        NLOHMANN_JSON_PASTE35, \\\n        NLOHMANN_JSON_PASTE34, \\\n        NLOHMANN_JSON_PASTE33, \\\n        NLOHMANN_JSON_PASTE32, \\\n        NLOHMANN_JSON_PASTE31, \\\n        NLOHMANN_JSON_PASTE30, \\\n        NLOHMANN_JSON_PASTE29, \\\n        NLOHMANN_JSON_PASTE28, \\\n        NLOHMANN_JSON_PASTE27, \\\n        NLOHMANN_JSON_PASTE26, \\\n        NLOHMANN_JSON_PASTE25, \\\n        NLOHMANN_JSON_PASTE24, \\\n        NLOHMANN_JSON_PASTE23, \\\n        NLOHMANN_JSON_PASTE22, \\\n        NLOHMANN_JSON_PASTE21, \\\n        NLOHMANN_JSON_PASTE20, \\\n        NLOHMANN_JSON_PASTE19, \\\n        NLOHMANN_JSON_PASTE18, \\\n        NLOHMANN_JSON_PASTE17, \\\n        NLOHMANN_JSON_PASTE16, \\\n        NLOHMANN_JSON_PASTE15, \\\n        NLOHMANN_JSON_PASTE14, \\\n        NLOHMANN_JSON_PASTE13, \\\n        NLOHMANN_JSON_PASTE12, \\\n        NLOHMANN_JSON_PASTE11, \\\n        NLOHMANN_JSON_PASTE10, \\\n        NLOHMANN_JSON_PASTE9, \\\n        NLOHMANN_JSON_PASTE8, \\\n        NLOHMANN_JSON_PASTE7, \\\n        NLOHMANN_JSON_PASTE6, \\\n        NLOHMANN_JSON_PASTE5, \\\n        NLOHMANN_JSON_PASTE4, \\\n        NLOHMANN_JSON_PASTE3, \\\n        NLOHMANN_JSON_PASTE2, \\\n        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))\n#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)\n#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)\n#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)\n#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)\n#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)\n#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)\n#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)\n#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)\n#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)\n#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)\n#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)\n#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)\n#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)\n#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)\n#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)\n#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)\n#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)\n#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)\n#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)\n#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)\n#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)\n#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)\n#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)\n#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)\n#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)\n#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)\n#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)\n#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)\n#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)\n#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)\n#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)\n#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)\n#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)\n#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)\n#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)\n#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)\n#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)\n#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)\n#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)\n#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)\n#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)\n#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)\n#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)\n#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)\n#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)\n#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)\n#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)\n#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)\n#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)\n#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)\n#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)\n#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)\n#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)\n#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)\n#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)\n#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)\n#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)\n#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)\n#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)\n#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)\n#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)\n#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)\n#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)\n\n#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;\n#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \\\n    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \\\n    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n\n// inspired from https://stackoverflow.com/a/26745591\n// allows to call any std function as if (e.g. with begin):\n// using std::begin; begin(x);\n//\n// it allows using the detected idiom to retrieve the return type\n// of such an expression\n#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name)                                 \\\n    namespace detail {                                                            \\\n    using std::std_name;                                                          \\\n    \\\n    template<typename... T>                                                       \\\n    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \\\n    }                                                                             \\\n    \\\n    namespace detail2 {                                                           \\\n    struct std_name##_tag                                                         \\\n    {                                                                             \\\n    };                                                                            \\\n    \\\n    template<typename... T>                                                       \\\n    std_name##_tag std_name(T&&...);                                              \\\n    \\\n    template<typename... T>                                                       \\\n    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \\\n    \\\n    template<typename... T>                                                       \\\n    struct would_call_std_##std_name                                              \\\n    {                                                                             \\\n        static constexpr auto const value = ::nlohmann::detail::                  \\\n                                            is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \\\n    };                                                                            \\\n    } /* namespace detail2 */ \\\n    \\\n    template<typename... T>                                                       \\\n    struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...>   \\\n    {                                                                             \\\n    }\n\n#ifndef JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_USE_IMPLICIT_CONVERSIONS 1\n#endif\n\n#if JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_EXPLICIT\n#else\n    #define JSON_EXPLICIT explicit\n#endif\n\n#ifndef JSON_DIAGNOSTICS\n    #define JSON_DIAGNOSTICS 0\n#endif\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief replace all occurrences of a substring by another string\n\n@param[in,out] s  the string to manipulate; changed so that all\n               occurrences of @a f are replaced with @a t\n@param[in]     f  the substring to replace with @a t\n@param[in]     t  the string to replace @a f\n\n@pre The search string @a f must not be empty. **This precondition is\nenforced with an assertion.**\n\n@since version 2.0.0\n*/\ninline void replace_substring(std::string& s, const std::string& f,\n                              const std::string& t)\n{\n    JSON_ASSERT(!f.empty());\n    for (auto pos = s.find(f);                // find first occurrence of f\n            pos != std::string::npos;         // make sure f was found\n            s.replace(pos, f.size(), t),      // replace with t, and\n            pos = s.find(f, pos + t.size()))  // find next occurrence of f\n    {}\n}\n\n/*!\n * @brief string escaping as described in RFC 6901 (Sect. 4)\n * @param[in] s string to escape\n * @return    escaped string\n *\n * Note the order of escaping \"~\" to \"~0\" and \"/\" to \"~1\" is important.\n */\ninline std::string escape(std::string s)\n{\n    replace_substring(s, \"~\", \"~0\");\n    replace_substring(s, \"/\", \"~1\");\n    return s;\n}\n\n/*!\n * @brief string unescaping as described in RFC 6901 (Sect. 4)\n * @param[in] s string to unescape\n * @return    unescaped string\n *\n * Note the order of escaping \"~1\" to \"/\" and \"~0\" to \"~\" is important.\n */\nstatic void unescape(std::string& s)\n{\n    replace_substring(s, \"~1\", \"/\");\n    replace_substring(s, \"~0\", \"~\");\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n\n#include <cstddef> // size_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////////\n// exceptions //\n////////////////\n\n/*!\n@brief general exception of the @ref basic_json class\n\nThis class is an extension of `std::exception` objects with a member @a id for\nexception ids. It is used as the base class for all exceptions thrown by the\n@ref basic_json class. This class can hence be used as \"wildcard\" to catch\nexceptions.\n\nSubclasses:\n- @ref parse_error for exceptions indicating a parse error\n- @ref invalid_iterator for exceptions indicating errors with iterators\n- @ref type_error for exceptions indicating executing a member function with\n                  a wrong type\n- @ref out_of_range for exceptions indicating access out of the defined range\n- @ref other_error for exceptions indicating other library errors\n\n@internal\n@note To have nothrow-copy-constructible exceptions, we internally use\n      `std::runtime_error` which can cope with arbitrary-length error messages.\n      Intermediate strings are built with static functions and then passed to\n      the actual constructor.\n@endinternal\n\n@liveexample{The following code shows how arbitrary library exceptions can be\ncaught.,exception}\n\n@since version 3.0.0\n*/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return \"[json.exception.\" + ename + \".\" + std::to_string(id_) + \"] \";\n    }\n\n    template<typename BasicJsonType>\n    static std::string diagnostics(const BasicJsonType& leaf_element)\n    {\n#if JSON_DIAGNOSTICS\n        std::vector<std::string> tokens;\n        for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent)\n        {\n            switch (current->m_parent->type())\n            {\n                case value_t::array:\n                {\n                    for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)\n                    {\n                        if (&current->m_parent->m_value.array->operator[](i) == current)\n                        {\n                            tokens.emplace_back(std::to_string(i));\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::object:\n                {\n                    for (const auto& element : *current->m_parent->m_value.object)\n                    {\n                        if (&element.second == current)\n                        {\n                            tokens.emplace_back(element.first.c_str());\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::null: // LCOV_EXCL_LINE\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:   // LCOV_EXCL_LINE\n                    break; // LCOV_EXCL_LINE\n            }\n        }\n\n        if (tokens.empty())\n        {\n            return \"\";\n        }\n\n        return \"(\" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},\n                                     [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + detail::escape(b);\n        }) + \") \";\n#else\n        static_cast<void>(leaf_element);\n        return \"\";\n#endif\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/*!\n@brief exception indicating a parse error\n\nThis exception is thrown by the library when a parse error occurs. Parse errors\ncan occur during the deserialization of JSON text, CBOR, MessagePack, as well\nas when using JSON Patch.\n\nMember @a byte holds the byte index of the last read character in the input\nfile.\n\nExceptions have ids 1xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.\njson.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\\uxxxx` entries (\"surrogate pairs\"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.\njson.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.\njson.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.\njson.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one \"op\" member, whose value indicates the operation to perform. Its value must be one of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\"; other values are errors.\njson.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.\njson.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.\njson.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.\njson.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.\njson.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.\njson.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.\njson.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.\njson.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).\njson.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed.\n\n@note For an input with n bytes, 1 is the index of the first character and n+1\n      is the index of the terminating null byte or the end of file. This also\n      holds true when reading a byte vector (CBOR or MessagePack).\n\n@liveexample{The following code shows how a `parse_error` exception can be\ncaught.,parse_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    template<typename BasicJsonType>\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        position_string(pos) + \": \" + exception::diagnostics(context) + what_arg;\n        return {id_, pos.chars_read_total, w.c_str()};\n    }\n\n    template<typename BasicJsonType>\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        (byte_ != 0 ? (\" at byte \" + std::to_string(byte_)) : \"\") +\n                        \": \" + exception::diagnostics(context) + what_arg;\n        return {id_, byte_, w.c_str()};\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return \" at line \" + std::to_string(pos.lines_read + 1) +\n               \", column \" + std::to_string(pos.chars_read_current_line);\n    }\n};\n\n/*!\n@brief exception indicating errors with iterators\n\nThis exception is thrown if iterators passed to a library function do not match\nthe expected semantics.\n\nExceptions have ids 2xx.\n\nname / id                           | example message | description\n----------------------------------- | --------------- | -------------------------\njson.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.\njson.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.\njson.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.\njson.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.\njson.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.\njson.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.\njson.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.\njson.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.\njson.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.\njson.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().\n\n@liveexample{The following code shows how an `invalid_iterator` exception can be\ncaught.,invalid_iterator}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass invalid_iterator : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"invalid_iterator\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating executing a member function with a wrong type\n\nThis exception is thrown in case of a type error; that is, a library function is\nexecuted on a JSON value whose type does not match the expected semantics.\n\nExceptions have ids 3xx.\n\nname / id                     | example message | description\n----------------------------- | --------------- | -------------------------\njson.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.\njson.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.\njson.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.\njson.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.\njson.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.\njson.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.\njson.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.\njson.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.\njson.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.\njson.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.\njson.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.\njson.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.\njson.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.\njson.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.\njson.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.\njson.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |\njson.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |\n\n@liveexample{The following code shows how a `type_error` exception can be\ncaught.,type_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass type_error : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"type_error\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating access out of the defined range\n\nThis exception is thrown in case a library function is called on an input\nparameter that exceeds the expected range, for instance in case of array\nindices or nonexisting object keys.\n\nExceptions have ids 4xx.\n\nname / id                       | example message | description\n------------------------------- | --------------- | -------------------------\njson.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.\njson.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.\njson.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.\njson.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.\njson.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.\njson.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.\njson.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |\njson.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |\njson.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |\n\n@liveexample{The following code shows how an `out_of_range` exception can be\ncaught.,out_of_range}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass out_of_range : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"out_of_range\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating other library errors\n\nThis exception is thrown in case of errors that cannot be classified with the\nother exception types.\n\nExceptions have ids 5xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.other_error.501 | unsuccessful: {\"op\":\"test\",\"path\":\"/baz\", \"value\":\"bar\"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n\n@liveexample{The following code shows how an `other_error` exception can be\ncaught.,other_error}\n\n@since version 3.0.0\n*/\nclass other_error : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"other_error\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n#include <utility> // index_sequence, make_index_sequence, index_sequence_for\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n#ifdef JSON_HAS_CPP_14\n\n// the following utilities are natively available in C++14\nusing std::enable_if_t;\nusing std::index_sequence;\nusing std::make_index_sequence;\nusing std::index_sequence_for;\n\n#else\n\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\n// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h\n// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.\n\n//// START OF CODE FROM GOOGLE ABSEIL\n\n// integer_sequence\n//\n// Class template representing a compile-time integer sequence. An instantiation\n// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its\n// type through its template arguments (which is a common need when\n// working with C++11 variadic templates). `absl::integer_sequence` is designed\n// to be a drop-in replacement for C++14's `std::integer_sequence`.\n//\n// Example:\n//\n//   template< class T, T... Ints >\n//   void user_function(integer_sequence<T, Ints...>);\n//\n//   int main()\n//   {\n//     // user_function's `T` will be deduced to `int` and `Ints...`\n//     // will be deduced to `0, 1, 2, 3, 4`.\n//     user_function(make_integer_sequence<int, 5>());\n//   }\ntemplate <typename T, T... Ints>\nstruct integer_sequence\n{\n    using value_type = T;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\n// index_sequence\n//\n// A helper template for an `integer_sequence` of `size_t`,\n// `absl::index_sequence` is designed to be a drop-in replacement for C++14's\n// `std::index_sequence`.\ntemplate <size_t... Ints>\nusing index_sequence = integer_sequence<size_t, Ints...>;\n\nnamespace utility_internal\n{\n\ntemplate <typename Seq, size_t SeqSize, size_t Rem>\nstruct Extend;\n\n// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 0>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;\n};\n\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 1>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;\n};\n\n// Recursion helper for 'make_integer_sequence<T, N>'.\n// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.\ntemplate <typename T, size_t N>\nstruct Gen\n{\n    using type =\n        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;\n};\n\ntemplate <typename T>\nstruct Gen<T, 0>\n{\n    using type = integer_sequence<T>;\n};\n\n}  // namespace utility_internal\n\n// Compile-time sequences of integers\n\n// make_integer_sequence\n//\n// This template alias is equivalent to\n// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in\n// replacement for C++14's `std::make_integer_sequence`.\ntemplate <typename T, T N>\nusing make_integer_sequence = typename utility_internal::Gen<T, N>::type;\n\n// make_index_sequence\n//\n// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,\n// and is designed to be a drop-in replacement for C++14's\n// `std::make_index_sequence`.\ntemplate <size_t N>\nusing make_index_sequence = make_integer_sequence<size_t, N>;\n\n// index_sequence_for\n//\n// Converts a typename pack into an index sequence of the same length, and\n// is designed to be a drop-in replacement for C++14's\n// `std::index_sequence_for()`\ntemplate <typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n//// END OF CODE FROM GOOGLE ABSEIL\n\n#endif\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static constexpr T value{};\n};\n\ntemplate<typename T>\nconstexpr T static_const<T>::value;\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/identity_tag.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// dispatching helper struct\ntemplate <class T> struct identity_tag {};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n#include <tuple> // tuple\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n\n#include <iterator> // random_access_iterator_tag\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename It, typename = void>\nstruct iterator_types {};\n\ntemplate<typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate<typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n            : iterator_types<T>\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/meta/call_std/begin.hpp>\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);\n} // namespace nlohmann\n\n// #include <nlohmann/detail/meta/call_std/end.hpp>\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n#define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer,\n         class BinaryType = std::vector<std::uint8_t>>\nclass basic_json;\n\n/*!\n@brief JSON Pointer\n\nA JSON pointer defines a string syntax for identifying a specific value\nwithin a JSON document. It can be used with functions `at` and\n`operator[]`. Furthermore, JSON pointers are the base for JSON patches.\n\n@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)\n\n@since version 2.0.0\n*/\ntemplate<typename BasicJsonType>\nclass json_pointer;\n\n/*!\n@brief default JSON class\n\nThis type is the default specialization of the @ref basic_json class which\nuses the standard template types.\n\n@since version 1.0.0\n*/\nusing json = basic_json<>;\n\ntemplate<class Key, class T, class IgnoredLess, class Allocator>\nstruct ordered_map;\n\n/*!\n@brief ordered JSON class\n\nThis type preserves the insertion order of object keys.\n\n@since version 3.9.0\n*/\nusing ordered_json = basic_json<nlohmann::ordered_map>;\n\n}  // namespace nlohmann\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n\nnamespace nlohmann\n{\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n//////////////////////\n// json_ref helpers //\n//////////////////////\n\ntemplate<typename>\nclass json_ref;\n\ntemplate<typename>\nstruct is_json_ref : std::false_type {};\n\ntemplate<typename T>\nstruct is_json_ref<json_ref<T>> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate<typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate<typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate<typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate<typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate<typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate<typename T>\nusing reference_t = typename T::reference;\n\ntemplate<typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate<typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate<typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate<typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\n// trait checking if j.get<T> is valid\n// use this trait instead of std::is_constructible or std::is_convertible,\n// both rely on, or make use of implicit conversions, and thus fail when T\n// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)\ntemplate <typename BasicJsonType, typename T>\nstruct is_getable\n{\n    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;\n};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\n\n///////////////////\n// is_ functions //\n///////////////////\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B1> struct conjunction<B1> : B1 { };\ntemplate<class B1, class... Bn>\nstruct conjunction<B1, Bn...>\n: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};\n\n// https://en.cppreference.com/w/cpp/types/negation\ntemplate<class B> struct negation : std::integral_constant < bool, !B::value > { };\n\n// Reimplementation of is_constructible and is_default_constructible, due to them being broken for\n// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).\n// This causes compile errors in e.g. clang 3.5 or gcc 4.9.\ntemplate <typename T>\nstruct is_default_constructible : std::is_default_constructible<T> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<std::pair<T1, T2>>\n            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<const std::pair<T1, T2>>\n            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<std::tuple<Ts...>>\n            : conjunction<is_default_constructible<Ts>...> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<const std::tuple<Ts...>>\n            : conjunction<is_default_constructible<Ts>...> {};\n\n\ntemplate <typename T, typename... Args>\nstruct is_constructible : std::is_constructible<T, Args...> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};\n\n\ntemplate<typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate<typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\ntemplate<typename T>\nstruct is_range\n{\n  private:\n    using t_ref = typename std::add_lvalue_reference<T>::type;\n\n    using iterator = detected_t<result_of_begin, t_ref>;\n    using sentinel = detected_t<result_of_end, t_ref>;\n\n    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator\n    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for\n    // but reimplementing these would be too much work, as a lot of other concepts are used underneath\n    static constexpr auto is_iterator_begin =\n        is_iterator_traits<iterator_traits<iterator>>::value;\n\n  public:\n    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;\n};\n\ntemplate<typename R>\nusing iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;\n\ntemplate<typename T>\nusing range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;\n\n// The following implementation of is_complete_type is taken from\n// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/\n// and is written by Xiang Fan who agreed to using it in this library.\n\ntemplate<typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate<typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value &&\n        is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (is_default_constructible<ConstructibleObjectType>::value &&\n         (std::is_move_assignable<ConstructibleObjectType>::value ||\n          std::is_copy_assignable<ConstructibleObjectType>::value) &&\n         (is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value &&\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) ||\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type\n{\n    static constexpr auto value =\n        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n{\n    static constexpr auto value =\n        is_constructible<ConstructibleStringType,\n        typename BasicJsonType::string_t>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t <\n    is_detected<iterator_t, CompatibleArrayType>::value&&\n    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        is_constructible<BasicJsonType,\n        range_value_t<CompatibleArrayType>>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t < !std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value&&\n    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n    is_default_constructible<ConstructibleArrayType>::value&&\n(std::is_move_assignable<ConstructibleArrayType>::value ||\n std::is_copy_assignable<ConstructibleArrayType>::value)&&\nis_detected<iterator_t, ConstructibleArrayType>::value&&\nis_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&\nis_detected<range_value_t, ConstructibleArrayType>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&\n        is_complete_type <\n        detected_t<range_value_t, ConstructibleArrayType >>::value >>\n{\n    using value_type = range_value_t<ConstructibleArrayType>;\n\n    static constexpr bool value =\n        std::is_same<value_type,\n        typename BasicJsonType::array_t::value_type>::value ||\n        has_from_json<BasicJsonType,\n        value_type>::value ||\n        has_non_default_from_json <\n        BasicJsonType,\n        value_type >::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType,\n         typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t < std::is_integral<RealIntegerType>::value&&\n    std::is_integral<CompatibleNumberIntegerType>::value&&\n    !std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value &&\n        CompatibleLimits::is_integer &&\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\ntemplate<typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate<typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};\n\n// a naive helper to check if a type is an ordered_map (exploits the fact that\n// ordered_map inherits capacity() from std::vector)\ntemplate <typename T>\nstruct is_ordered_map\n{\n    using one = char;\n\n    struct two\n    {\n        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    };\n\n    template <typename C> static one test( decltype(&C::capacity) ) ;\n    template <typename C> static two test(...);\n\n    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n};\n\n// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)\ntemplate < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >\nT conditional_static_cast(U value)\n{\n    return static_cast<T>(value);\n}\n\ntemplate<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>\nT conditional_static_cast(U value)\n{\n    return value;\n}\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#ifdef JSON_HAS_CPP_17\n    #include <filesystem>\n#endif\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be null, but is \" + std::string(j.type_name()), j));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&\n                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                         int > = 0 >\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::boolean:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name()), j));\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(j.type_name()), j));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name()), j));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename ConstructibleStringType,\n    enable_if_t <\n        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&\n        !std::is_same<typename BasicJsonType::string_t,\n                      ConstructibleStringType>::value,\n        int > = 0 >\nvoid from_json(const BasicJsonType& j, ConstructibleStringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name()), j));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    l.resize(j.size());\n    std::transform(j.begin(), j.end(), std::begin(l),\n                   [](const BasicJsonType & elem)\n    {\n        return elem.template get<T>();\n    });\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\nvoid from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                          priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate < typename BasicJsonType, typename ConstructibleArrayType,\n           enable_if_t <\n               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&\n               !is_basic_json<ConstructibleArrayType>::value,\n               int > = 0 >\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t... Idx >\nstd::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,\n        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)\n{\n    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t N >\nauto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)\n-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n\n    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(j.type_name()), j));\n    }\n\n    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be object, but is \" + std::string(j.type_name()), j));\n    }\n\n    ConstructibleObjectType ret;\n    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t <\n               std::is_arithmetic<ArithmeticType>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n               int > = 0 >\nvoid from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name()), j));\n    }\n}\n\ntemplate<typename BasicJsonType, typename... Args, std::size_t... Idx>\nstd::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)\n{\n    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);\n}\n\ntemplate < typename BasicJsonType, class A1, class A2 >\nstd::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)\n{\n    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),\n            std::forward<BasicJsonType>(j).at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\nvoid from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)\n{\n    p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nstd::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)\n{\n    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)\n{\n    t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename TupleRelated>\nauto from_json(BasicJsonType&& j, TupleRelated&& t)\n-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n\n    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name()), j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name()), j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\n#ifdef JSON_HAS_CPP_17\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, std::filesystem::path& p)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name()), j));\n    }\n    p = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n#endif\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T&& val) const\n    noexcept(noexcept(from_json(j, std::forward<T>(val))))\n    -> decltype(from_json(j, std::forward<T>(val)))\n    {\n        return from_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\nconstexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)\n} // namespace\n} // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n\n#include <algorithm> // copy\n#include <iterator> // begin, end\n#include <string> // string\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n\n#include <cstddef> // size_t\n#include <iterator> // input_iterator_tag\n#include <string> // string, to_string\n#include <tuple> // tuple_size, get, tuple_element\n#include <utility> // move\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename string_type>\nvoid int_to_string( string_type& target, std::size_t value )\n{\n    // For ADL\n    using std::to_string;\n    target = to_string(value);\n}\ntemplate<typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type * ;\n    using reference = value_type & ;\n    using iterator_category = std::input_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor;\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    const string_type empty_str{};\n\n  public:\n    explicit iteration_proxy_value(IteratorType it) noexcept\n        : anchor(std::move(it))\n    {}\n\n    /// dereference operator (needed for range-based for)\n    iteration_proxy_value& operator*()\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        JSON_ASSERT(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::reference container;\n\n  public:\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(cont) {}\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.end());\n    }\n};\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate<typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>\n            : public std::integral_constant<std::size_t, 2> {};\n\ntemplate<std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n} // namespace std\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#ifdef JSON_HAS_CPP_17\n    #include <filesystem>\n#endif\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////\n// constructors //\n//////////////////\n\n/*\n * Note all external_constructor<>::construct functions need to call\n * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an\n * allocated value (e.g., a string). See bug issue\n * https://github.com/nlohmann/json/issues/2865 for more information.\n */\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleStringType,\n               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::binary>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::binary;\n        j.m_value = typename BasicJsonType::binary_t(b);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::binary;\n        j.m_value = typename BasicJsonType::binary_t(std::move(b));\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleArrayType,\n               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n            j.set_parent(j.m_value.array->back());\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        }\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleObjectType,\n               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\nvoid to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate < typename BasicJsonType, typename CompatibleArrayType,\n           enable_if_t < is_compatible_array_type<BasicJsonType,\n                         CompatibleArrayType>::value&&\n                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&\n                         !is_basic_json<CompatibleArrayType>::value,\n                         int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)\n{\n    external_constructor<value_t::binary>::construct(j, bin);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate < typename BasicJsonType, typename CompatibleObjectType,\n           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,\n                  const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n                  int > = 0 >\nvoid to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\nvoid to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\n#ifdef JSON_HAS_CPP_17\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::filesystem::path& p)\n{\n    j = p.string();\n}\n#endif\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `to_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\nconstexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)\n} // namespace\n} // namespace nlohmann\n\n// #include <nlohmann/detail/meta/identity_tag.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\n\ntemplate<typename ValueType, typename>\nstruct adl_serializer\n{\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @note This function is chosen for default-constructible value types.\n\n    @param[in] j        JSON value to read from\n    @param[in,out] val  value to write to\n    */\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @note This function is chosen for value types which are not default-constructible.\n\n    @param[in] j  JSON value to read from\n\n    @return copy of the JSON value, converted to @a ValueType\n    */\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j) noexcept(\n    noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))\n    {\n        return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});\n    }\n\n    /*!\n    @brief convert any value type to a JSON value\n\n    This function is usually called by the constructors of the @ref basic_json\n    class.\n\n    @param[in,out] j  JSON value to write to\n    @param[in] val    value to read from\n    */\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<TargetType>(val));\n    }\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/byte_container_with_subtype.hpp>\n\n\n#include <cstdint> // uint8_t, uint64_t\n#include <tuple> // tie\n#include <utility> // move\n\nnamespace nlohmann\n{\n\n/*!\n@brief an internal type for a backed binary type\n\nThis type extends the template parameter @a BinaryType provided to `basic_json`\nwith a subtype used by BSON and MessagePack. This type exists so that the user\ndoes not have to specify a type themselves with a specific naming scheme in\norder to override the binary type.\n\n@tparam BinaryType container to store bytes (`std::vector<std::uint8_t>` by\n                   default)\n\n@since version 3.8.0; changed type of subtypes to std::uint64_t in 3.10.0.\n*/\ntemplate<typename BinaryType>\nclass byte_container_with_subtype : public BinaryType\n{\n  public:\n    /// the type of the underlying container\n    using container_type = BinaryType;\n    /// the type of the subtype\n    using subtype_type = std::uint64_t;\n\n    byte_container_with_subtype() noexcept(noexcept(container_type()))\n        : container_type()\n    {}\n\n    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n    {}\n\n    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n    {}\n\n    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    bool operator==(const byte_container_with_subtype& rhs) const\n    {\n        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==\n               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);\n    }\n\n    bool operator!=(const byte_container_with_subtype& rhs) const\n    {\n        return !(rhs == *this);\n    }\n\n    /*!\n    @brief sets the binary subtype\n\n    Sets the binary subtype of the value, also flags a binary JSON value as\n    having a subtype, which has implications for serialization.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref subtype() -- return the binary subtype\n    @sa see @ref clear_subtype() -- clears the binary subtype\n    @sa see @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void set_subtype(subtype_type subtype_) noexcept\n    {\n        m_subtype = subtype_;\n        m_has_subtype = true;\n    }\n\n    /*!\n    @brief return the binary subtype\n\n    Returns the numerical subtype of the value if it has a subtype. If it does\n    not have a subtype, this function will return subtype_type(-1) as a sentinel\n    value.\n\n    @return the numerical subtype of the binary value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref set_subtype() -- sets the binary subtype\n    @sa see @ref clear_subtype() -- clears the binary subtype\n    @sa see @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0; fixed return value to properly return\n           subtype_type(-1) as documented in version 3.10.0\n    */\n    constexpr subtype_type subtype() const noexcept\n    {\n        return m_has_subtype ? m_subtype : subtype_type(-1);\n    }\n\n    /*!\n    @brief return whether the value has a subtype\n\n    @return whether the value has a subtype\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref subtype() -- return the binary subtype\n    @sa see @ref set_subtype() -- sets the binary subtype\n    @sa see @ref clear_subtype() -- clears the binary subtype\n\n    @since version 3.8.0\n    */\n    constexpr bool has_subtype() const noexcept\n    {\n        return m_has_subtype;\n    }\n\n    /*!\n    @brief clears the binary subtype\n\n    Clears the binary subtype and flags the value as not having a subtype, which\n    has implications for serialization; for instance MessagePack will prefer the\n    bin family over the ext family.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref subtype() -- return the binary subtype\n    @sa see @ref set_subtype() -- sets the binary subtype\n    @sa see @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void clear_subtype() noexcept\n    {\n        m_subtype = 0;\n        m_has_subtype = false;\n    }\n\n  private:\n    subtype_type m_subtype = 0;\n    bool m_has_subtype = false;\n};\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/hash.hpp>\n\n\n#include <cstdint> // uint8_t\n#include <cstddef> // size_t\n#include <functional> // hash\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n// boost::hash_combine\ninline std::size_t combine(std::size_t seed, std::size_t h) noexcept\n{\n    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);\n    return seed;\n}\n\n/*!\n@brief hash a JSON value\n\nThe hash function tries to rely on std::hash where possible. Furthermore, the\ntype of the JSON value is taken into account to have different hash values for\nnull, 0, 0U, and false, etc.\n\n@tparam BasicJsonType basic_json specialization\n@param j JSON value to hash\n@return hash value of j\n*/\ntemplate<typename BasicJsonType>\nstd::size_t hash(const BasicJsonType& j)\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n    const auto type = static_cast<std::size_t>(j.type());\n    switch (j.type())\n    {\n        case BasicJsonType::value_t::null:\n        case BasicJsonType::value_t::discarded:\n        {\n            return combine(type, 0);\n        }\n\n        case BasicJsonType::value_t::object:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j.items())\n            {\n                const auto h = std::hash<string_t> {}(element.key());\n                seed = combine(seed, h);\n                seed = combine(seed, hash(element.value()));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::array:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j)\n            {\n                seed = combine(seed, hash(element));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::string:\n        {\n            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::boolean:\n        {\n            const auto h = std::hash<bool> {}(j.template get<bool>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_integer:\n        {\n            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_unsigned:\n        {\n            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_float:\n        {\n            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::binary:\n        {\n            auto seed = combine(type, j.get_binary().size());\n            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());\n            seed = combine(seed, h);\n            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));\n            for (const auto byte : j.get_binary())\n            {\n                seed = combine(seed, std::hash<std::uint8_t> {}(byte));\n            }\n            return seed;\n        }\n\n        default:                   // LCOV_EXCL_LINE\n            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            return 0;              // LCOV_EXCL_LINE\n    }\n}\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstring> // strlen\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n#ifndef JSON_NO_IO\n    #include <cstdio>   // FILE *\n    #include <istream>  // istream\n#endif                  // JSON_NO_IO\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson };\n\n////////////////////\n// input adapters //\n////////////////////\n\n#ifndef JSON_NO_IO\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter\n{\n  public:\n    using char_type = char;\n\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f) noexcept\n        : m_file(f)\n    {}\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) noexcept = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = delete;\n    ~file_input_adapter() = default;\n\n    std::char_traits<char>::int_type get_character() noexcept\n    {\n        return std::fgetc(m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter\n{\n  public:\n    using char_type = char;\n\n    ~input_stream_adapter()\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        if (is != nullptr)\n        {\n            is->clear(is->rdstate() & std::ios::eofbit);\n        }\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(&i), sb(i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&&) = delete;\n\n    input_stream_adapter(input_stream_adapter&& rhs) noexcept\n        : is(rhs.is), sb(rhs.sb)\n    {\n        rhs.is = nullptr;\n        rhs.sb = nullptr;\n    }\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, eg. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character()\n    {\n        auto res = sb->sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream* is = nullptr;\n    std::streambuf* sb = nullptr;\n};\n#endif  // JSON_NO_IO\n\n// General-purpose iterator-based adapter. It might not be as fast as\n// theoretically possible for some containers, but it is extremely versatile.\ntemplate<typename IteratorType>\nclass iterator_input_adapter\n{\n  public:\n    using char_type = typename std::iterator_traits<IteratorType>::value_type;\n\n    iterator_input_adapter(IteratorType first, IteratorType last)\n        : current(std::move(first)), end(std::move(last))\n    {}\n\n    typename std::char_traits<char_type>::int_type get_character()\n    {\n        if (JSON_HEDLEY_LIKELY(current != end))\n        {\n            auto result = std::char_traits<char_type>::to_int_type(*current);\n            std::advance(current, 1);\n            return result;\n        }\n\n        return std::char_traits<char_type>::eof();\n    }\n\n  private:\n    IteratorType current;\n    IteratorType end;\n\n    template<typename BaseInputAdapter, size_t T>\n    friend struct wide_string_input_helper;\n\n    bool empty() const\n    {\n        return current == end;\n    }\n};\n\n\ntemplate<typename BaseInputAdapter, size_t T>\nstruct wide_string_input_helper;\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 4>\n{\n    // UTF-32\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 2>\n{\n    // UTF-16\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc || wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (JSON_HEDLEY_UNLIKELY(!input.empty()))\n                {\n                    const auto wc2 = static_cast<unsigned int>(input.get_character());\n                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\n// Wraps another input apdater to convert wide character types into individual bytes.\ntemplate<typename BaseInputAdapter, typename WideCharType>\nclass wide_string_input_adapter\n{\n  public:\n    using char_type = char;\n\n    wide_string_input_adapter(BaseInputAdapter base)\n        : base_adapter(base) {}\n\n    typename std::char_traits<char>::int_type get_character() noexcept\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(WideCharType)>();\n\n            JSON_ASSERT(utf8_bytes_filled > 0);\n            JSON_ASSERT(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        JSON_ASSERT(utf8_bytes_filled > 0);\n        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    BaseInputAdapter base_adapter;\n\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\n\ntemplate<typename IteratorType, typename Enable = void>\nstruct iterator_input_adapter_factory\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using adapter_type = iterator_input_adapter<iterator_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(std::move(first), std::move(last));\n    }\n};\n\ntemplate<typename T>\nstruct is_iterator_of_multibyte\n{\n    using value_type = typename std::iterator_traits<T>::value_type;\n    enum\n    {\n        value = sizeof(value_type) > 1\n    };\n};\n\ntemplate<typename IteratorType>\nstruct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using base_adapter_type = iterator_input_adapter<iterator_type>;\n    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(base_adapter_type(std::move(first), std::move(last)));\n    }\n};\n\n// General purpose iterator-based input\ntemplate<typename IteratorType>\ntypename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)\n{\n    using factory_type = iterator_input_adapter_factory<IteratorType>;\n    return factory_type::create(first, last);\n}\n\n// Convenience shorthand from container to iterator\n// Enables ADL on begin(container) and end(container)\n// Encloses the using declarations in namespace for not to leak them to outside scope\n\nnamespace container_input_adapter_factory_impl\n{\n\nusing std::begin;\nusing std::end;\n\ntemplate<typename ContainerType, typename Enable = void>\nstruct container_input_adapter_factory {};\n\ntemplate<typename ContainerType>\nstruct container_input_adapter_factory< ContainerType,\n       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>\n       {\n           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));\n\n           static adapter_type create(const ContainerType& container)\n{\n    return input_adapter(begin(container), end(container));\n}\n       };\n\n} // namespace container_input_adapter_factory_impl\n\ntemplate<typename ContainerType>\ntypename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)\n{\n    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);\n}\n\n#ifndef JSON_NO_IO\n// Special cases with fast paths\ninline file_input_adapter input_adapter(std::FILE* file)\n{\n    return file_input_adapter(file);\n}\n\ninline input_stream_adapter input_adapter(std::istream& stream)\n{\n    return input_stream_adapter(stream);\n}\n\ninline input_stream_adapter input_adapter(std::istream&& stream)\n{\n    return input_stream_adapter(stream);\n}\n#endif  // JSON_NO_IO\n\nusing contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));\n\n// Null-delimited strings, and the like.\ntemplate < typename CharT,\n           typename std::enable_if <\n               std::is_pointer<CharT>::value&&\n               !std::is_array<CharT>::value&&\n               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n               sizeof(typename std::remove_pointer<CharT>::type) == 1,\n               int >::type = 0 >\ncontiguous_bytes_input_adapter input_adapter(CharT b)\n{\n    auto length = std::strlen(reinterpret_cast<const char*>(b));\n    const auto* ptr = reinterpret_cast<const char*>(b);\n    return input_adapter(ptr, ptr + length);\n}\n\ntemplate<typename T, std::size_t N>\nauto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    return input_adapter(array, array + N);\n}\n\n// This class only handles inputs of input_buffer_adapter type.\n// It's required so that expressions like {ptr, len} can be implicitely casted\n// to the correct adapter.\nclass span_input_adapter\n{\n  public:\n    template < typename CharT,\n               typename std::enable_if <\n                   std::is_pointer<CharT>::value&&\n                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n                   sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                   int >::type = 0 >\n    span_input_adapter(CharT b, std::size_t l)\n        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}\n\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    span_input_adapter(IteratorType first, IteratorType last)\n        : ia(input_adapter(first, last)) {}\n\n    contiguous_bytes_input_adapter&& get()\n    {\n        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)\n    }\n\n  private:\n    contiguous_bytes_input_adapter ia;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n\n#include <cstddef>\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief an floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief a binary string was read\n    @param[in] val  binary value\n    @return whether parsing should proceed\n    @note It is safe to move the passed binary.\n    */\n    virtual bool binary(binary_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    json_sax() = default;\n    json_sax(const json_sax&) = default;\n    json_sax(json_sax&&) noexcept = default;\n    json_sax& operator=(const json_sax&) = default;\n    json_sax& operator=(json_sax&&) noexcept = default;\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @param[in,out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_object());\n        JSON_ASSERT(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep && ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back())\n        {\n            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n            {\n                // discard object\n                *ref_stack.back() = discarded;\n            }\n            else\n            {\n                ref_stack.back()->set_parents();\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (keep)\n            {\n                ref_stack.back()->set_parents();\n            }\n            else\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        JSON_ASSERT(!keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (!keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (!keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (!ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n\n        // object\n        JSON_ASSERT(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        JSON_ASSERT(!key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (!store_element)\n        {\n            return {false, nullptr};\n        }\n\n        JSON_ASSERT(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {};\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool binary(binary_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n}  // namespace detail\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////\n// lexer //\n///////////\n\ntemplate<typename BasicJsonType>\nclass lexer_base\n{\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case token_type::value_unsigned:\n            case token_type::value_integer:\n            case token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n};\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass lexer : public lexer_base<BasicJsonType>\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    using token_type = typename lexer_base<BasicJsonType>::token_type;\n\n    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept\n        : ia(std::move(adapter))\n        , ignore_comments(ignore_comments_)\n        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))\n    {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto* loc = localeconv();\n        JSON_ASSERT(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        JSON_ASSERT(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' && current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' && current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' && current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)\n    {\n        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 8259. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        JSON_ASSERT(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char_type>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' && get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                                        // in the result so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(static_cast<char_int_type>(codepoint));\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    /*!\n     * @brief scan a comment\n     * @return whether comment could be scanned successfully\n     */\n    bool scan_comment()\n    {\n        switch (get())\n        {\n            // single-line comments skip input until a newline or EOF is read\n            case '/':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case '\\n':\n                        case '\\r':\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                            return true;\n\n                        default:\n                            break;\n                    }\n                }\n            }\n\n            // multi-line comments skip input until */ is read\n            case '*':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                        {\n                            error_message = \"invalid comment; missing closing '*/'\";\n                            return false;\n                        }\n\n                        case '*':\n                        {\n                            switch (get())\n                            {\n                                case '/':\n                                    return true;\n\n                                default:\n                                {\n                                    unget();\n                                    continue;\n                                }\n                            }\n                        }\n\n                        default:\n                            continue;\n                    }\n                }\n            }\n\n            // unexpected character after reading '/'\n            default:\n            {\n                error_message = \"invalid comment; expecting '/' or '*' after '/'\";\n                return false;\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 8259.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 8259. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char_type* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia.get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            JSON_ASSERT(!token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(char_int_type c)\n    {\n        token_buffer.push_back(static_cast<typename string_t::value_type>(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if (static_cast<unsigned char>(c) <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                (std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(static_cast<std::string::value_type>(c));\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB && get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    void skip_whitespace()\n    {\n        do\n        {\n            get();\n        }\n        while (current == ' ' || current == '\\t' || current == '\\n' || current == '\\r');\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 && !skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        skip_whitespace();\n\n        // ignore comments\n        while (ignore_comments && current == '/')\n        {\n            if (!scan_comment())\n            {\n                return token_type::parse_error;\n            }\n\n            // skip following whitespace\n            skip_whitespace();\n        }\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n            {\n                std::array<char_type, 4> true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}};\n                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);\n            }\n            case 'f':\n            {\n                std::array<char_type, 5> false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}};\n                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);\n            }\n            case 'n':\n            {\n                std::array<char_type, 4> null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}};\n                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);\n            }\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char_type>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// whether comments should be ignored (true) or signaled as errors (false)\n    const bool ignore_comments = false;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char_type> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char_int_type decimal_point_char = '.';\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n\n#include <cstdint> // size_t\n#include <utility> // declval\n#include <string> // string\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate<typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate<typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate<typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate<typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate<typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate<typename T, typename Binary>\nusing binary_function_t =\n    decltype(std::declval<T&>().binary(std::declval<Binary&>()));\n\ntemplate<typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate<typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate<typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate<typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate<typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate<typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,\n        \"Missing/invalid function: bool binary(binary_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/// how to treat CBOR tags\nenum class cbor_tag_handler_t\n{\n    error,   ///< throw a parse_error exception in case of a tag\n    ignore,  ///< ignore tags\n    store    ///< store tags as binary type\n};\n\n/*!\n@brief determine system byte order\n\n@return true if and only if system's byte order is little endian\n\n@note from https://stackoverflow.com/a/1001328/266378\n*/\nstatic inline bool little_endianess(int num = 1) noexcept\n{\n    return *reinterpret_cast<char*>(&num) == 1;\n}\n\n\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using json_sax_t = SAX;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter))\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n    @param[in] tag_handler  how to treat CBOR tags\n\n    @return whether parsing was successful\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true,\n                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal(true, tag_handler);\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n                result = parse_ubjson_internal();\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result && strict)\n        {\n            if (format == input_format_t::ubjson)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(),\n                                        parse_error::create(110, chars_read, exception_message(format, \"expected end of input; last byte: 0x\" + get_token_string(), \"value\"), BasicJsonType()));\n            }\n        }\n\n        return result;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<typename string_t::value_type>(current);\n        }\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"string length must be at least 1, is \" + std::to_string(len), \"string\"), BasicJsonType()));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();\n    }\n\n    /*!\n    @brief Parses a byte array input of length @a len from the BSON input.\n    @param[in] len  The length of the byte array to be read.\n    @param[in,out] result  A reference to the binary variable where the read\n                            array is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 0\n    @return `true` if the byte array was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_binary(const NumberType len, binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 0))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"byte array length cannot be negative, is \" + std::to_string(len), \"binary\"), BasicJsonType()));\n        }\n\n        // All BSON binary values have a subtype\n        std::uint8_t subtype{};\n        get_number<std::uint8_t>(input_format_t::bson, subtype);\n        result.set_subtype(subtype);\n\n        return get_binary(input_format_t::bson, len, result);\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const char_int_type element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number{};\n                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len{};\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x05: // binary\n            {\n                std::int32_t len{};\n                binary_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value{};\n                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value{};\n                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, \"Unsupported BSON record type 0x\" + std::string(cr.data()), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n\n        while (auto element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (!is_array && !sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true) or whether the last read character should\n                         be considered instead (false)\n    @param[in] tag_handler how CBOR tags should be treated\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char,\n                             const cbor_tag_handler_t tag_handler)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            case 0x5F: // Binary data (indefinite length)\n            {\n                binary_t b;\n                return get_cbor_binary(b) && sax->binary(b);\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) && sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(std::size_t(-1), tag_handler);\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(std::size_t(-1), tag_handler);\n\n            case 0xC6: // tagged item\n            case 0xC7:\n            case 0xC8:\n            case 0xC9:\n            case 0xCA:\n            case 0xCB:\n            case 0xCC:\n            case 0xCD:\n            case 0xCE:\n            case 0xCF:\n            case 0xD0:\n            case 0xD1:\n            case 0xD2:\n            case 0xD3:\n            case 0xD4:\n            case 0xD8: // tagged item (1 bytes follow)\n            case 0xD9: // tagged item (2 bytes follow)\n            case 0xDA: // tagged item (4 bytes follow)\n            case 0xDB: // tagged item (8 bytes follow)\n            {\n                switch (tag_handler)\n                {\n                    case cbor_tag_handler_t::error:\n                    {\n                        auto last_token = get_token_string();\n                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n                    }\n\n                    case cbor_tag_handler_t::ignore:\n                    {\n                        // ignore binary subtype\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        return parse_cbor_internal(true, tag_handler);\n                    }\n\n                    case cbor_tag_handler_t::store:\n                    {\n                        binary_t b;\n                        // use binary subtype and store in binary container\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            default:\n                                return parse_cbor_internal(true, tag_handler);\n                        }\n                        get();\n                        return get_cbor_binary(b) && sax->binary(b);\n                    }\n\n                    default:                 // LCOV_EXCL_LINE\n                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                        return false;        // LCOV_EXCL_LINE\n                }\n            }\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (!get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\" + last_token, \"string\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into the byte array.\n    Additionally, CBOR's byte arrays with indefinite lengths are supported.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_cbor_binary(binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"binary\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            {\n                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5F: // Binary data (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    binary_t chunk;\n                    if (!get_cbor_binary(chunk))\n                    {\n                        return false;\n                    }\n                    result.insert(result.end(), chunk.begin(), chunk.end());\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x\" + last_token, \"binary\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or std::size_t(-1) for an\n                    array of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len,\n                        const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or std::size_t(-1) for an\n                    object of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len,\n                         const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        if (len != 0)\n        {\n            string_t key;\n            if (len != std::size_t(-1))\n            {\n                for (std::size_t i = 0; i < len; ++i)\n                {\n                    get();\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                while (get() != 0xFF)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) && sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xC4: // bin 8\n            case 0xC5: // bin 16\n            case 0xC6: // bin 32\n            case 0xC7: // ext 8\n            case 0xC8: // ext 16\n            case 0xC9: // ext 32\n            case 0xD4: // fixext 1\n            case 0xD5: // fixext 2\n            case 0xD6: // fixext 4\n            case 0xD7: // fixext 8\n            case 0xD8: // fixext 16\n            {\n                binary_t b;\n                return get_msgpack_binary(b) && sax->binary(b);\n            }\n\n            case 0xCA: // float 32\n            {\n                float number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, \"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\" + last_token, \"string\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into a byte array.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_msgpack_binary(binary_t& result)\n    {\n        // helper function to set the subtype\n        auto assign_and_return_true = [&result](std::int8_t subtype)\n        {\n            result.set_subtype(static_cast<std::uint8_t>(subtype));\n            return true;\n        };\n\n        switch (current)\n        {\n            case 0xC4: // bin 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC5: // bin 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC6: // bin 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC7: // ext 8\n            {\n                std::uint8_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC8: // ext 16\n            {\n                std::uint16_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC9: // ext 32\n            {\n                std::uint32_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD4: // fixext 1\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 1, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD5: // fixext 2\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 2, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD6: // fixext 4\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 4, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD7: // fixext 8\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 8, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD8: // fixext 16\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 16, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            default:           // LCOV_EXCL_LINE\n                return false;  // LCOV_EXCL_LINE\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            default:\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token, \"string\"), BasicJsonType()));\n        }\n    }\n\n    /*!\n    @param[out] result  determined size\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result)\n    {\n        switch (get_ignore_noop())\n        {\n            case 'U':\n            {\n                std::uint8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token, \"size\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result)\n    {\n        result.first = string_t::npos; // size\n        result.second = 0; // type\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"expected '#' after type information; last byte: 0x\" + last_token, \"size\"), BasicJsonType()));\n            }\n\n            return get_ubjson_size_value(result.first);\n        }\n\n        if (current == '#')\n        {\n            return get_ubjson_size_value(result.first);\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const char_int_type prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char_type>::eof():  // EOF\n                return unexpect_eof(input_format_t::ubjson, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'd':\n            {\n                float number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'H':\n            {\n                return get_ubjson_high_precision_number();\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\" + last_token, \"char\"), BasicJsonType()));\n                }\n                string_t s(1, static_cast<typename string_t::value_type>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) && sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    // Note, no reader for UBJSON binary types is implemented because they do\n    // not exist\n\n    bool get_ubjson_high_precision_number()\n    {\n        // get size of following number string\n        std::size_t size{};\n        auto res = get_ubjson_size_value(size);\n        if (JSON_HEDLEY_UNLIKELY(!res))\n        {\n            return res;\n        }\n\n        // get number string\n        std::vector<char> number_vector;\n        for (std::size_t i = 0; i < size; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"number\")))\n            {\n                return false;\n            }\n            number_vector.push_back(static_cast<char>(current));\n        }\n\n        // parse number string\n        using ia_type = decltype(detail::input_adapter(number_vector));\n        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);\n        const auto result_number = number_lexer.scan();\n        const auto number_string = number_lexer.get_token_string();\n        const auto result_remainder = number_lexer.scan();\n\n        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;\n\n        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))\n        {\n            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\"), BasicJsonType()));\n        }\n\n        switch (result_number)\n        {\n            case token_type::value_integer:\n                return sax->number_integer(number_lexer.get_number_integer());\n            case token_type::value_unsigned:\n                return sax->number_unsigned(number_lexer.get_number_unsigned());\n            case token_type::value_float:\n                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));\n            case token_type::uninitialized:\n            case token_type::literal_true:\n            case token_type::literal_false:\n            case token_type::literal_null:\n            case token_type::value_string:\n            case token_type::begin_array:\n            case token_type::begin_object:\n            case token_type::end_array:\n            case token_type::end_object:\n            case token_type::name_separator:\n            case token_type::value_separator:\n            case token_type::parse_error:\n            case token_type::end_of_input:\n            case token_type::literal_or_value:\n            default:\n                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\"), BasicJsonType()));\n        }\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char_type>::eof()` in that case.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++chars_read;\n        return current = ia.get_character();\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    char_int_type get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianess, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<std::uint8_t, sizeof(NumberType)> vec{};\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian != InputIsLittleEndian)\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"string\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<typename string_t::value_type>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @brief create a byte array by reading bytes from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of bytes to read\n    @param[out] result byte array created by reading @a len bytes\n\n    @return whether byte array creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of memory.\n    */\n    template<typename NumberType>\n    bool get_binary(const input_format_t format,\n                    const NumberType len,\n                    binary_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"binary\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<std::uint8_t>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context), BasicJsonType()));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        return error_msg + \" \" + context + \": \" + detail;\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/input/parser.hpp>\n\n\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\nenum class parse_event_t : std::uint8_t\n{\n    /// the parser read `{` and started to process a JSON object\n    object_start,\n    /// the parser read `}` and finished processing a JSON object\n    object_end,\n    /// the parser read `[` and started to process a JSON array\n    array_start,\n    /// the parser read `]` and finished processing a JSON array\n    array_end,\n    /// the parser read a key of a value in an object\n    key,\n    /// the parser finished reading a JSON value\n    value\n};\n\ntemplate<typename BasicJsonType>\nusing parser_callback_t =\n    std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive descent parser.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    /// a parser reading from an input adapter\n    explicit parser(InputAdapterType&& adapter,\n                    const parser_callback_t<BasicJsonType> cb = nullptr,\n                    const bool allow_exceptions_ = true,\n                    const bool skip_comments = false)\n        : callback(cb)\n        , m_lexer(std::move(adapter), skip_comments)\n        , allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\"), BasicJsonType()));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, \"value\"), BasicJsonType()));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n\n        result.assert_invariant();\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result && strict && (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, \"value\"), BasicJsonType()));\n        }\n\n        return result;\n    }\n\n  private:\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (!skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, \"object key\"), BasicJsonType()));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, \"object separator\"), BasicJsonType()));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, \"number overflow parsing '\" + m_lexer.get_token_string() + \"'\", BasicJsonType()));\n                        }\n\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        break;\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, \"value\"), BasicJsonType()));\n                    }\n\n                    case token_type::uninitialized:\n                    case token_type::end_array:\n                    case token_type::end_object:\n                    case token_type::name_separator:\n                    case token_type::value_separator:\n                    case token_type::end_of_input:\n                    case token_type::literal_or_value:\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, \"value\"), BasicJsonType()));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n\n            if (states.back())  // array\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse a new value\n                    get_token();\n                    continue;\n                }\n\n                // closing ]\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this array. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, \"array\"), BasicJsonType()));\n            }\n\n            // states.back() is false -> object\n\n            // comma -> next value\n            if (get_token() == token_type::value_separator)\n            {\n                // parse key\n                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))\n                {\n                    return sax->parse_error(m_lexer.get_position(),\n                                            m_lexer.get_token_string(),\n                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, \"object key\"), BasicJsonType()));\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                {\n                    return false;\n                }\n\n                // parse separator (:)\n                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                {\n                    return sax->parse_error(m_lexer.get_position(),\n                                            m_lexer.get_token_string(),\n                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, \"object separator\"), BasicJsonType()));\n                }\n\n                // parse values\n                get_token();\n                continue;\n            }\n\n            // closing }\n            if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                {\n                    return false;\n                }\n\n                // We are done with this object. Before we can parse a\n                // new value, we need to evaluate the new state first.\n                // By setting skip_to_state_evaluation to false, we\n                // are effectively jumping to the beginning of this if.\n                JSON_ASSERT(!states.empty());\n                states.pop_back();\n                skip_to_state_evaluation = true;\n                continue;\n            }\n\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, \"object\"), BasicJsonType()));\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return last_token = m_lexer.scan();\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (!context.empty())\n        {\n            error_msg += \"while parsing \" + context + \" \";\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += std::string(m_lexer.get_error_message()) + \"; last read: '\" +\n                         m_lexer.get_token_string() + \"'\";\n        }\n        else\n        {\n            error_msg += \"unexpected \" + std::string(lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += \"; expected \" + std::string(lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t<BasicJsonType> callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iter_impl.hpp>\n\n\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n    /// the iterator with BasicJsonType of different const-ness\n    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    /// allow basic_json to access private members\n    friend other_iter_impl;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n\n  public:\n\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    iter_impl() = default;\n    ~iter_impl() = default;\n    iter_impl(iter_impl&&) noexcept = default;\n    iter_impl& operator=(iter_impl&&) noexcept = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        if (&other != this)\n        {\n            m_object = other.m_object;\n            m_it = other.m_it;\n        }\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator++(int) // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator--(int) // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator==(const IterImpl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", *m_object));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator!=(const IterImpl& other) const\n    {\n        return !operator==(other);\n    }\n\n    /*!\n    @brief comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", *m_object));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\", *m_object));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return !other.operator < (*this);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return !operator<=(other);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return !operator<(other);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", *m_object));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", *m_object));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\", *m_object));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\", *m_object));\n    }\n\n    /*!\n    @brief return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/json_pointer.hpp>\n\n\n#include <algorithm> // all_of\n#include <cctype> // isdigit\n#include <limits> // max\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\ntemplate<typename BasicJsonType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n  public:\n    /*!\n    @brief create JSON pointer\n\n    Create a JSON pointer according to the syntax described in\n    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).\n\n    @param[in] s  string representing the JSON pointer; if omitted, the empty\n                  string is assumed which references the whole JSON value\n\n    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does\n                           not begin with a slash (`/`); see example below\n\n    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is\n    not followed by `0` (representing `~`) or `1` (representing `/`); see\n    example below\n\n    @liveexample{The example shows the construction several valid JSON pointers\n    as well as the exceptional behavior.,json_pointer}\n\n    @since version 2.0.0\n    */\n    explicit json_pointer(const std::string& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /*!\n    @brief return a string representation of the JSON pointer\n\n    @invariant For each JSON pointer `ptr`, it holds:\n    @code {.cpp}\n    ptr == json_pointer(ptr.to_string());\n    @endcode\n\n    @return a string representation of the JSON pointer\n\n    @liveexample{The example shows the result of `to_string`.,json_pointer__to_string}\n\n    @since version 2.0.0\n    */\n    std::string to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               std::string{},\n                               [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + detail::escape(b);\n        });\n    }\n\n    /// @copydoc to_string()\n    operator std::string() const\n    {\n        return to_string();\n    }\n\n    /*!\n    @brief append another JSON pointer at the end of this JSON pointer\n\n    @param[in] ptr  JSON pointer to append\n    @return JSON pointer with @a ptr appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa see @ref operator/=(std::string) to append a reference token\n    @sa see @ref operator/=(std::size_t) to append an array index\n    @sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /*!\n    @brief append an unescaped reference token at the end of this JSON pointer\n\n    @param[in] token  reference token to append\n    @return JSON pointer with @a token appended without escaping @a token\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa see @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa see @ref operator/=(std::size_t) to append an array index\n    @sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::string token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /*!\n    @brief append an array index at the end of this JSON pointer\n\n    @param[in] array_idx  array index to append\n    @return JSON pointer with @a array_idx appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa see @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa see @ref operator/=(std::string) to append a reference token\n    @sa see @ref operator/(const json_pointer&, std::string) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::size_t array_idx)\n    {\n        return *this /= std::to_string(array_idx);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n\n    @param[in] lhs  JSON pointer\n    @param[in] rhs  JSON pointer\n    @return a new JSON pointer with @a rhs appended to @a lhs\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a lhs and @a rhs.\n\n    @sa see @ref operator/=(const json_pointer&) to append a JSON pointer\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] token  reference token\n    @return a new JSON pointer with unescaped @a token appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa see @ref operator/=(std::string) to append a reference token\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param)\n    {\n        return json_pointer(ptr) /= std::move(token);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] array_idx  array index\n    @return a new JSON pointer with @a array_idx appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa see @ref operator/=(std::size_t) to append an array index\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)\n    {\n        return json_pointer(ptr) /= array_idx;\n    }\n\n    /*!\n    @brief returns the parent of this JSON pointer\n\n    @return parent of this JSON pointer; in case this JSON pointer is the root,\n            the root itself is returned\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @liveexample{The example shows the result of `parent_pointer` for different\n    JSON Pointers.,json_pointer__parent_pointer}\n\n    @since version 3.6.0\n    */\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /*!\n    @brief remove last reference token\n\n    @pre not `empty()`\n\n    @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", BasicJsonType()));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /*!\n    @brief return last reference token\n\n    @pre not `empty()`\n    @return last reference token\n\n    @liveexample{The example shows the usage of `back`.,json_pointer__back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    const std::string& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", BasicJsonType()));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /*!\n    @brief append an unescaped token at the end of the reference pointer\n\n    @param[in] token  token to add\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows the result of `push_back` for different\n    JSON Pointers.,json_pointer__push_back}\n\n    @since version 3.6.0\n    */\n    void push_back(const std::string& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @copydoc push_back(const std::string&)\n    void push_back(std::string&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /*!\n    @brief return whether pointer points to the root document\n\n    @return true iff the JSON pointer points to the root document\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example shows the result of `empty` for different JSON\n    Pointers.,json_pointer__empty}\n\n    @since version 3.6.0\n    */\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw parse_error.106  if an array index begins with '0'\n    @throw parse_error.109  if an array index begins not with a digit\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    @throw out_of_range.410 if an array index exceeds size_type\n    */\n    static typename BasicJsonType::size_type array_index(const std::string& s)\n    {\n        using size_type = typename BasicJsonType::size_type;\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))\n        {\n            JSON_THROW(detail::parse_error::create(106, 0, \"array index '\" + s + \"' must not begin with '0'\", BasicJsonType()));\n        }\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))\n        {\n            JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + s + \"' is not a number\", BasicJsonType()));\n        }\n\n        std::size_t processed_chars = 0;\n        unsigned long long res = 0;  // NOLINT(runtime/int)\n        JSON_TRY\n        {\n            res = std::stoull(s, &processed_chars);\n        }\n        JSON_CATCH(std::out_of_range&)\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\", BasicJsonType()));\n        }\n\n        // check if the string was completely read\n        if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\", BasicJsonType()));\n        }\n\n        // only triggered on special platforms (like 32bit), see also\n        // https://github.com/nlohmann/json/pull/2203\n        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)\n        {\n            JSON_THROW(detail::out_of_range::create(410, \"array index \" + s + \" exceeds size_type\", BasicJsonType())); // LCOV_EXCL_LINE\n        }\n\n        return static_cast<size_type>(res);\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", BasicJsonType()));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n  private:\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        auto* result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    result = &result->operator[](array_index(reference_token));\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\", j));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums || reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        ptr = &ptr->operator[](array_index(reference_token));\n                    }\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\", *ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402, \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) + \") is out of range\", *ptr));\n                    }\n\n                    // use unchecked array access\n                    ptr = &ptr->operator[](array_index(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\", *ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    bool contains(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (!ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !(\"0\" <= reference_token && reference_token <= \"9\")))\n                    {\n                        // invalid char\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))\n                        {\n                            // first char should be between '1' and '9'\n                            return false;\n                        }\n                        for (std::size_t i = 1; i < reference_token.size(); i++)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))\n                            {\n                                // other char should be between '0' and '9'\n                                return false;\n                            }\n                        }\n                    }\n\n                    const auto idx = array_index(reference_token);\n                    if (idx >= ptr->size())\n                    {\n                        // index out of range\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](idx);\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<std::string> split(const std::string& reference_string)\n    {\n        std::vector<std::string> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1, \"JSON pointer must be empty or begin with '/' - was: '\" + reference_string + \"'\", BasicJsonType()));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == std::string::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == std::string::npos)\n            start = (slash == std::string::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != std::string::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                JSON_ASSERT(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||\n                                         (reference_token[pos + 1] != '0' &&\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\", BasicJsonType()));\n                }\n            }\n\n            // finally, store the reference token\n            detail::unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n  private:\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    static void flatten(const std::string& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(reference_string + \"/\" + std::to_string(i),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(reference_string + \"/\" + detail::escape(element.first), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::null:\n            case detail::value_t::string:\n            case detail::value_t::boolean:\n            case detail::value_t::number_integer:\n            case detail::value_t::number_unsigned:\n            case detail::value_t::number_float:\n            case detail::value_t::binary:\n            case detail::value_t::discarded:\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\", value));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\", element.second));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief compares two JSON pointers for equality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is equal to @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator==(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return lhs.reference_tokens == rhs.reference_tokens;\n    }\n\n    /*!\n    @brief compares two JSON pointers for inequality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is not equal @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator!=(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /// the reference tokens\n    std::vector<std::string> reference_tokens;\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/json_ref.hpp>\n\n\n#include <initializer_list>\n#include <utility>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value))\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(&value)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...)\n    {}\n\n    // class should be movable only\n    json_ref(json_ref&&) noexcept = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (value_ref == nullptr)\n        {\n            return std::move(owned_value);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return value_ref ? *value_ref : owned_value;\n    }\n\n    value_type const* operator->() const\n    {\n        return &** this;\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type const* value_ref = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <cmath> // isnan, isinf\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n#include <string> // string\n#include <utility> // move\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <string> // basic_string\n#include <vector> // vector\n\n#ifndef JSON_NO_IO\n    #include <ios>      // streamsize\n    #include <ostream>  // basic_ostream\n#endif  // JSON_NO_IO\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n\n    output_adapter_protocol() = default;\n    output_adapter_protocol(const output_adapter_protocol&) = default;\n    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;\n    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;\n    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType, typename AllocatorType = std::allocator<CharType>>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        std::copy(s, s + length, std::back_inserter(v));\n    }\n\n  private:\n    std::vector<CharType, AllocatorType>& v;\n};\n\n#ifndef JSON_NO_IO\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n#endif  // JSON_NO_IO\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    template<typename AllocatorType = std::allocator<CharType>>\n    output_adapter(std::vector<CharType, AllocatorType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}\n\n#ifndef JSON_NO_IO\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n#endif  // JSON_NO_IO\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))\n    {\n        JSON_ASSERT(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::array:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                JSON_THROW(type_error::create(317, \"to serialize to BSON, top-level type must be object, but is \" + std::string(j.type_name()), j));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<std::uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<std::uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<std::uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<std::uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                if (std::isnan(j.m_value.number_float))\n                {\n                    // NaN is 0xf97e00 in CBOR\n                    oa->write_character(to_char_type(0xF9));\n                    oa->write_character(to_char_type(0x7E));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else if (std::isinf(j.m_value.number_float))\n                {\n                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00\n                    oa->write_character(to_char_type(0xf9));\n                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else\n                {\n                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);\n                }\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (j.m_value.binary->has_subtype())\n                {\n                    if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xd8));\n                        write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xd9));\n                        write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xda));\n                        write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xdb));\n                        write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));\n                    }\n                }\n\n                // step 1: write control byte and the binary array size\n                const auto N = j.m_value.binary->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x40 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x58));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x59));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<std::uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<std::uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                // step 0: determine if the binary type has a set subtype to\n                // determine whether or not to use the ext or fixext types\n                const bool use_ext = j.m_value.binary->has_subtype();\n\n                // step 1: write control byte and the byte string length\n                const auto N = j.m_value.binary->size();\n                if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    std::uint8_t output_type{};\n                    bool fixed = true;\n                    if (use_ext)\n                    {\n                        switch (N)\n                        {\n                            case 1:\n                                output_type = 0xD4; // fixext 1\n                                break;\n                            case 2:\n                                output_type = 0xD5; // fixext 2\n                                break;\n                            case 4:\n                                output_type = 0xD6; // fixext 4\n                                break;\n                            case 8:\n                                output_type = 0xD7; // fixext 8\n                                break;\n                            case 16:\n                                output_type = 0xD8; // fixext 16\n                                break;\n                            default:\n                                output_type = 0xC7; // ext 8\n                                fixed = false;\n                                break;\n                        }\n\n                    }\n                    else\n                    {\n                        output_type = 0xC4; // bin 8\n                        fixed = false;\n                    }\n\n                    oa->write_character(to_char_type(output_type));\n                    if (!fixed)\n                    {\n                        write_number(static_cast<std::uint8_t>(N));\n                    }\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC8 // ext 16\n                                               : 0xC5; // bin 16\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC9 // ext 32\n                                               : 0xC6; // bin 32\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 1.5: if this is an ext type, write the subtype\n                if (use_ext)\n                {\n                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));\n                }\n\n                // step 2: write the byte string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.array->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                if (use_type && !j.m_value.binary->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    oa->write_character(to_char_type('$'));\n                    oa->write_character('U');\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true);\n                }\n\n                if (use_type)\n                {\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                        j.m_value.binary->size());\n                }\n                else\n                {\n                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)\n                    {\n                        oa->write_character(to_char_type('U'));\n                        oa->write_character(j.m_value.binary->data()[i]);\n                    }\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.object->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409, \"BSON key cannot contain code point U+0000 (at byte \" + std::to_string(it) + \")\", j));\n            static_cast<void>(j);\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double, true>(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const BasicJsonType& j)\n    {\n        if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.number_unsigned));\n        }\n        else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_unsigned));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(j.m_value.number_unsigned) + \" cannot be represented by BSON as it does not fit int64\", j));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t array_index = 0ul;\n\n        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)\n        {\n            return result + calc_bson_element_size(std::to_string(array_index++), el);\n        });\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @return The size of the BSON-encoded binary array @a value\n    */\n    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and binary value @a value\n    */\n    void write_bson_binary(const string_t& name,\n                           const binary_t& value)\n    {\n        write_bson_entry_header(name, 0x05);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size()));\n        write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : std::uint8_t(0x00));\n\n        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name, j);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::binary:\n                return header_size + calc_bson_binary_size(*j.m_value.binary);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            case value_t::discarded:\n            default:\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)\n                return 0ul;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::binary:\n                return write_bson_binary(name, *j.m_value.binary);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            case value_t::discarded:\n            default:\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)\n                return;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] value  JSON value to serialize\n    @pre       value.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0),\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] value  JSON value to serialize\n    @pre       value.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= (std::numeric_limits<std::uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template < typename NumberType, typename std::enable_if <\n                   std::is_signed<NumberType>::value&&\n                   !std::is_floating_point<NumberType>::value, int >::type = 0 >\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::int8_t>(n));\n        }\n        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n    */\n    CharType ubjson_prefix(const BasicJsonType& j) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                {\n                    return 'l';\n                }\n                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n                {\n                    return 'I';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n                {\n                    return 'l';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array: // fallthrough\n            case value_t::binary:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            case value_t::discarded:\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @tparam NumberType the type of the number\n    @tparam OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n\n    @note This function needs to respect the system's endianess, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool OutputIsLittleEndian = false>\n    void write_number(const NumberType n)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec{};\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian != OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n    void write_compact_float(const number_float_t n, detail::input_format_t format)\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&\n                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&\n                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(static_cast<float>(n))\n                                : get_msgpack_float_prefix(static_cast<float>(n)));\n            write_number(static_cast<float>(n));\n        }\n        else\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(n)\n                                : get_msgpack_float_prefix(n));\n            write_number(n);\n        }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_trivial<CharType>::value, \"CharType must be trivial\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value &&\n                   std::is_signed<char>::value &&\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/output/serializer.hpp>\n\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string, char_traits\n#include <iomanip> // setfill, setw\n#include <sstream> // stringstream\n#include <type_traits> // is_same\n#include <utility> // move\n\n// #include <nlohmann/detail/conversions/to_chars.hpp>\n\n\n#include <array> // array\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate<typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        JSON_ASSERT(x.e == y.e);\n        JSON_ASSERT(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        JSON_ASSERT(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        JSON_ASSERT(delta >= 0);\n        JSON_ASSERT(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate<typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 && E > 1;\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    JSON_ASSERT(e >= -1500);\n    JSON_ASSERT(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    JSON_ASSERT(index >= 0);\n    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    JSON_ASSERT(kAlpha <= cached.e + e + 64);\n    JSON_ASSERT(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n\n    pow10 = 1;\n    return 1;\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    JSON_ASSERT(len >= 1);\n    JSON_ASSERT(dist <= delta);\n    JSON_ASSERT(rest <= delta);\n    JSON_ASSERT(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            && delta - rest >= ten_k\n            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))\n    {\n        JSON_ASSERT(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    JSON_ASSERT(M_plus.e >= kAlpha);\n    JSON_ASSERT(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    JSON_ASSERT(p1 > 0);\n\n    std::uint32_t pow10{};\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    JSON_ASSERT(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    JSON_ASSERT(m_plus.e == m_minus.e);\n    JSON_ASSERT(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    JSON_ASSERT(e > -1000);\n    JSON_ASSERT(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    JSON_ASSERT(min_exp < 0);\n    JSON_ASSERT(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n && n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (static_cast<size_t>(n) + 2);\n    }\n\n    if (0 < n && n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        JSON_ASSERT(k > n);\n\n        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));\n        buf[n] = '.';\n        return buf + (static_cast<size_t>(k) + 1U);\n    }\n\n    if (min_exp < n && n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);\n        buf[1] = '.';\n        buf += 1 + static_cast<size_t>(k);\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n} // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    JSON_ASSERT(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    JSON_ASSERT(last - first >= kMaxExp + 2);\n    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using binary_char_t = typename BasicJsonType::binary_t::value_type;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n    - binary values are serialized as objects containing the subtype and the\n      byte array\n\n    @param[in] val               value to serialize\n    @param[in] pretty_print      whether the output shall be pretty-printed\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] indent_step       the indent level\n    @param[in] current_indent    the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val,\n              const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::binary:\n            {\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"bytes\\\": [\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_characters(\", \", 2);\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\n\", 3);\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"subtype\\\": \", 11);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                    }\n                    else\n                    {\n                        o->write_characters(\"null\", 4);\n                    }\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_characters(\"{\\\"bytes\\\":[\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_character(',');\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\\"subtype\\\":\", 12);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                        o->write_character('}');\n                    }\n                    else\n                    {\n                        o->write_characters(\"null}\", 5);\n                    }\n                }\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint{};\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<std::uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    (std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(codepoint));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    (std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                    static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            std::stringstream ss;\n                            ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0);\n                            JSON_THROW(type_error::create(316, \"invalid UTF-8 byte at index \" + std::to_string(i) + \": 0x\" + ss.str(), BasicJsonType()));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (!ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    std::stringstream ss;\n                    ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast<std::uint8_t>(s.back()) | 0);\n                    JSON_THROW(type_error::create(316, \"incomplete UTF-8 string; last byte: 0x\" + ss.str(), BasicJsonType()));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        }\n    }\n\n  private:\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    inline unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template < typename NumberType, detail::enable_if_t <\n                   std::is_integral<NumberType>::value ||\n                   std::is_same<NumberType, number_unsigned_t>::value ||\n                   std::is_same<NumberType, number_integer_t>::value ||\n                   std::is_same<NumberType, binary_char_t>::value,\n                   int > = 0 >\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n\n        const bool is_negative = std::is_signed<NumberType>::value && !(x >= 0); // see issue #755\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars{};\n\n        if (is_negative)\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(static_cast<number_integer_t>(x));\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        JSON_ASSERT(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward\n        // so we later avoid reversing the result\n        buffer_ptr += n_chars;\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (!std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||\n              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        auto* begin = number_buffer.data();\n        auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        JSON_ASSERT(len > 0);\n        // check if buffer was large enough\n        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            JSON_ASSERT((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' && decimal_point != '.')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' || c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        JSON_ASSERT(byte < utf8d.size());\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);\n        JSON_ASSERT(index < 400);\n        state = utf8d[index];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as unsigned integer. The plus/minus shuffling is necessary as we can\n     * not directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    inline number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n\n// #include <nlohmann/ordered_map.hpp>\n\n\n#include <functional> // less\n#include <initializer_list> // initializer_list\n#include <iterator> // input_iterator_tag, iterator_traits\n#include <memory> // allocator\n#include <stdexcept> // for out_of_range\n#include <type_traits> // enable_if, is_convertible\n#include <utility> // pair\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n\n/// ordered_map: a minimal map-like container that preserves insertion order\n/// for use within nlohmann::basic_json<ordered_map>\ntemplate <class Key, class T, class IgnoredLess = std::less<Key>,\n          class Allocator = std::allocator<std::pair<const Key, T>>>\n                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>\n{\n    using key_type = Key;\n    using mapped_type = T;\n    using Container = std::vector<std::pair<const Key, T>, Allocator>;\n    using typename Container::iterator;\n    using typename Container::const_iterator;\n    using typename Container::size_type;\n    using typename Container::value_type;\n\n    // Explicit constructors instead of `using Container::Container`\n    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)\n    ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {}\n    template <class It>\n    ordered_map(It first, It last, const Allocator& alloc = Allocator())\n        : Container{first, last, alloc} {}\n    ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )\n        : Container{init, alloc} {}\n\n    std::pair<iterator, bool> emplace(const key_type& key, T&& t)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return {it, false};\n            }\n        }\n        Container::emplace_back(key, t);\n        return {--this->end(), true};\n    }\n\n    T& operator[](const Key& key)\n    {\n        return emplace(key, T{}).first->second;\n    }\n\n    const T& operator[](const Key& key) const\n    {\n        return at(key);\n    }\n\n    T& at(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    const T& at(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    size_type erase(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                // Since we cannot move const Keys, re-construct them in place\n                for (auto next = it; ++next != this->end(); ++it)\n                {\n                    it->~value_type(); // Destroy but keep allocation\n                    new (&*it) value_type{std::move(*next)};\n                }\n                Container::pop_back();\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator erase(iterator pos)\n    {\n        auto it = pos;\n\n        // Since we cannot move const Keys, re-construct them in place\n        for (auto next = it; ++next != this->end(); ++it)\n        {\n            it->~value_type(); // Destroy but keep allocation\n            new (&*it) value_type{std::move(*next)};\n        }\n        Container::pop_back();\n        return pos;\n    }\n\n    size_type count(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator find(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    const_iterator find(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    std::pair<iterator, bool> insert( value_type&& value )\n    {\n        return emplace(value.first, std::move(value.second));\n    }\n\n    std::pair<iterator, bool> insert( const value_type& value )\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == value.first)\n            {\n                return {it, false};\n            }\n        }\n        Container::push_back(value);\n        return {--this->end(), true};\n    }\n\n    template<typename InputIt>\n    using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,\n            std::input_iterator_tag>::value>::type;\n\n    template<typename InputIt, typename = require_input_iter<InputIt>>\n    void insert(InputIt first, InputIt last)\n    {\n        for (auto it = first; it != last; ++it)\n        {\n            insert(*it);\n        }\n    }\n};\n\n}  // namespace nlohmann\n\n\n#if defined(JSON_HAS_CPP_17)\n    #include <string_view>\n#endif\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n\n/*!\n@brief a class to store JSON values\n\n@tparam ObjectType type for JSON objects (`std::map` by default; will be used\nin @ref object_t)\n@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used\nin @ref array_t)\n@tparam StringType type for JSON strings and object keys (`std::string` by\ndefault; will be used in @ref string_t)\n@tparam BooleanType type for JSON booleans (`bool` by default; will be used\nin @ref boolean_t)\n@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by\ndefault; will be used in @ref number_integer_t)\n@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c\n`uint64_t` by default; will be used in @ref number_unsigned_t)\n@tparam NumberFloatType type for JSON floating-point numbers (`double` by\ndefault; will be used in @ref number_float_t)\n@tparam BinaryType type for packed binary data for compatibility with binary\nserialization formats (`std::vector<std::uint8_t>` by default; will be used in\n@ref binary_t)\n@tparam AllocatorType type of the allocator to use (`std::allocator` by\ndefault)\n@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`\nand `from_json()` (@ref adl_serializer by default)\n\n@requirement The class satisfies the following concept requirements:\n- Basic\n - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):\n   JSON values can be default constructed. The result will be a JSON null\n   value.\n - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):\n   A JSON value can be constructed from an rvalue argument.\n - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):\n   A JSON value can be copy-constructed from an lvalue expression.\n - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):\n   A JSON value van be assigned from an rvalue argument.\n - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):\n   A JSON value can be copy-assigned from an lvalue expression.\n - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):\n   JSON values can be destructed.\n- Layout\n - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):\n   JSON values have\n   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):\n   All non-static data members are private and standard layout types, the\n   class has no virtual functions or (virtual) base classes.\n- Library-wide\n - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):\n   JSON values can be compared with `==`, see @ref\n   operator==(const_reference,const_reference).\n - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):\n   JSON values can be compared with `<`, see @ref\n   operator<(const_reference,const_reference).\n - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):\n   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of\n   other compatible types, using unqualified function call @ref swap().\n - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):\n   JSON values can be compared against `std::nullptr_t` objects which are used\n   to model the `null` value.\n- Container\n - [Container](https://en.cppreference.com/w/cpp/named_req/Container):\n   JSON values can be used like STL containers and provide iterator access.\n - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);\n   JSON values can be used like STL containers and provide reverse iterator\n   access.\n\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@internal\n@note ObjectType trick from https://stackoverflow.com/a/9860911\n@endinternal\n\n@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange\nFormat](https://tools.ietf.org/html/rfc8259)\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n    friend ::nlohmann::json_pointer<basic_json>;\n\n    template<typename BasicJsonType, typename InputType>\n    friend class ::nlohmann::detail::parser;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename InputType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n    friend class ::nlohmann::detail::exception;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer_base<basic_json>;\n\n    template<typename InputAdapterType>\n    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(\n        InputAdapterType adapter,\n        detail::parser_callback_t<basic_json>cb = nullptr,\n        const bool allow_exceptions = true,\n        const bool ignore_comments = false\n                                 )\n    {\n        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),\n                std::move(cb), allow_exceptions, ignore_comments);\n    }\n\n  private:\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    template<typename InputType>\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<basic_json>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// how to treat CBOR tags\n    using cbor_tag_handler_t = detail::cbor_tag_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    /// @copydoc detail::exception\n    using exception = detail::exception;\n    /// @copydoc detail::parse_error\n    using parse_error = detail::parse_error;\n    /// @copydoc detail::invalid_iterator\n    using invalid_iterator = detail::invalid_iterator;\n    /// @copydoc detail::type_error\n    using type_error = detail::type_error;\n    /// @copydoc detail::out_of_range\n    using out_of_range = detail::out_of_range;\n    /// @copydoc detail::other_error\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /*!\n    @brief returns the allocator associated with the container\n    */\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /*!\n    @brief returns version information on the library\n\n    This function returns a JSON object with information about the library,\n    including the version number and information on the platform and compiler.\n\n    @return JSON object holding version information\n    key         | description\n    ----------- | ---------------\n    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).\n    `copyright` | The copyright line for the library as string.\n    `name`      | The name of the library as string.\n    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.\n    `url`       | The URL of the project as string.\n    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).\n\n    @liveexample{The following code shows an example output of the `meta()`\n    function.,meta}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @complexity Constant.\n\n    @since 2.1.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2021 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_PATCH);\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", std::to_string(__GNUC__) + \".\" + std::to_string(__GNUC_MINOR__) + \".\" + std::to_string(__GNUC_PATCHLEVEL__)}};\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#ifdef __cplusplus\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n#if defined(JSON_HAS_CPP_14)\n    // Use transparent comparator if possible, combined with perfect forwarding\n    // on find() and count() calls prevents unnecessary string construction.\n    using object_comparator_t = std::less<>;\n#else\n    using object_comparator_t = std::less<StringType>;\n#endif\n\n    /*!\n    @brief a type for an object\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows:\n    > An object is an unordered collection of zero or more name/value pairs,\n    > where a name is a string and a value is a string, number, boolean, null,\n    > object, or array.\n\n    To store objects in C++, a type is defined by the template parameters\n    described below.\n\n    @tparam ObjectType  the container to store objects (e.g., `std::map` or\n    `std::unordered_map`)\n    @tparam StringType the type of the keys or names (e.g., `std::string`).\n    The comparison function `std::less<StringType>` is used to order elements\n    inside the container.\n    @tparam AllocatorType the allocator to use for objects (e.g.,\n    `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ObjectType (`std::map`), @a StringType\n    (`std::string`), and @a AllocatorType (`std::allocator`), the default\n    value for @a object_t is:\n\n    @code {.cpp}\n    std::map<\n      std::string, // key_type\n      basic_json, // value_type\n      std::less<std::string>, // key_compare\n      std::allocator<std::pair<const std::string, basic_json>> // allocator_type\n    >\n    @endcode\n\n    #### Behavior\n\n    The choice of @a object_t influences the behavior of the JSON class. With\n    the default type, objects have the following behavior:\n\n    - When all names are unique, objects will be interoperable in the sense\n      that all software implementations receiving that object will agree on\n      the name-value mappings.\n    - When the names within an object are not unique, it is unspecified which\n      one of the values for a given key will be chosen. For instance,\n      `{\"key\": 2, \"key\": 1}` could be equal to either `{\"key\": 1}` or\n      `{\"key\": 2}`.\n    - Internally, name/value pairs are stored in lexicographical order of the\n      names. Objects will also be serialized (see @ref dump) in this order.\n      For instance, `{\"b\": 1, \"a\": 2}` and `{\"a\": 2, \"b\": 1}` will be stored\n      and serialized as `{\"a\": 2, \"b\": 1}`.\n    - When comparing objects, the order of the name/value pairs is irrelevant.\n      This makes objects interoperable in the sense that they will not be\n      affected by these differences. For instance, `{\"b\": 1, \"a\": 2}` and\n      `{\"a\": 2, \"b\": 1}` will be treated as equal.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the object's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON object.\n\n    #### Storage\n\n    Objects are stored as pointers in a @ref basic_json type. That is, for any\n    access to object values, a pointer of type `object_t*` must be\n    dereferenced.\n\n    @sa see @ref array_t -- type for an array value\n\n    @since version 1.0.0\n\n    @note The order name/value pairs are added to the object is *not*\n    preserved by the library. Therefore, iterating an object may return\n    name/value pairs in a different order than they were originally stored. In\n    fact, keys will be traversed in alphabetical order as `std::map` with\n    `std::less` is used by default. Please note this behavior conforms to [RFC\n    8259](https://tools.ietf.org/html/rfc8259), because any order implements the\n    specified \"unordered\" nature of JSON objects.\n    */\n    using object_t = ObjectType<StringType,\n          basic_json,\n          object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /*!\n    @brief a type for an array\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows:\n    > An array is an ordered sequence of zero or more values.\n\n    To store objects in C++, a type is defined by the template parameters\n    explained below.\n\n    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or\n    `std::list`)\n    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ArrayType (`std::vector`) and @a\n    AllocatorType (`std::allocator`), the default value for @a array_t is:\n\n    @code {.cpp}\n    std::vector<\n      basic_json, // value_type\n      std::allocator<basic_json> // allocator_type\n    >\n    @endcode\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the array's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON array.\n\n    #### Storage\n\n    Arrays are stored as pointers in a @ref basic_json type. That is, for any\n    access to array values, a pointer of type `array_t*` must be dereferenced.\n\n    @sa see @ref object_t -- type for an object value\n\n    @since version 1.0.0\n    */\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /*!\n    @brief a type for a string\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows:\n    > A string is a sequence of zero or more Unicode characters.\n\n    To store objects in C++, a type is defined by the template parameter\n    described below. Unicode values are split by the JSON class into\n    byte-sized characters during deserialization.\n\n    @tparam StringType  the container to store strings (e.g., `std::string`).\n    Note this container is used for keys/names in objects, see @ref object_t.\n\n    #### Default type\n\n    With the default values for @a StringType (`std::string`), the default\n    value for @a string_t is:\n\n    @code {.cpp}\n    std::string\n    @endcode\n\n    #### Encoding\n\n    Strings are stored in UTF-8 encoding. Therefore, functions like\n    `std::string::size()` or `std::string::length()` return the number of\n    bytes in the string rather than the number of characters or glyphs.\n\n    #### String comparison\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) states:\n    > Software implementations are typically required to test names of object\n    > members for equality. Implementations that transform the textual\n    > representation into sequences of Unicode code units and then perform the\n    > comparison numerically, code unit by code unit, are interoperable in the\n    > sense that implementations will agree in all cases on equality or\n    > inequality of two strings. For example, implementations that compare\n    > strings with escaped characters unconverted may incorrectly find that\n    > `\"a\\\\b\"` and `\"a\\u005Cb\"` are not equal.\n\n    This implementation is interoperable as it does compare strings code unit\n    by code unit.\n\n    #### Storage\n\n    String values are stored as pointers in a @ref basic_json type. That is,\n    for any access to string values, a pointer of type `string_t*` must be\n    dereferenced.\n\n    @since version 1.0.0\n    */\n    using string_t = StringType;\n\n    /*!\n    @brief a type for a boolean\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a\n    type which differentiates the two literals `true` and `false`.\n\n    To store objects in C++, a type is defined by the template parameter @a\n    BooleanType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a BooleanType (`bool`), the default value for\n    @a boolean_t is:\n\n    @code {.cpp}\n    bool\n    @endcode\n\n    #### Storage\n\n    Boolean values are stored directly inside a @ref basic_json type.\n\n    @since version 1.0.0\n    */\n    using boolean_t = BooleanType;\n\n    /*!\n    @brief a type for a number (integer)\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store integer numbers in C++, a type is defined by the template\n    parameter @a NumberIntegerType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberIntegerType (`int64_t`), the default\n    value for @a number_integer_t is:\n\n    @code {.cpp}\n    int64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number\n    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers\n    that are out of range will yield over/underflow when used in a\n    constructor. During deserialization, too large or small integer numbers\n    will be automatically be stored as @ref number_unsigned_t or @ref\n    number_float_t.\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange of the exactly supported range [INT64_MIN,\n    INT64_MAX], this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa see @ref number_float_t -- type for number values (floating-point)\n\n    @sa see @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_integer_t = NumberIntegerType;\n\n    /*!\n    @brief a type for a number (unsigned)\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store unsigned integer numbers in C++, a type is defined by the\n    template parameter @a NumberUnsignedType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberUnsignedType (`uint64_t`), the\n    default value for @a number_unsigned_t is:\n\n    @code {.cpp}\n    uint64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer\n    number that can be stored is `0`. Integer numbers that are out of range\n    will yield over/underflow when used in a constructor. During\n    deserialization, too large or small integer numbers will be automatically\n    be stored as @ref number_integer_t or @ref number_float_t.\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange (when considered in conjunction with the\n    number_integer_t type) of the exactly supported range [0, UINT64_MAX],\n    this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa see @ref number_float_t -- type for number values (floating-point)\n    @sa see @ref number_integer_t -- type for number values (integer)\n\n    @since version 2.0.0\n    */\n    using number_unsigned_t = NumberUnsignedType;\n\n    /*!\n    @brief a type for a number (floating-point)\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store floating-point numbers in C++, a type is defined by the template\n    parameter @a NumberFloatType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberFloatType (`double`), the default\n    value for @a number_float_t is:\n\n    @code {.cpp}\n    double\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in floating-point literals will be ignored. Internally,\n      the value will be stored as decimal number. For instance, the C++\n      floating-point literal `01.2` will be serialized to `1.2`. During\n      deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) states:\n    > This specification allows implementations to set limits on the range and\n    > precision of numbers accepted. Since software that implements IEEE\n    > 754-2008 binary64 (double precision) numbers is generally available and\n    > widely used, good interoperability can be achieved by implementations\n    > that expect no more precision or range than these provide, in the sense\n    > that implementations will approximate JSON numbers within the expected\n    > precision.\n\n    This implementation does exactly follow this approach, as it uses double\n    precision floating-point numbers. Note values smaller than\n    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`\n    will be stored as NaN internally and be serialized to `null`.\n\n    #### Storage\n\n    Floating-point number values are stored directly inside a @ref basic_json\n    type.\n\n    @sa see @ref number_integer_t -- type for number values (integer)\n\n    @sa see @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_float_t = NumberFloatType;\n\n    /*!\n    @brief a type for a packed binary type\n\n    This type is a type designed to carry binary data that appears in various\n    serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and\n    BSON's generic binary subtype. This type is NOT a part of standard JSON and\n    exists solely for compatibility with these binary types. As such, it is\n    simply defined as an ordered sequence of zero or more byte values.\n\n    Additionally, as an implementation detail, the subtype of the binary data is\n    carried around as a `std::uint8_t`, which is compatible with both of the\n    binary data formats that use binary subtyping, (though the specific\n    numbering is incompatible with each other, and it is up to the user to\n    translate between them).\n\n    [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type\n    as:\n    > Major type 2: a byte string. The string's length in bytes is represented\n    > following the rules for positive integers (major type 0).\n\n    [MessagePack's documentation on the bin type\n    family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family)\n    describes this type as:\n    > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes\n    > in addition to the size of the byte array.\n\n    [BSON's specifications](http://bsonspec.org/spec.html) describe several\n    binary types; however, this type is intended to represent the generic binary\n    type which has the description:\n    > Generic binary subtype - This is the most commonly used binary subtype and\n    > should be the 'default' for drivers and tools.\n\n    None of these impose any limitations on the internal representation other\n    than the basic unit of storage be some type of array whose parts are\n    decomposable into bytes.\n\n    The default representation of this binary format is a\n    `std::vector<std::uint8_t>`, which is a very common way to represent a byte\n    array in modern C++.\n\n    #### Default type\n\n    The default values for @a BinaryType is `std::vector<std::uint8_t>`\n\n    #### Storage\n\n    Binary Arrays are stored as pointers in a @ref basic_json type. That is,\n    for any access to array values, a pointer of the type `binary_t*` must be\n    dereferenced.\n\n    #### Notes on subtypes\n\n    - CBOR\n       - Binary values are represented as byte strings. Subtypes are serialized\n         as tagged values.\n    - MessagePack\n       - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,\n         or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)\n         is used. For other sizes, the ext family (ext8, ext16, ext32) is used.\n         The subtype is then added as singed 8-bit integer.\n       - If no subtype is given, the bin family (bin8, bin16, bin32) is used.\n    - BSON\n       - If a subtype is given, it is used and added as unsigned 8-bit integer.\n       - If no subtype is given, the generic binary subtype 0x00 is used.\n\n    @sa see @ref binary -- create a binary array\n\n    @since version 3.8.0\n    */\n    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * obj)\n        {\n            AllocatorTraits::deallocate(alloc, obj, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);\n        JSON_ASSERT(obj != nullptr);\n        return obj.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    binary    | binary          | pointer to @ref binary_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// binary (stored with pointer to save storage)\n        binary_t* binary;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    binary = create<binary_t>();\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = boolean_t(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = number_integer_t(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = number_unsigned_t(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = number_float_t(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                case value_t::discarded:\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4\", basic_json())); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value) : string(create<string_t>(value)) {}\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}\n\n        /// constructor for objects\n        json_value(const object_t& value) : object(create<object_t>(value)) {}\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}\n\n        /// constructor for arrays\n        json_value(const array_t& value) : array(create<array_t>(value)) {}\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}\n\n        /// constructor for binary arrays\n        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays\n        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        /// constructor for binary arrays (internal type)\n        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays (internal type)\n        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        void destroy(value_t t)\n        {\n            if (t == value_t::array || t == value_t::object)\n            {\n                // flatten the current json_value to a heap-allocated stack\n                std::vector<basic_json> stack;\n\n                // move the top-level items to stack\n                if (t == value_t::array)\n                {\n                    stack.reserve(array->size());\n                    std::move(array->begin(), array->end(), std::back_inserter(stack));\n                }\n                else\n                {\n                    stack.reserve(object->size());\n                    for (auto&& it : *object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n                }\n\n                while (!stack.empty())\n                {\n                    // move the last item to local variable to be processed\n                    basic_json current_item(std::move(stack.back()));\n                    stack.pop_back();\n\n                    // if current_item is array/object, move\n                    // its children to the stack to be processed later\n                    if (current_item.is_array())\n                    {\n                        std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));\n\n                        current_item.m_value.array->clear();\n                    }\n                    else if (current_item.is_object())\n                    {\n                        for (auto&& it : *current_item.m_value.object)\n                        {\n                            stack.push_back(std::move(it.second));\n                        }\n\n                        current_item.m_value.object->clear();\n                    }\n\n                    // it's now safe that current_item get destructed\n                    // since it doesn't have any children\n                }\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);\n                    break;\n                }\n\n                case value_t::null:\n                case value_t::boolean:\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                case value_t::discarded:\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n  private:\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n\n    Furthermore, the parent relation is checked for arrays and objects: If\n    @a check_parents true and the value is an array or object, then the\n    container's elements must have the current value as parent.\n\n    @param[in] check_parents  whether the parent relation should be checked.\n               The value is true by default and should only be set to false\n               during destruction of objects when the invariant does not\n               need to hold.\n    */\n    void assert_invariant(bool check_parents = true) const noexcept\n    {\n        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);\n        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);\n        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);\n        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);\n\n#if JSON_DIAGNOSTICS\n        JSON_TRY\n        {\n            // cppcheck-suppress assertWithSideEffect\n            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)\n            {\n                return j.m_parent == this;\n            }));\n        }\n        JSON_CATCH(...) {} // LCOV_EXCL_LINE\n#endif\n        static_cast<void>(check_parents);\n    }\n\n    void set_parents()\n    {\n#if JSON_DIAGNOSTICS\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                for (auto& element : *m_value.array)\n                {\n                    element.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                for (auto& element : *m_value.object)\n                {\n                    element.second.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n#endif\n    }\n\n    iterator set_parents(iterator it, typename iterator::difference_type count)\n    {\n#if JSON_DIAGNOSTICS\n        for (typename iterator::difference_type i = 0; i < count; ++i)\n        {\n            (it + i)->m_parent = this;\n        }\n#else\n        static_cast<void>(count);\n#endif\n        return it;\n    }\n\n    reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1))\n    {\n#if JSON_DIAGNOSTICS\n        if (old_capacity != std::size_t(-1))\n        {\n            // see https://github.com/nlohmann/json/issues/2838\n            JSON_ASSERT(type() == value_t::array);\n            if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))\n            {\n                // capacity has changed: update all parents\n                set_parents();\n                return j;\n            }\n        }\n\n        // ordered_json uses a vector internally, so pointers could have\n        // been invalidated; see https://github.com/nlohmann/json/issues/2962\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning(push )\n#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr\n#endif\n        if (detail::is_ordered_map<object_t>::value)\n        {\n            set_parents();\n            return j;\n        }\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning( pop )\n#endif\n\n        j.m_parent = this;\n#else\n        static_cast<void>(j);\n        static_cast<void>(old_capacity);\n#endif\n        return j;\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /*!\n    @brief parser event types\n\n    The parser callback distinguishes the following events:\n    - `object_start`: the parser read `{` and started to process a JSON object\n    - `key`: the parser read a key of a value in an object\n    - `object_end`: the parser read `}` and finished processing a JSON object\n    - `array_start`: the parser read `[` and started to process a JSON array\n    - `array_end`: the parser read `]` and finished processing a JSON array\n    - `value`: the parser finished reading a JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    @sa see @ref parser_callback_t for more information and examples\n    */\n    using parse_event_t = detail::parse_event_t;\n\n    /*!\n    @brief per-element parser callback type\n\n    With a parser callback function, the result of parsing a JSON text can be\n    influenced. When passed to @ref parse, it is called on certain events\n    (passed as @ref parse_event_t via parameter @a event) with a set recursion\n    depth @a depth and context JSON value @a parsed. The return value of the\n    callback function is a boolean indicating whether the element that emitted\n    the callback shall be kept or not.\n\n    We distinguish six scenarios (determined by the event type) in which the\n    callback function can be called. The following table describes the values\n    of the parameters @a depth, @a event, and @a parsed.\n\n    parameter @a event | description | parameter @a depth | parameter @a parsed\n    ------------------ | ----------- | ------------------ | -------------------\n    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded\n    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key\n    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object\n    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded\n    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array\n    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    Discarding a value (i.e., returning `false`) has different effects\n    depending on the context in which function was called:\n\n    - Discarded values in structured types are skipped. That is, the parser\n      will behave as if the discarded value was never read.\n    - In case a value outside a structured type is skipped, it is replaced\n      with `null`. This case happens if the top-level element is skipped.\n\n    @param[in] depth  the depth of the recursion during parsing\n\n    @param[in] event  an event of type parse_event_t indicating the context in\n    the callback function has been called\n\n    @param[in,out] parsed  the current intermediate parse result; note that\n    writing to this value has no effect for parse_event_t::key events\n\n    @return Whether the JSON value which called the function during parsing\n    should be kept (`true`) or not (`false`). In the latter case, it is either\n    skipped completely or replaced by an empty discarded object.\n\n    @sa see @ref parse for examples\n\n    @since version 1.0.0\n    */\n    using parser_callback_t = detail::parser_callback_t<basic_json>;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /*!\n    @brief create an empty value with a given type\n\n    Create an empty JSON value with a given type. The value will be default\n    initialized with an empty value which depends on the type:\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n    binary      | empty array\n\n    @param[in] v  the type of the value to create\n\n    @complexity Constant.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows the constructor for different @ref\n    value_t values,basic_json__value_t}\n\n    @sa see @ref clear() -- restores the postcondition of this constructor\n\n    @since version 1.0.0\n    */\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a null object\n\n    Create a `null` JSON value. It either takes a null pointer as parameter\n    (explicitly creating `null`) or no parameter (implicitly creating `null`).\n    The passed null pointer itself is not read -- it is only used to choose\n    the right constructor.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @liveexample{The following code shows the constructor with and without a\n    null pointer parameter.,basic_json__nullptr_t}\n\n    @since version 1.0.0\n    */\n    basic_json(std::nullptr_t = nullptr) noexcept\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value\n\n    This is a \"catch all\" constructor for all compatible JSON types; that is,\n    types for which a `to_json()` method exists. The constructor forwards the\n    parameter @a val to that method (to `json_serializer<U>::to_json` method\n    with `U = uncvref_t<CompatibleType>`, to be exact).\n\n    Template type @a CompatibleType includes, but is not limited to, the\n    following types:\n    - **arrays**: @ref array_t and all kinds of compatible containers such as\n      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,\n      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,\n      `std::multiset`, and `std::unordered_multiset` with a `value_type` from\n      which a @ref basic_json value can be constructed.\n    - **objects**: @ref object_t and all kinds of compatible associative\n      containers such as `std::map`, `std::unordered_map`, `std::multimap`,\n      and `std::unordered_multimap` with a `key_type` compatible to\n      @ref string_t and a `value_type` from which a @ref basic_json value can\n      be constructed.\n    - **strings**: @ref string_t, string literals, and all compatible string\n      containers can be used.\n    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,\n      @ref number_float_t, and all convertible number types such as `int`,\n      `size_t`, `int64_t`, `float` or `double` can be used.\n    - **boolean**: @ref boolean_t / `bool` can be used.\n    - **binary**: @ref binary_t / `std::vector<std::uint8_t>` may be used,\n      unfortunately because string literals cannot be distinguished from binary\n      character arrays by the C++ type system, all types compatible with `const\n      char*` will be directed to the string constructor instead.  This is both\n      for backwards compatibility, and due to the fact that a binary type is not\n      a standard JSON type.\n\n    See the examples below.\n\n    @tparam CompatibleType a type such that:\n    - @a CompatibleType is not derived from `std::istream`,\n    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move\n         constructors),\n    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)\n    - @a CompatibleType is not a @ref basic_json nested type (e.g.,\n         @ref json_pointer, @ref iterator, etc ...)\n    - `json_serializer<U>` has a `to_json(basic_json_t&, CompatibleType&&)` method\n\n    @tparam U = `uncvref_t<CompatibleType>`\n\n    @param[in] val the value to be forwarded to the respective constructor\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @liveexample{The following code shows the constructor with several\n    compatible types.,basic_json__CompatibleType}\n\n    @since version 2.1.0\n    */\n    template < typename CompatibleType,\n               typename U = detail::uncvref_t<CompatibleType>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >\n    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value from an existing one\n\n    This is a constructor for existing @ref basic_json types.\n    It does not hijack copy/move constructors, since the parameter has different\n    template arguments than the current ones.\n\n    The constructor tries to convert the internal @ref m_value of the parameter.\n\n    @tparam BasicJsonType a type such that:\n    - @a BasicJsonType is a @ref basic_json type.\n    - @a BasicJsonType has different template arguments than @ref basic_json_t.\n\n    @param[in] val the @ref basic_json value to be converted.\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n        using other_binary_t = typename BasicJsonType::binary_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::binary:\n                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a container (array or object) from an initializer list\n\n    Creates a JSON value of type array or object from the passed initializer\n    list @a init. In case @a type_deduction is `true` (default), the type of\n    the JSON value to be created is deducted from the initializer list @a init\n    according to the following rules:\n\n    1. If the list is empty, an empty JSON object value `{}` is created.\n    2. If the list consists of pairs whose first element is a string, a JSON\n       object value is created where the first elements of the pairs are\n       treated as keys and the second elements are as values.\n    3. In all other cases, an array is created.\n\n    The rules aim to create the best fit between a C++ initializer list and\n    JSON values. The rationale is as follows:\n\n    1. The empty initializer list is written as `{}` which is exactly an empty\n       JSON object.\n    2. C++ has no way of describing mapped types other than to list a list of\n       pairs. As JSON requires that keys must be of type string, rule 2 is the\n       weakest constraint one can pose on initializer lists to interpret them\n       as an object.\n    3. In all other cases, the initializer list could not be interpreted as\n       JSON object type, so interpreting it as JSON array type is safe.\n\n    With the rules described above, the following JSON values cannot be\n    expressed by an initializer list:\n\n    - the empty array (`[]`): use @ref array(initializer_list_t)\n      with an empty initializer list in this case\n    - arrays whose elements satisfy rule 2: use @ref\n      array(initializer_list_t) with the same initializer list\n      in this case\n\n    @note When used without parentheses around an empty initializer list, @ref\n    basic_json() is called instead of this function, yielding the JSON null\n    value.\n\n    @param[in] init  initializer list with JSON values\n\n    @param[in] type_deduction internal parameter; when set to `true`, the type\n    of the JSON value is deducted from the initializer list @a init; when set\n    to `false`, the type provided via @a manual_type is forced. This mode is\n    used by the functions @ref array(initializer_list_t) and\n    @ref object(initializer_list_t).\n\n    @param[in] manual_type internal parameter; when @a type_deduction is set\n    to `false`, the created JSON value will use the provided type (only @ref\n    value_t::array and @ref value_t::object are valid); when @a type_deduction\n    is set to `true`, this parameter has no effect\n\n    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is\n    `value_t::object`, but @a init contains an element which is not a pair\n    whose first element is a string. In this case, the constructor could not\n    create an object. If @a type_deduction would have be `true`, an array\n    would have been created. See @ref object(initializer_list_t)\n    for an example.\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows how JSON values are created from\n    initializer lists.,basic_json__list_init_t}\n\n    @sa see @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n    @sa see @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (!type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\", basic_json()));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            for (auto& element_ref : init)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            }\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief explicitly create a binary array (without subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = init;\n        return res;\n    }\n\n    /*!\n    @brief explicitly create a binary array (with subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n    @param[in] subtype subtype to use in MessagePack and BSON\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(init, subtype);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = std::move(init);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&, typename binary_t::subtype_type)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(std::move(init), subtype);\n        return res;\n    }\n\n    /*!\n    @brief explicitly create an array from an initializer list\n\n    Creates a JSON array value from a given initializer list. That is, given a\n    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the\n    initializer list is empty, the empty array `[]` is created.\n\n    @note This function is only needed to express two edge cases that cannot\n    be realized with the initializer list constructor (@ref\n    basic_json(initializer_list_t, bool, value_t)). These cases\n    are:\n    1. creating an array whose elements are all pairs whose first element is a\n    string -- in this case, the initializer list constructor would create an\n    object, taking the first elements as keys\n    2. creating an empty array -- passing the empty initializer list to the\n    initializer list constructor yields an empty object\n\n    @param[in] init  initializer list with JSON values to create an array from\n    (optional)\n\n    @return JSON array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `array`\n    function.,array}\n\n    @sa see @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa see @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /*!\n    @brief explicitly create an object from an initializer list\n\n    Creates a JSON object value from a given initializer list. The initializer\n    lists elements must be pairs, and their first elements must be strings. If\n    the initializer list is empty, the empty object `{}` is created.\n\n    @note This function is only added for symmetry reasons. In contrast to the\n    related function @ref array(initializer_list_t), there are\n    no cases which can only be expressed by this function. That is, any\n    initializer list @a init can also be passed to the initializer list\n    constructor @ref basic_json(initializer_list_t, bool, value_t).\n\n    @param[in] init  initializer list to create an object from (optional)\n\n    @return JSON object value\n\n    @throw type_error.301 if @a init is not a list of pairs whose first\n    elements are strings. In this case, no object can be created. When such a\n    value is passed to @ref basic_json(initializer_list_t, bool, value_t),\n    an array would have been created from the passed initializer list @a init.\n    See example below.\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `object`\n    function.,object}\n\n    @sa see @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa see @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /*!\n    @brief construct an array with count copies of given value\n\n    Constructs a JSON array value by creating @a cnt copies of a passed value.\n    In case @a cnt is `0`, an empty array is created.\n\n    @param[in] cnt  the number of JSON copies of @a val to create\n    @param[in] val  the JSON value to copy\n\n    @post `std::distance(begin(),end()) == cnt` holds.\n\n    @complexity Linear in @a cnt.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows examples for the @ref\n    basic_json(size_type\\, const basic_json&)\n    constructor.,basic_json__size_type_basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief construct a JSON container given an iterator range\n\n    Constructs the JSON value with the contents of the range `[first, last)`.\n    The semantics depends on the different types a JSON value can have:\n    - In case of a null type, invalid_iterator.206 is thrown.\n    - In case of other primitive types (number, boolean, or string), @a first\n      must be `begin()` and @a last must be `end()`. In this case, the value is\n      copied. Otherwise, invalid_iterator.204 is thrown.\n    - In case of structured types (array, object), the constructor behaves as\n      similar versions for `std::vector` or `std::map`; that is, a JSON array\n      or object is constructed from the values in the range.\n\n    @tparam InputIT an input iterator type (@ref iterator or @ref\n    const_iterator)\n\n    @param[in] first begin of the range to copy from (included)\n    @param[in] last end of the range to copy from (excluded)\n\n    @pre Iterators @a first and @a last must be initialized. **This\n         precondition is enforced with an assertion (see warning).** If\n         assertions are switched off, a violation of this precondition yields\n         undefined behavior.\n\n    @pre Range `[first, last)` is valid. Usually, this precondition cannot be\n         checked efficiently. Only certain edge cases are detected; see the\n         description of the exceptions below. A violation of this precondition\n         yields undefined behavior.\n\n    @warning A precondition is enforced with a runtime assertion that will\n             result in calling `std::abort` if this precondition is not met.\n             Assertions can be disabled by defining `NDEBUG` at compile time.\n             See https://en.cppreference.com/w/cpp/error/assert for more\n             information.\n\n    @throw invalid_iterator.201 if iterators @a first and @a last are not\n    compatible (i.e., do not belong to the same JSON value). In this case,\n    the range `[first, last)` is undefined.\n    @throw invalid_iterator.204 if iterators @a first and @a last belong to a\n    primitive type (number, boolean, or string), but @a first does not point\n    to the first element any more. In this case, the range `[first, last)` is\n    undefined. See example code below.\n    @throw invalid_iterator.206 if iterators @a first and @a last belong to a\n    null value. In this case, the range `[first, last)` is undefined.\n\n    @complexity Linear in distance between @a first and @a last.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows several ways to create JSON values by\n    specifying a subrange with iterators.,basic_json__InputIt_InputIt}\n\n    @since version 1.0.0\n    */\n    template < class InputIT, typename std::enable_if <\n                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||\n                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >\n    basic_json(InputIT first, InputIT last)\n    {\n        JSON_ASSERT(first.m_object != nullptr);\n        JSON_ASSERT(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\", basic_json()));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()\n                                         || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", *first.m_object));\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::object:\n            case value_t::array:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *first.m_object->m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(invalid_iterator::create(206, \"cannot construct with iterators from \" + std::string(first.m_object->type_name()), *first.m_object));\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    template<typename JsonRef,\n             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,\n                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >\n    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}\n\n    /*!\n    @brief copy constructor\n\n    Creates a copy of a given JSON value.\n\n    @param[in] other  the JSON value to copy\n\n    @post `*this == other`\n\n    @complexity Linear in the size of @a other.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - As postcondition, it holds: `other == basic_json(other)`.\n\n    @liveexample{The following code shows an example for the copy\n    constructor.,basic_json__basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *other.m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief move constructor\n\n    Move constructor. Constructs a JSON value with the contents of the given\n    value @a other using move semantics. It \"steals\" the resources from @a\n    other and leaves it as JSON null value.\n\n    @param[in,out] other  value to move to this object\n\n    @post `*this` has the same value as @a other before the call.\n    @post @a other is a JSON null value.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)\n    requirements.\n\n    @liveexample{The code below shows the move constructor explicitly called\n    via std::move.,basic_json__moveconstructor}\n\n    @since version 1.0.0\n    */\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant(false);\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief copy assignment\n\n    Copy assignment operator. Copies a JSON value via the \"copy and swap\"\n    strategy: It is expressed in terms of the copy constructor, destructor,\n    and the `swap()` member function.\n\n    @param[in] other  value to copy from\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n\n    @liveexample{The code below shows and example for the copy assignment. It\n    creates a copy of value `a` which is then swapped with `b`. Finally\\, the\n    copy of `a` (which is the null value after the swap) is\n    destroyed.,basic_json__copyassignment}\n\n    @since version 1.0.0\n    */\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        set_parents();\n        assert_invariant();\n        return *this;\n    }\n\n    /*!\n    @brief destructor\n\n    Destroys the JSON value and frees all allocated memory.\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - All stored elements are destroyed and all memory is freed.\n\n    @since version 1.0.0\n    */\n    ~basic_json() noexcept\n    {\n        assert_invariant(false);\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /*!\n    @brief serialization\n\n    Serialization function for JSON values. The function tries to mimic\n    Python's `json.dumps()` function, and currently supports its @a indent\n    and @a ensure_ascii parameters.\n\n    @param[in] indent If indent is nonnegative, then array elements and object\n    members will be pretty-printed with that indent level. An indent level of\n    `0` will only insert newlines. `-1` (the default) selects the most compact\n    representation.\n    @param[in] indent_char The character to use for indentation if @a indent is\n    greater than `0`. The default is ` ` (space).\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] error_handler  how to react on decoding errors; there are three\n    possible values: `strict` (throws and exception in case a decoding error\n    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),\n    and `ignore` (ignore invalid UTF-8 sequences during serialization; all\n    bytes are copied to the output unchanged).\n\n    @return string containing the serialization of the JSON value\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded and @a error_handler is set to strict\n\n    @note Binary values are serialized as object containing two keys:\n      - \"bytes\": an array of bytes as integers\n      - \"subtype\": the subtype as integer or \"null\" if the binary has no subtype\n\n    @complexity Linear.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @liveexample{The following example shows the effect of different @a indent\\,\n    @a indent_char\\, and @a ensure_ascii parameters to the result of the\n    serialization.,dump}\n\n    @see https://docs.python.org/2/library/json.html#json.dump\n\n    @since version 1.0.0; indentation character @a indent_char, option\n           @a ensure_ascii and exceptions added in version 3.0.0; error\n           handlers added in version 3.4.0; serialization of binary values added\n           in version 3.8.0.\n    */\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief return the type of the JSON value (explicit)\n\n    Return the type of the JSON value as a value from the @ref value_t\n    enumeration.\n\n    @return the type of the JSON value\n            Value type                | return value\n            ------------------------- | -------------------------\n            null                      | value_t::null\n            boolean                   | value_t::boolean\n            string                    | value_t::string\n            number (integer)          | value_t::number_integer\n            number (unsigned integer) | value_t::number_unsigned\n            number (floating-point)   | value_t::number_float\n            object                    | value_t::object\n            array                     | value_t::array\n            binary                    | value_t::binary\n            discarded                 | value_t::discarded\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `type()` for all JSON\n    types.,type}\n\n    @sa see @ref operator value_t() -- return the type of the JSON value (implicit)\n    @sa see @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /*!\n    @brief return whether type is primitive\n\n    This function returns true if and only if the JSON type is primitive\n    (string, number, boolean, or null).\n\n    @return `true` if type is primitive (string, number, boolean, or null),\n    `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_primitive()` for all JSON\n    types.,is_primitive}\n\n    @sa see @ref is_structured() -- returns whether JSON value is structured\n    @sa see @ref is_null() -- returns whether JSON value is `null`\n    @sa see @ref is_string() -- returns whether JSON value is a string\n    @sa see @ref is_boolean() -- returns whether JSON value is a boolean\n    @sa see @ref is_number() -- returns whether JSON value is a number\n    @sa see @ref is_binary() -- returns whether JSON value is a binary array\n\n    @since version 1.0.0\n    */\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() || is_string() || is_boolean() || is_number() || is_binary();\n    }\n\n    /*!\n    @brief return whether type is structured\n\n    This function returns true if and only if the JSON type is structured\n    (array or object).\n\n    @return `true` if type is structured (array or object), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_structured()` for all JSON\n    types.,is_structured}\n\n    @sa see @ref is_primitive() -- returns whether value is primitive\n    @sa see @ref is_array() -- returns whether value is an array\n    @sa see @ref is_object() -- returns whether value is an object\n\n    @since version 1.0.0\n    */\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() || is_object();\n    }\n\n    /*!\n    @brief return whether value is null\n\n    This function returns true if and only if the JSON value is null.\n\n    @return `true` if type is null, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_null()` for all JSON\n    types.,is_null}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_null() const noexcept\n    {\n        return m_type == value_t::null;\n    }\n\n    /*!\n    @brief return whether value is a boolean\n\n    This function returns true if and only if the JSON value is a boolean.\n\n    @return `true` if type is boolean, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_boolean()` for all JSON\n    types.,is_boolean}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_type == value_t::boolean;\n    }\n\n    /*!\n    @brief return whether value is a number\n\n    This function returns true if and only if the JSON value is a number. This\n    includes both integer (signed and unsigned) and floating-point values.\n\n    @return `true` if type is number (regardless whether integer, unsigned\n    integer or floating-type), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number()` for all JSON\n    types.,is_number}\n\n    @sa see @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa see @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa see @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() || is_number_float();\n    }\n\n    /*!\n    @brief return whether value is an integer number\n\n    This function returns true if and only if the JSON value is a signed or\n    unsigned integer number. This excludes floating-point values.\n\n    @return `true` if type is an integer or unsigned integer number, `false`\n    otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_integer()` for all\n    JSON types.,is_number_integer}\n\n    @sa see @ref is_number() -- check if value is a number\n    @sa see @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa see @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is an unsigned integer number\n\n    This function returns true if and only if the JSON value is an unsigned\n    integer number. This excludes floating-point and signed integer values.\n\n    @return `true` if type is an unsigned integer number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_unsigned()` for all\n    JSON types.,is_number_unsigned}\n\n    @sa see @ref is_number() -- check if value is a number\n    @sa see @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa see @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 2.0.0\n    */\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is a floating-point number\n\n    This function returns true if and only if the JSON value is a\n    floating-point number. This excludes signed and unsigned integer values.\n\n    @return `true` if type is a floating-point number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_float()` for all\n    JSON types.,is_number_float}\n\n    @sa see @ref is_number() -- check if value is number\n    @sa see @ref is_number_integer() -- check if value is an integer number\n    @sa see @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_type == value_t::number_float;\n    }\n\n    /*!\n    @brief return whether value is an object\n\n    This function returns true if and only if the JSON value is an object.\n\n    @return `true` if type is object, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_object()` for all JSON\n    types.,is_object}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_object() const noexcept\n    {\n        return m_type == value_t::object;\n    }\n\n    /*!\n    @brief return whether value is an array\n\n    This function returns true if and only if the JSON value is an array.\n\n    @return `true` if type is array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_array()` for all JSON\n    types.,is_array}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_array() const noexcept\n    {\n        return m_type == value_t::array;\n    }\n\n    /*!\n    @brief return whether value is a string\n\n    This function returns true if and only if the JSON value is a string.\n\n    @return `true` if type is string, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_string()` for all JSON\n    types.,is_string}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_string() const noexcept\n    {\n        return m_type == value_t::string;\n    }\n\n    /*!\n    @brief return whether value is a binary array\n\n    This function returns true if and only if the JSON value is a binary array.\n\n    @return `true` if type is binary array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_binary()` for all JSON\n    types.,is_binary}\n\n    @since version 3.8.0\n    */\n    constexpr bool is_binary() const noexcept\n    {\n        return m_type == value_t::binary;\n    }\n\n    /*!\n    @brief return whether value is discarded\n\n    This function returns true if and only if the JSON value was discarded\n    during parsing with a callback function (see @ref parser_callback_t).\n\n    @note This function will always be `false` for JSON values after parsing.\n    That is, discarded values can only occur during parsing, but will be\n    removed when inside a structured value or replaced by null in other cases.\n\n    @return `true` if type is discarded, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_discarded()` for all JSON\n    types.,is_discarded}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_type == value_t::discarded;\n    }\n\n    /*!\n    @brief return the type of the JSON value (implicit)\n\n    Implicitly return the type of the JSON value as a value from the @ref\n    value_t enumeration.\n\n    @return the type of the JSON value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies the @ref value_t operator for\n    all JSON types.,operator__value_t}\n\n    @sa see @ref type() -- return the type of the JSON value (explicit)\n    @sa see @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(type_name()), *this));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, \"incompatible ReferenceType for get_ref, actual type is \" + std::string(obj.type_name()), obj));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /*!\n    @brief get a pointer value (implicit)\n\n    Implicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning Writing data to the pointee of the result yields an undefined\n    state.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static\n    assertion.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get_ptr}\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (implicit)\n    @copydoc get_ptr()\n    */\n    template < typename PointerType, typename std::enable_if <\n                   std::is_pointer<PointerType>::value&&\n                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n  private:\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::is_default_constructible<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        auto ret = ValueType();\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueType>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @a BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value,\n                   int > = 0 >\n    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType,\n             detail::enable_if_t<\n                 std::is_same<BasicJsonType, basic_json_t>::value,\n                 int> = 0>\n    basic_json get_impl(detail::priority_tag<3> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType,\n             detail::enable_if_t<\n                 std::is_pointer<PointerType>::value,\n                 int> = 0>\n    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept\n    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n  public:\n    /*!\n    @brief get a (pointer) value (explicit)\n\n    Performs explicit type conversion between the JSON value and a compatible value if required.\n\n    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.\n    No copies are made.\n\n    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible\n    from the current @ref basic_json.\n\n    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`\n    method.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @tparam ValueType if necessary\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>\n#if defined(JSON_HAS_CPP_14)\n    constexpr\n#endif\n    auto get() const noexcept(\n    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))\n    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return get_impl<ValueType>(detail::priority_tag<4> {});\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa see @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value.\n    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType v;\n    JSONSerializer<ValueType>::from_json(*this, v);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n\n    @tparam ValueType the input parameter type.\n\n    @return the input parameter, allowing chaining calls.\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get_to}\n\n    @since version 3.3.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    // specialization to allow to call get_to with a basic_json value\n    // see https://github.com/nlohmann/json/issues/2175\n    template<typename ValueType,\n             detail::enable_if_t <\n                 detail::is_basic_json<ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const\n    {\n        v = *this;\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n\n    Implicit reference access to the internally stored JSON value. No copies\n    are made.\n\n    @warning Writing data to the referee of the result yields an undefined\n    state.\n\n    @tparam ReferenceType reference type; must be a reference to @ref array_t,\n    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or\n    @ref number_float_t. Enforced by static assertion.\n\n    @return reference to the internally stored JSON value if the requested\n    reference type @a ReferenceType fits to the JSON value; throws\n    type_error.303 otherwise\n\n    @throw type_error.303 in case passed type @a ReferenceType is incompatible\n    with the stored JSON value; see example below\n\n    @complexity Constant.\n\n    @liveexample{The example shows several calls to `get_ref()`.,get_ref}\n\n    @since version 1.1.0\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n    @copydoc get_ref()\n    */\n    template < typename ReferenceType, typename std::enable_if <\n                   std::is_reference<ReferenceType>::value&&\n                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   detail::conjunction <\n                       detail::negation<std::is_pointer<ValueType>>,\n                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,\n                                        detail::negation<std::is_same<ValueType, typename string_t::value_type>>,\n                                        detail::negation<detail::is_basic_json<ValueType>>,\n                                        detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,\n\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))\n                                                detail::negation<std::is_same<ValueType, std::string_view>>,\n#endif\n                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>\n                                                >::value, int >::type = 0 >\n                                        JSON_EXPLICIT operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /*!\n    @return reference to the binary value\n\n    @throw type_error.302 if the value is not binary\n\n    @sa see @ref is_binary() to check if the value is binary\n\n    @since version 3.8.0\n    */\n    binary_t& get_binary()\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name()), *this));\n        }\n\n        return *get_ptr<binary_t*>();\n    }\n\n    /// @copydoc get_binary()\n    const binary_t& get_binary() const\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name()), *this));\n        }\n\n        return *get_ptr<const binary_t*>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a reference to the element at specified location @a idx, with\n    bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__size_type}\n    */\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return set_parent(m_value.array->at(idx));\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a const reference to the element at specified location @a idx,\n    with bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__size_type_const}\n    */\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a reference to the element at with specified key @a key, with\n    bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa see @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__object_t_key_type}\n    */\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return set_parent(m_value.object->at(key));\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a const reference to the element at with specified key @a key,\n    with bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa see @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__object_t_key_type_const}\n    */\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a reference to the element at specified location @a idx.\n\n    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),\n    then the array is silently filled up with `null` values to make `idx` a\n    valid reference to the last stored element.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array or null; in that\n    cases, using the [] operator with an index makes no sense.\n\n    @complexity Constant if @a idx is in the range of the array. Otherwise\n    linear in `idx - size()`.\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `[]` operator. Note the addition of `null`\n    values.,operatorarray__size_type}\n\n    @since version 1.0.0\n    */\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n#if JSON_DIAGNOSTICS\n                // remember array size & capacity before resizing\n                const auto old_size = m_value.array->size();\n                const auto old_capacity = m_value.array->capacity();\n#endif\n                m_value.array->resize(idx + 1);\n\n#if JSON_DIAGNOSTICS\n                if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))\n                {\n                    // capacity has changed: update all parents\n                    set_parents();\n                }\n                else\n                {\n                    // set parent for values added above\n                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));\n                }\n#endif\n                assert_invariant();\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a const reference to the element at specified location @a idx.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array; in that case,\n    using the [] operator with an index makes no sense.\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how array elements can be read using\n    the `[]` operator.,operatorarray__size_type_const}\n\n    @since version 1.0.0\n    */\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    reference operator[](const typename object_t::key_type& key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return set_parent(m_value.object->operator[](key));\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    reference operator[](T* key)\n    {\n        // implicitly convert null to object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return set_parent(m_value.object->operator[](key));\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    const_reference operator[](T* key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified object element with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(key);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const typename object_t::key_type&), this function\n    does not throw if the given key @a key was not found.\n\n    @note Unlike @ref operator[](const typename object_t::key_type& key), this\n    function does not implicitly add an element to the position defined by @a\n    key. This function is furthermore also applicable to const objects.\n\n    @param[in] key  key of the element to access\n    @param[in] default_value  the value to return if @a key is not found\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a key\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n\n    @since version 1.0.0\n    */\n    // using std::is_convertible in a std::enable_if will fail when using explicit conversions\n    template < class ValueType, typename std::enable_if <\n                   detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, ValueType>::value, int >::type = 0 >\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const\n    */\n    string_t value(const typename object_t::key_type& key, const char* default_value) const\n    {\n        return value(key, string_t(default_value));\n    }\n\n    /*!\n    @brief access specified object element via JSON Pointer with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(ptr);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const json_pointer&), this function does not throw\n    if the given key @a key was not found.\n\n    @param[in] ptr  a JSON pointer to the element to access\n    @param[in] default_value  the value to return if @a ptr found no value\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a ptr\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value_ptr}\n\n    @sa see @ref operator[](const json_pointer&) for unchecked access by reference\n\n    @since version 2.0.2\n    */\n    template<class ValueType, typename std::enable_if<\n                 detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ValueType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const json_pointer&, ValueType) const\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    string_t value(const json_pointer& ptr, const char* default_value) const\n    {\n        return value(ptr, string_t(default_value));\n    }\n\n    /*!\n    @brief access the first element\n\n    Returns a reference to the first element in the container. For a JSON\n    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.\n\n    @return In case of a structured type (array or object), a reference to the\n    first element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on `null` value\n\n    @liveexample{The following code shows an example for `front()`.,front}\n\n    @sa see @ref back() -- access the last element\n\n    @since version 1.0.0\n    */\n    reference front()\n    {\n        return *begin();\n    }\n\n    /*!\n    @copydoc basic_json::front()\n    */\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /*!\n    @brief access the last element\n\n    Returns a reference to the last element in the container. For a JSON\n    container `c`, the expression `c.back()` is equivalent to\n    @code {.cpp}\n    auto tmp = c.end();\n    --tmp;\n    return *tmp;\n    @endcode\n\n    @return In case of a structured type (array or object), a reference to the\n    last element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on a `null` value. See example\n    below.\n\n    @liveexample{The following code shows an example for `back()`.,back}\n\n    @sa see @ref front() -- access the first element\n\n    @since version 1.0.0\n    */\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @copydoc basic_json::back()\n    */\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @brief remove element given an iterator\n\n    Removes the element specified by iterator @a pos. The iterator @a pos must\n    be valid and dereferenceable. Thus the `end()` iterator (which is valid,\n    but is not dereferenceable) cannot be used as a value for @a pos.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] pos iterator to the element to remove\n    @return Iterator following the last removed element. If the iterator @a\n    pos refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.202 if called on an iterator which does not belong\n    to the current JSON value; example: `\"iterator does not fit current\n    value\"`\n    @throw invalid_iterator.205 if called on a primitive type with invalid\n    iterator (i.e., any iterator which is not `begin()`); example: `\"iterator\n    out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: amortized constant\n    - arrays: linear in distance between @a pos and the end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType}\n\n    @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa see @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa see @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\", *this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove elements given an iterator range\n\n    Removes the element specified by the range `[first; last)`. The iterator\n    @a first does not need to be dereferenceable if `first == last`: erasing\n    an empty range is a no-op.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] first iterator to the beginning of the range to remove\n    @param[in] last iterator past the end of the range to remove\n    @return Iterator following the last removed element. If the iterator @a\n    second refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.203 if called on iterators which does not belong\n    to the current JSON value; example: `\"iterators do not fit current value\"`\n    @throw invalid_iterator.204 if called on a primitive type with invalid\n    iterators (i.e., if `first != begin()` and `last != end()`); example:\n    `\"iterators out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: `log(size()) + std::distance(first, last)`\n    - arrays: linear in the distance between @a first and @a last, plus linear\n      in the distance between @a last and end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType_IteratorType}\n\n    @sa see @ref erase(IteratorType) -- removes the element at a given position\n    @sa see @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa see @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\", *this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()\n                                       || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", *this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove element from a JSON object given a key\n\n    Removes elements from a JSON object with the key value @a key.\n\n    @param[in] key value of the elements to remove\n\n    @return Number of elements removed. If @a ObjectType is the default\n    `std::map` type, the return value will always be `0` (@a key was not\n    found) or `1` (@a key was found).\n\n    @post References and iterators to the erased elements are invalidated.\n    Other references and iterators are not affected.\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n\n    @complexity `log(size()) + count(key)`\n\n    @liveexample{The example shows the effect of `erase()`.,erase__key_type}\n\n    @sa see @ref erase(IteratorType) -- removes the element at a given position\n    @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa see @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->erase(key);\n        }\n\n        JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief remove element from a JSON array given an index\n\n    Removes element from a JSON array at the index @a idx.\n\n    @param[in] idx index of the element to remove\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n    @throw out_of_range.401 when `idx >= size()`; example: `\"array index 17\n    is out of range\"`\n\n    @complexity Linear in distance between @a idx and the end of the container.\n\n    @liveexample{The example shows the effect of `erase()`.,erase__size_type}\n\n    @sa see @ref erase(IteratorType) -- removes the element at a given position\n    @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa see @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n\n    @since version 1.0.0\n    */\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", *this));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /*!\n    @brief find an element in a JSON object\n\n    Finds an element in a JSON object with key equivalent to @a key. If the\n    element is not found or the JSON value is not an object, end() is\n    returned.\n\n    @note This method always returns @ref end() when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value of the element to search for.\n\n    @return Iterator to an element with key equivalent to @a key. If no such\n    element is found or the JSON value is not an object, past-the-end (see\n    @ref end()) iterator is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `find()` is used.,find__key_type}\n\n    @sa see @ref contains(KeyT&&) const -- checks whether a key exists\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    iterator find(KeyT&& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief find an element in a JSON object\n    @copydoc find(KeyT&&)\n    */\n    template<typename KeyT>\n    const_iterator find(KeyT&& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief returns the number of occurrences of a key in a JSON object\n\n    Returns the number of elements with key @a key. If ObjectType is the\n    default `std::map` type, the return value will always be `0` (@a key was\n    not found) or `1` (@a key was found).\n\n    @note This method always returns `0` when executed on a JSON type that is\n          not an object.\n\n    @param[in] key key value of the element to count\n\n    @return Number of elements with key @a key. If the JSON value is not an\n    object, the return value will be `0`.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `count()` is used.,count}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    size_type count(KeyT&& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object\n\n    Check whether an element exists in a JSON object with key equivalent to\n    @a key. If the element is not found or the JSON value is not an object,\n    false is returned.\n\n    @note This method always returns false when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value to check its existence.\n\n    @return true if an element with specified @a key exists. If no such\n    element with such key is found or the JSON value is not an object,\n    false is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains}\n\n    @sa see @ref find(KeyT&&) -- returns an iterator to an object element\n    @sa see @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer\n\n    @since version 3.6.0\n    */\n    template < typename KeyT, typename std::enable_if <\n                   !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 >\n    bool contains(KeyT && key) const\n    {\n        return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object given a JSON pointer\n\n    Check whether the given JSON pointer @a ptr can be resolved in the current\n    JSON value.\n\n    @note This method can be executed on any JSON value type.\n\n    @param[in] ptr JSON pointer to check its existence.\n\n    @return true if the JSON pointer can be resolved to a stored value, false\n    otherwise.\n\n    @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains_json_pointer}\n\n    @sa see @ref contains(KeyT &&) const -- checks the existence of a key\n\n    @since version 3.7.0\n    */\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /*!\n    @brief returns an iterator to the first element\n\n    Returns an iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `begin()`.,begin}\n\n    @sa see @ref cbegin() -- returns a const iterator to the beginning\n    @sa see @ref end() -- returns an iterator to the end\n    @sa see @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cbegin()\n    */\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /*!\n    @brief returns a const iterator to the first element\n\n    Returns a const iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.\n\n    @liveexample{The following code shows an example for `cbegin()`.,cbegin}\n\n    @sa see @ref begin() -- returns an iterator to the beginning\n    @sa see @ref end() -- returns an iterator to the end\n    @sa see @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to one past the last element\n\n    Returns an iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `end()`.,end}\n\n    @sa see @ref cend() -- returns a const iterator to the end\n    @sa see @ref begin() -- returns an iterator to the beginning\n    @sa see @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cend()\n    */\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /*!\n    @brief returns a const iterator to one past the last element\n\n    Returns a const iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.\n\n    @liveexample{The following code shows an example for `cend()`.,cend}\n\n    @sa see @ref end() -- returns an iterator to the end\n    @sa see @ref begin() -- returns an iterator to the beginning\n    @sa see @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-beginning\n\n    Returns an iterator to the reverse-beginning; that is, the last element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(end())`.\n\n    @liveexample{The following code shows an example for `rbegin()`.,rbegin}\n\n    @sa see @ref crbegin() -- returns a const reverse iterator to the beginning\n    @sa see @ref rend() -- returns a reverse iterator to the end\n    @sa see @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /*!\n    @copydoc basic_json::crbegin()\n    */\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-end\n\n    Returns an iterator to the reverse-end; that is, one before the first\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(begin())`.\n\n    @liveexample{The following code shows an example for `rend()`.,rend}\n\n    @sa see @ref crend() -- returns a const reverse iterator to the end\n    @sa see @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa see @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /*!\n    @copydoc basic_json::crend()\n    */\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /*!\n    @brief returns a const reverse iterator to the last element\n\n    Returns a const iterator to the reverse-beginning; that is, the last\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.\n\n    @liveexample{The following code shows an example for `crbegin()`.,crbegin}\n\n    @sa see @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa see @ref rend() -- returns a reverse iterator to the end\n    @sa see @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /*!\n    @brief returns a const reverse iterator to one before the first\n\n    Returns a const reverse iterator to the reverse-end; that is, one before\n    the first element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.\n\n    @liveexample{The following code shows an example for `crend()`.,crend}\n\n    @sa see @ref rend() -- returns a reverse iterator to the end\n    @sa see @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa see @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /*!\n    @brief wrapper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without iterator_wrapper:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without iterator proxy:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with iterator proxy:\n\n    @code{cpp}\n    for (auto it : json::iterator_wrapper(j_object))\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example).\n\n    @param[in] ref  reference to a JSON value\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @note The name of this function is not yet final and may change in the\n    future.\n\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use @ref items() instead;\n                that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @copydoc iterator_wrapper(reference)\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @brief helper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without `items()` function:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with `items()` function:\n\n    @code{cpp}\n    for (auto& el : j_object.items())\n    {\n        std::cout << \"key: \" << el.key() << \", value:\" << el.value() << '\\n';\n    }\n    @endcode\n\n    The `items()` function also allows to use\n    [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding)\n    (C++17):\n\n    @code{cpp}\n    for (auto& [key, val] : j_object.items())\n    {\n        std::cout << \"key: \" << key << \", value:\" << val << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example). For primitive types (e.g., numbers),\n          `key()` returns an empty string.\n\n    @warning Using `items()` on temporary objects is dangerous. Make sure the\n             object's lifetime exeeds the iteration. See\n             <https://github.com/nlohmann/json/issues/2040> for more\n             information.\n\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the function is used.,items}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 3.1.0, structured bindings support since 3.5.0.\n    */\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /*!\n    @copydoc items()\n    */\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /*!\n    @brief checks whether the container is empty.\n\n    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `true`\n            boolean     | `false`\n            string      | `false`\n            number      | `false`\n            binary      | `false`\n            object      | result of function `object_t::empty()`\n            array       | result of function `array_t::empty()`\n\n    @liveexample{The following code uses `empty()` to check if a JSON\n    object contains any elements.,empty}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `empty()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return whether a string stored as JSON value\n    is empty - it returns whether the JSON container itself is empty which is\n    false in the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `begin() == end()`.\n\n    @sa see @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the number of elements\n\n    Returns the number of elements in a JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0`\n            boolean     | `1`\n            string      | `1`\n            number      | `1`\n            binary      | `1`\n            object      | result of function object_t::size()\n            array       | result of function array_t::size()\n\n    @liveexample{The following code calls `size()` on the different value\n    types.,size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their size() functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return the length of a string stored as JSON\n    value - it returns the number of elements in the JSON value which is 1 in\n    the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `std::distance(begin(), end())`.\n\n    @sa see @ref empty() -- checks whether the container is empty\n    @sa see @ref max_size() -- returns the maximal number of elements\n\n    @since version 1.0.0\n    */\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the maximum possible number of elements\n\n    Returns the maximum number of elements a JSON value is able to hold due to\n    system or library implementation limitations, i.e. `std::distance(begin(),\n    end())` for the JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0` (same as `size()`)\n            boolean     | `1` (same as `size()`)\n            string      | `1` (same as `size()`)\n            number      | `1` (same as `size()`)\n            binary      | `1` (same as `size()`)\n            object      | result of function `object_t::max_size()`\n            array       | result of function `array_t::max_size()`\n\n    @liveexample{The following code calls `max_size()` on the different value\n    types. Note the output is implementation specific.,max_size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `max_size()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of returning `b.size()` where `b` is the largest\n      possible JSON value.\n\n    @sa see @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /*!\n    @brief clears the contents\n\n    Clears the content of a JSON value and resets it to the default value as\n    if @ref basic_json(value_t) would have been called with the current value\n    type from @ref type():\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    binary      | An empty byte vector\n    object      | `{}`\n    array       | `[]`\n\n    @post Has the same effect as calling\n    @code {.cpp}\n    *this = basic_json(type());\n    @endcode\n\n    @liveexample{The example below shows the effect of `clear()` to different\n    JSON types.,clear}\n\n    @complexity Linear in the size of the JSON value.\n\n    @iterators All iterators, pointers and references related to this container\n               are invalidated.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @sa see @ref basic_json(value_t) -- constructor that creates an object with the\n        same value than calling `clear()`\n\n    @since version 1.0.0\n    */\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value.binary->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Appends the given element @a val to the end of the JSON value. If the\n    function is called on a JSON null value, an empty array is created before\n    appending @a val.\n\n    @param[in] val the value to add to the JSON array\n\n    @throw type_error.308 when called on a type other than JSON array or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON array. Note how the `null` value was silently\n    converted to a JSON array.,push_back}\n\n    @since version 1.0.0\n    */\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->push_back(std::move(val));\n        set_parent(m_value.array->back(), old_capacity);\n        // if val is moved from, basic_json move constructor marks it null so we do not call the destructor\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->push_back(val);\n        set_parent(m_value.array->back(), old_capacity);\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    Inserts the given element @a val to the JSON object. If the function is\n    called on a JSON null value, an empty object is created before inserting\n    @a val.\n\n    @param[in] val the value to add to the JSON object\n\n    @throw type_error.308 when called on a type other than JSON object or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON object. Note how the `null` value was silently\n    converted to a JSON object.,push_back__object_t__value}\n\n    @since version 1.0.0\n    */\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to object\n        auto res = m_value.object->insert(val);\n        set_parent(res.first->second);\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(const typename object_t::value_type&)\n    */\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    This function allows to use `push_back` with an initializer list. In case\n\n    1. the current value is an object,\n    2. the initializer list @a init contains only two elements, and\n    3. the first element of @a init is a string,\n\n    @a init is converted into an object element and added using\n    @ref push_back(const typename object_t::value_type&). Otherwise, @a init\n    is converted to a JSON value and added using @ref push_back(basic_json&&).\n\n    @param[in] init  an initializer list\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @note This function is required to resolve an ambiguous overload error,\n          because pairs like `{\"key\", \"value\"}` can be both interpreted as\n          `object_t::value_type` or `std::initializer_list<basic_json>`, see\n          https://github.com/nlohmann/json/issues/235 for more information.\n\n    @liveexample{The example shows how initializer lists are treated as\n    objects when possible.,push_back__initializer_list}\n    */\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() && init.size() == 2 && (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(initializer_list_t)\n    */\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Creates a JSON value from the passed parameters @a args to the end of the\n    JSON value. If the function is called on a JSON null value, an empty array\n    is created before appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return reference to the inserted element\n\n    @throw type_error.311 when called on a type other than JSON array or\n    null; example: `\"cannot use emplace_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` can be used to add\n    elements to a JSON array. Note how the `null` value was silently converted\n    to a JSON array.,emplace_back}\n\n    @since version 2.0.8, returns reference since 3.7.0\n    */\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n        return set_parent(m_value.array->back(), old_capacity);\n    }\n\n    /*!\n    @brief add an object to an object if key does not exist\n\n    Inserts a new element into a JSON object constructed in-place with the\n    given @a args if there is no element with the key in the container. If the\n    function is called on a JSON null value, an empty object is created before\n    appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return a pair consisting of an iterator to the inserted element, or the\n            already-existing element if no insertion happened, and a bool\n            denoting whether the insertion took place.\n\n    @throw type_error.311 when called on a type other than JSON object or\n    null; example: `\"cannot use emplace() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `emplace()` can be used to add elements\n    to a JSON object. Note how the `null` value was silently converted to a\n    JSON object. Further note how no value is added if there was already one\n    value stored with the same key.,emplace}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        set_parent(res.first->second);\n\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        JSON_ASSERT(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        set_parents();\n        return result;\n    }\n\n    /*!\n    @brief inserts element\n\n    Inserts element @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] val element to insert\n    @return iterator pointing to the inserted @a val.\n\n    @throw type_error.309 if called on JSON values other than arrays;\n    example: `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Constant plus linear in the distance between @a pos and end of\n    the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief inserts element\n    @copydoc insert(const_iterator, const basic_json&)\n    */\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts @a cnt copies of @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] cnt number of copies of @a val to insert\n    @param[in] val element to insert\n    @return iterator pointing to the first element inserted, or @a pos if\n    `cnt==0`\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Linear in @a cnt plus linear in the distance between @a pos\n    and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__count}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)` before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n    @throw invalid_iterator.211 if @a first or @a last are iterators into\n    container for which insert is called; example: `\"passed iterators may not\n    belong to container\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `first==last`\n\n    @complexity Linear in `std::distance(first, last)` plus linear in the\n    distance between @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", *this));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\", *this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from initializer list @a ilist before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] ilist initializer list to insert the values from\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `ilist` is empty\n\n    @complexity Linear in `ilist.size()` plus linear in the distance between\n    @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__ilist}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)`.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than objects; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number\n    of elements to insert.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range_object}\n\n    @since version 3.0.0\n    */\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", *this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\", *this));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from JSON object @a j and overwrites existing keys.\n\n    @param[in] j  JSON object to read values from\n    @param[in] merge_objects  when true, existing keys are not overwritten, but\n                              contents of objects are merged recursively\n                              (default: false)\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0, `merge_objects` parameter added in 3.10.4.\n    */\n    void update(const_reference j, bool merge_objects = false)\n    {\n        update(j.begin(), j.end(), merge_objects);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from from range `[first, last)` and overwrites existing\n    keys.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n    @param[in] merge_objects  when true, existing keys are not overwritten, but\n                              contents of objects are merged recursively\n                              (default: false)\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n    @throw type_error.312 if iterator @a first or @a last does does not\n    point to an object; example: `\"cannot use update() with string\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used__range.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0, `merge_objects` parameter added in 3.10.4.\n    */\n    void update(const_iterator first, const_iterator last, bool merge_objects = false)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name()), *this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", *this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(first.m_object->type_name()), *first.m_object));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            if (merge_objects && it.value().is_object())\n            {\n                auto it2 = m_value.object->find(it.key());\n                if (it2 != m_value.object->end())\n                {\n                    it2->second.update(it.value(), true);\n                    continue;\n                }\n            }\n            m_value.object->operator[](it.key()) = it.value();\n#if JSON_DIAGNOSTICS\n            m_value.object->operator[](it.key()).m_parent = this;\n#endif\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n\n        set_parents();\n        other.set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value from @a left with those of @a right. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated. implemented as a friend function callable via ADL.\n\n    @param[in,out] left JSON value to exchange the contents with\n    @param[in,out] right JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    friend void swap(reference left, reference right) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        left.swap(right);\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON array with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other array to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an array; example: `\"cannot\n    use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how arrays can be swapped with\n    `swap()`.,swap__array_t}\n\n    @since version 1.0.0\n    */\n    void swap(array_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            std::swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON object with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other object to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an object; example:\n    `\"cannot use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how objects can be swapped with\n    `swap()`.,swap__object_t}\n\n    @since version 1.0.0\n    */\n    void swap(object_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            std::swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other string to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__string_t}\n\n    @since version 1.0.0\n    */\n    void swap(string_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            std::swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other binary to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__binary_t}\n\n    @since version 3.8.0\n    */\n    void swap(binary_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /// @copydoc swap(binary_t&)\n    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /// @}\n\n  public:\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    /*!\n    @brief comparison: equal\n\n    Compares two JSON values for equality according to the following rules:\n    - Two JSON values are equal if (1) they are from the same type and (2)\n      their stored values are the same according to their respective\n      `operator==`.\n    - Integer and floating-point numbers are automatically converted before\n      comparison. Note that two NaN values are always treated as unequal.\n    - Two JSON null values are equal.\n\n    @note Floating-point inside JSON values numbers are compared with\n    `json::number_float_t::operator==` which is `double::operator==` by\n    default. To compare floating-point while respecting an epsilon, an alternative\n    [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39)\n    could be used, for instance\n    @code {.cpp}\n    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>\n    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept\n    {\n        return std::abs(a - b) <= epsilon;\n    }\n    @endcode\n    Or you can self-defined operator equal function like this:\n    @code {.cpp}\n    bool my_equal(const_reference lhs, const_reference rhs) {\n    const auto lhs_type lhs.type();\n    const auto rhs_type rhs.type();\n    if (lhs_type == rhs_type) {\n        switch(lhs_type)\n            // self_defined case\n            case value_t::number_float:\n                return std::abs(lhs - rhs) <= std::numeric_limits<float>::epsilon();\n            // other cases remain the same with the original\n            ...\n    }\n    ...\n    }\n    @endcode\n\n    @note NaN values never compare equal to themselves or to other NaN values.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are equal\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Linear.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__equal}\n\n    @since version 1.0.0\n    */\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return *lhs.m_value.array == *rhs.m_value.array;\n\n                case value_t::object:\n                    return *lhs.m_value.object == *rhs.m_value.object;\n\n                case value_t::null:\n                    return true;\n\n                case value_t::string:\n                    return *lhs.m_value.string == *rhs.m_value.string;\n\n                case value_t::boolean:\n                    return lhs.m_value.boolean == rhs.m_value.boolean;\n\n                case value_t::number_integer:\n                    return lhs.m_value.number_integer == rhs.m_value.number_integer;\n\n                case value_t::number_unsigned:\n                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;\n\n                case value_t::number_float:\n                    return lhs.m_value.number_float == rhs.m_value.number_float;\n\n                case value_t::binary:\n                    return *lhs.m_value.binary == *rhs.m_value.binary;\n\n                case value_t::discarded:\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n\n        return false;\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /*!\n    @brief comparison: not equal\n\n    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are not equal\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__notequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /*!\n    @brief comparison: less than\n\n    Compares whether one JSON value @a lhs is less than another JSON value @a\n    rhs according to the following rules:\n    - If @a lhs and @a rhs have the same type, the values are compared using\n      the default `<` operator.\n    - Integer and floating-point numbers are automatically converted before\n      comparison\n    - In case @a lhs and @a rhs have different types, the values are ignored\n      and the order of the types is considered, see\n      @ref operator<(const value_t, const value_t).\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__less}\n\n    @since version 1.0.0\n    */\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    // note parentheses are necessary, see\n                    // https://github.com/nlohmann/json/issues/1530\n                    return (*lhs.m_value.array) < (*rhs.m_value.array);\n\n                case value_t::object:\n                    return (*lhs.m_value.object) < (*rhs.m_value.object);\n\n                case value_t::null:\n                    return false;\n\n                case value_t::string:\n                    return (*lhs.m_value.string) < (*rhs.m_value.string);\n\n                case value_t::boolean:\n                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);\n\n                case value_t::number_integer:\n                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);\n\n                case value_t::number_unsigned:\n                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);\n\n                case value_t::number_float:\n                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);\n\n                case value_t::binary:\n                    return (*lhs.m_value.binary) < (*rhs.m_value.binary);\n\n                case value_t::discarded:\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;\n        }\n\n        // We only reach this line if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        return operator<(lhs_type, rhs_type);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /*!\n    @brief comparison: less than or equal\n\n    Compares whether one JSON value @a lhs is less than or equal to another\n    JSON value by calculating `not (rhs < lhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greater}\n\n    @since version 1.0.0\n    */\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(rhs < lhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /*!\n    @brief comparison: greater than\n\n    Compares whether one JSON value @a lhs is greater than another\n    JSON value by calculating `not (lhs <= rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__lessequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n\n    Compares whether one JSON value @a lhs is greater than or equal to another\n    JSON value by calculating `not (lhs < rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greaterequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs < rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n#ifndef JSON_NO_IO\n    /*!\n    @brief serialize to stream\n\n    Serialize the given JSON value @a j to the output stream @a o. The JSON\n    value will be serialized using the @ref dump member function.\n\n    - The indentation of the output can be controlled with the member variable\n      `width` of the output stream @a o. For instance, using the manipulator\n      `std::setw(4)` on @a o sets the indentation level to `4` and the\n      serialization result is the same as calling `dump(4)`.\n\n    - The indentation character can be controlled with the member variable\n      `fill` of the output stream @a o. For instance, the manipulator\n      `std::setfill('\\\\t')` sets indentation to use a tab character rather than\n      the default space character.\n\n    @param[in,out] o  stream to serialize to\n    @param[in] j  JSON value to serialize\n\n    @return the stream @a o\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @liveexample{The example below shows the serialization with different\n    parameters to `width` to adjust the indentation level.,operator_serialize}\n\n    @since version 1.0.0; indentation character added in version 3.0.0\n    */\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /*!\n    @brief serialize to stream\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use\n                @ref operator<<(std::ostream&, const basic_json&)\n                instead; that is, replace calls like `j >> o;` with `o << j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /*!\n    @brief deserialize from a compatible input\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb or reading from the input @a i has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an array.,parse__array__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__string__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__istream__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}\n\n    @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to\n    ignore comments.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(InputType&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief deserialize from a pair of character iterators\n\n    The value_type of the iterator must be a integral type with size of 1, 2 or\n    4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32.\n\n    @param[in] first iterator to start of character range\n    @param[in] last  iterator to end of character range\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(IteratorType first,\n                            IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))\n    static basic_json parse(detail::span_input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief check if the input is valid JSON\n\n    Unlike the @ref parse(InputType&&, const parser_callback_t,const bool)\n    function, this function neither throws an exception in case of invalid JSON\n    input (i.e., a parse error) nor creates diagnostic information.\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i input to read from\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return Whether the input read from @a i is valid JSON.\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `accept()` function reading\n    from a string.,accept__string}\n    */\n    template<typename InputType>\n    static bool accept(InputType&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    template<typename IteratorType>\n    static bool accept(IteratorType first, IteratorType last,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))\n    static bool accept(detail::span_input_adapter&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(i.get(), nullptr, false, ignore_comments).accept(true);\n    }\n\n    /*!\n    @brief generate SAX events\n\n    The SAX event lister must follow the interface of @ref json_sax.\n\n    This function reads from a compatible input. Examples are:\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in,out] sax  SAX event listener\n    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)\n    @param[in] strict  whether the input has to be consumed completely\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default); only applies to the JSON file format.\n\n    @return return value of the last processed SAX event\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has\n    a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `sax_parse()` function\n    reading from string and processing the events with a user-defined SAX\n    event consumer.,sax_parse}\n\n    @since version 3.2.0\n    */\n    template <typename InputType, typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(InputType&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template<class IteratorType, class SAX>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template <typename SAX>\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = i.get();\n        return format == input_format_t::json\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n#ifndef JSON_NO_IO\n    /*!\n    @brief deserialize from stream\n    @deprecated This stream operator is deprecated and will be removed in\n                version 4.0.0 of the library. Please use\n                @ref operator>>(std::istream&, basic_json&)\n                instead; that is, replace calls like `j << i;` with `i >> j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /*!\n    @brief deserialize from stream\n\n    Deserializes an input stream to a JSON value.\n\n    @param[in,out] i  input stream to read a serialized JSON value from\n    @param[in,out] j  JSON value to write the deserialized input to\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below shows how a JSON value is constructed by\n    reading a serialization from a stream.,operator_deserialize}\n\n    @sa parse(std::istream&, const parser_callback_t) for a variant with a\n    parser callback function to filter values while parsing\n\n    @since version 1.0.0\n    */\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /*!\n    @brief return the type as string\n\n    Returns the type name as string to be used in error messages - usually to\n    indicate that a function was called on a wrong JSON type.\n\n    @return a string representation of a the @a m_type member:\n            Value type  | return value\n            ----------- | -------------\n            null        | `\"null\"`\n            boolean     | `\"boolean\"`\n            string      | `\"string\"`\n            number      | `\"number\"` (for all number types)\n            object      | `\"object\"`\n            array       | `\"array\"`\n            binary      | `\"binary\"`\n            discarded   | `\"discarded\"`\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Constant.\n\n    @liveexample{The following code exemplifies `type_name()` for all JSON\n    types.,type_name}\n\n    @sa see @ref type() -- return the type of the JSON value\n    @sa see @ref operator value_t() -- return the type of the JSON value (implicit)\n\n    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`\n    since 3.0.0\n    */\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        {\n            switch (m_type)\n            {\n                case value_t::null:\n                    return \"null\";\n                case value_t::object:\n                    return \"object\";\n                case value_t::array:\n                    return \"array\";\n                case value_t::string:\n                    return \"string\";\n                case value_t::boolean:\n                    return \"boolean\";\n                case value_t::binary:\n                    return \"binary\";\n                case value_t::discarded:\n                    return \"discarded\";\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                default:\n                    return \"number\";\n            }\n        }\n    }\n\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n#if JSON_DIAGNOSTICS\n    /// a pointer to a parent value (for debugging purposes)\n    basic_json* m_parent = nullptr;\n#endif\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /*!\n    @brief create a CBOR serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise\n    Binary Object Representation) serialization format. CBOR is a binary\n    serialization format which aims to be more compact than JSON itself, yet\n    more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    CBOR types according to the CBOR specification (RFC 7049):\n\n    JSON value type | value/range                                | CBOR type                          | first byte\n    --------------- | ------------------------------------------ | ---------------------------------- | ---------------\n    null            | `null`                                     | Null                               | 0xF6\n    boolean         | `true`                                     | True                               | 0xF5\n    boolean         | `false`                                    | False                              | 0xF4\n    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B\n    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A\n    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39\n    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38\n    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37\n    number_integer  | 0..23                                      | Integer                            | 0x00..0x17\n    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17\n    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_float    | *any value representable by a float*       | Single-Precision Float             | 0xFA\n    number_float    | *any value NOT representable by a float*   | Double-Precision Float             | 0xFB\n    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77\n    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78\n    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79\n    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A\n    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B\n    array           | *size*: 0..23                              | array                              | 0x80..0x97\n    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98\n    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99\n    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A\n    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B\n    object          | *size*: 0..23                              | map                                | 0xA0..0xB7\n    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8\n    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9\n    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA\n    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB\n    binary          | *size*: 0..23                              | byte string                        | 0x40..0x57\n    binary          | *size*: 23..255                            | byte string (1 byte follow)        | 0x58\n    binary          | *size*: 256..65535                         | byte string (2 bytes follow)       | 0x59\n    binary          | *size*: 65536..4294967295                  | byte string (4 bytes follow)       | 0x5A\n    binary          | *size*: 4294967296..18446744073709551615   | byte string (8 bytes follow)       | 0x5B\n\n    Binary values with subtype are mapped to tagged values (0xD8..0xDB)\n    depending on the subtype, followed by a byte string, see \"binary\" cells\n    in the table above.\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a CBOR value.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The following CBOR types are not used in the conversion:\n          - UTF-8 strings terminated by \"break\" (0x7F)\n          - arrays terminated by \"break\" (0x9F)\n          - maps terminated by \"break\" (0xBF)\n          - byte strings terminated by \"break\" (0x5F)\n          - date/time (0xC0..0xC1)\n          - bignum (0xC2..0xC3)\n          - decimal fraction (0xC4)\n          - bigfloat (0xC5)\n          - expected conversions (0xD5..0xD7)\n          - simple values (0xE0..0xF3, 0xF8)\n          - undefined (0xF7)\n          - half-precision floats (0xF9)\n          - break (0xFF)\n\n    @param[in] j  JSON value to serialize\n    @return CBOR serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in CBOR format.,to_cbor}\n\n    @sa http://cbor.io\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        analogous deserialization\n    @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9; compact representation of floating-point numbers\n           since version 3.8.0\n    */\n    static std::vector<std::uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_cbor(j);\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /*!\n    @brief create a MessagePack serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the MessagePack\n    serialization format. MessagePack is a binary serialization format which\n    aims to be more compact than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    MessagePack types according to the MessagePack specification:\n\n    JSON value type | value/range                       | MessagePack type | first byte\n    --------------- | --------------------------------- | ---------------- | ----------\n    null            | `null`                            | nil              | 0xC0\n    boolean         | `true`                            | true             | 0xC3\n    boolean         | `false`                           | false            | 0xC2\n    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3\n    number_integer  | -2147483648..-32769               | int32            | 0xD2\n    number_integer  | -32768..-129                      | int16            | 0xD1\n    number_integer  | -128..-33                         | int8             | 0xD0\n    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF\n    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F\n    number_integer  | 128..255                          | uint 8           | 0xCC\n    number_integer  | 256..65535                        | uint 16          | 0xCD\n    number_integer  | 65536..4294967295                 | uint 32          | 0xCE\n    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F\n    number_unsigned | 128..255                          | uint 8           | 0xCC\n    number_unsigned | 256..65535                        | uint 16          | 0xCD\n    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE\n    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_float    | *any value representable by a float*     | float 32 | 0xCA\n    number_float    | *any value NOT representable by a float* | float 64 | 0xCB\n    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF\n    string          | *length*: 32..255                 | str 8            | 0xD9\n    string          | *length*: 256..65535              | str 16           | 0xDA\n    string          | *length*: 65536..4294967295       | str 32           | 0xDB\n    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F\n    array           | *size*: 16..65535                 | array 16         | 0xDC\n    array           | *size*: 65536..4294967295         | array 32         | 0xDD\n    object          | *size*: 0..15                     | fix map          | 0x80..0x8F\n    object          | *size*: 16..65535                 | map 16           | 0xDE\n    object          | *size*: 65536..4294967295         | map 32           | 0xDF\n    binary          | *size*: 0..255                    | bin 8            | 0xC4\n    binary          | *size*: 256..65535                | bin 16           | 0xC5\n    binary          | *size*: 65536..4294967295         | bin 32           | 0xC6\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a MessagePack value.\n\n    @note The following values can **not** be converted to a MessagePack value:\n          - strings with more than 4294967295 bytes\n          - byte strings with more than 4294967295 bytes\n          - arrays with more than 4294967295 elements\n          - objects with more than 4294967295 elements\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in MessagePack format.,to_msgpack}\n\n    @sa http://msgpack.org\n    @sa see @ref from_msgpack for the analogous deserialization\n    @sa see @ref to_cbor(const basic_json& for the related CBOR format\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_msgpack(j);\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /*!\n    @brief create a UBJSON serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the UBJSON\n    (Universal Binary JSON) serialization format. UBJSON aims to be more compact\n    than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    UBJSON types according to the UBJSON specification:\n\n    JSON value type | value/range                       | UBJSON type | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | `Z`\n    boolean         | `true`                            | true        | `T`\n    boolean         | `false`                           | false       | `F`\n    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`\n    number_integer  | -2147483648..-32769               | int32       | `l`\n    number_integer  | -32768..-129                      | int16       | `I`\n    number_integer  | -128..127                         | int8        | `i`\n    number_integer  | 128..255                          | uint8       | `U`\n    number_integer  | 256..32767                        | int16       | `I`\n    number_integer  | 32768..2147483647                 | int32       | `l`\n    number_integer  | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 0..127                            | int8        | `i`\n    number_unsigned | 128..255                          | uint8       | `U`\n    number_unsigned | 256..32767                        | int16       | `I`\n    number_unsigned | 32768..2147483647                 | int32       | `l`\n    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 2147483649..18446744073709551615  | high-precision | `H`\n    number_float    | *any value*                       | float64     | `D`\n    string          | *with shortest length indicator*  | string      | `S`\n    array           | *see notes on optimized format*   | array       | `[`\n    object          | *see notes on optimized format*   | map         | `{`\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a UBJSON value.\n\n    @note The following values can **not** be converted to a UBJSON value:\n          - strings with more than 9223372036854775807 bytes (theoretical)\n\n    @note The following markers are not used in the conversion:\n          - `Z`: no-op values are not created.\n          - `C`: single-byte strings are serialized with `S` markers.\n\n    @note Any UBJSON output created @ref to_ubjson can be successfully parsed\n          by @ref from_ubjson.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The optimized formats for containers are supported: Parameter\n          @a use_size adds size information to the beginning of a container and\n          removes the closing marker. Parameter @a use_type further checks\n          whether all elements of a container have the same type and adds the\n          type marker to the beginning of the container. The @a use_type\n          parameter must only be used together with @a use_size = true. Note\n          that @a use_size = true alone may result in larger representations -\n          the benefit of this parameter is that the receiving side is\n          immediately informed on the number of elements of the container.\n\n    @note If the JSON data contains the binary type, the value stored is a list\n          of integers, as suggested by the UBJSON documentation.  In particular,\n          this means that serialization and the deserialization of a JSON\n          containing binary values into UBJSON and back will result in a\n          different JSON object.\n\n    @param[in] j  JSON value to serialize\n    @param[in] use_size  whether to add size annotations to container types\n    @param[in] use_type  whether to add type annotations to container types\n                         (must be combined with @a use_size = true)\n    @return UBJSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in UBJSON format.,to_ubjson}\n\n    @sa http://ubjson.org\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the\n        analogous deserialization\n    @sa see @ref to_cbor(const basic_json& for the related CBOR format\n    @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format\n\n    @since version 3.1.0\n    */\n    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,\n            const bool use_size = false,\n            const bool use_type = false)\n    {\n        std::vector<std::uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and returns a vector\n           containing the corresponding BSON-representation.\n\n    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are\n    stored as a single entity (a so-called document).\n\n    The library uses the following mapping from JSON values types to BSON types:\n\n    JSON value type | value/range                       | BSON type   | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | 0x0A\n    boolean         | `true`, `false`                   | boolean     | 0x08\n    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12\n    number_integer  | -2147483648..2147483647           | int32       | 0x10\n    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 0..2147483647                     | int32       | 0x10\n    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 9223372036854775808..18446744073709551615| --   | --\n    number_float    | *any value*                       | double      | 0x01\n    string          | *any value*                       | string      | 0x02\n    array           | *any value*                       | document    | 0x04\n    object          | *any value*                       | document    | 0x03\n    binary          | *any value*                       | binary      | 0x05\n\n    @warning The mapping is **incomplete**, since only JSON-objects (and things\n    contained therein) can be serialized to BSON.\n    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,\n    and the keys may not contain U+0000, since they are serialized a\n    zero-terminated c-strings.\n\n    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`\n    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)\n    @throw type_error.317    if `!j.is_object()`\n\n    @pre The input `j` is required to be an object: `j.is_object() == true`.\n\n    @note Any BSON output created via @ref to_bson can be successfully parsed\n          by @ref from_bson.\n\n    @param[in] j  JSON value to serialize\n    @return BSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in BSON format.,to_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa see @ref from_bson(detail::input_adapter&&, const bool strict) for the\n        analogous deserialization\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n    @sa see @ref to_cbor(const basic_json&) for the related CBOR format\n    @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format\n    */\n    static std::vector<std::uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and forwards the\n           corresponding BSON-representation to the given output_adapter `o`.\n    @param j The JSON object to convert to BSON.\n    @param o The output adapter that receives the binary BSON representation.\n    @pre The input `j` shall be an object: `j.is_object() == true`\n    @sa see @ref to_bson(const basic_json&)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_bson(j);\n    }\n\n    /*!\n    @copydoc to_bson(const basic_json&, detail::output_adapter<std::uint8_t>)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in CBOR format\n\n    Deserializes a given input @a i to a JSON value using the CBOR (Concise\n    Binary Object Representation) serialization format.\n\n    The library maps CBOR types to JSON value types as follows:\n\n    CBOR type              | JSON value type | first byte\n    ---------------------- | --------------- | ----------\n    Integer                | number_unsigned | 0x00..0x17\n    Unsigned integer       | number_unsigned | 0x18\n    Unsigned integer       | number_unsigned | 0x19\n    Unsigned integer       | number_unsigned | 0x1A\n    Unsigned integer       | number_unsigned | 0x1B\n    Negative integer       | number_integer  | 0x20..0x37\n    Negative integer       | number_integer  | 0x38\n    Negative integer       | number_integer  | 0x39\n    Negative integer       | number_integer  | 0x3A\n    Negative integer       | number_integer  | 0x3B\n    Byte string            | binary          | 0x40..0x57\n    Byte string            | binary          | 0x58\n    Byte string            | binary          | 0x59\n    Byte string            | binary          | 0x5A\n    Byte string            | binary          | 0x5B\n    UTF-8 string           | string          | 0x60..0x77\n    UTF-8 string           | string          | 0x78\n    UTF-8 string           | string          | 0x79\n    UTF-8 string           | string          | 0x7A\n    UTF-8 string           | string          | 0x7B\n    UTF-8 string           | string          | 0x7F\n    array                  | array           | 0x80..0x97\n    array                  | array           | 0x98\n    array                  | array           | 0x99\n    array                  | array           | 0x9A\n    array                  | array           | 0x9B\n    array                  | array           | 0x9F\n    map                    | object          | 0xA0..0xB7\n    map                    | object          | 0xB8\n    map                    | object          | 0xB9\n    map                    | object          | 0xBA\n    map                    | object          | 0xBB\n    map                    | object          | 0xBF\n    False                  | `false`         | 0xF4\n    True                   | `true`          | 0xF5\n    Null                   | `null`          | 0xF6\n    Half-Precision Float   | number_float    | 0xF9\n    Single-Precision Float | number_float    | 0xFA\n    Double-Precision Float | number_float    | 0xFB\n\n    @warning The mapping is **incomplete** in the sense that not all CBOR\n             types can be converted to a JSON value. The following CBOR types\n             are not supported and will yield parse errors (parse_error.112):\n             - date/time (0xC0..0xC1)\n             - bignum (0xC2..0xC3)\n             - decimal fraction (0xC4)\n             - bigfloat (0xC5)\n             - expected conversions (0xD5..0xD7)\n             - simple values (0xE0..0xF3, 0xF8)\n             - undefined (0xF7)\n\n    @warning CBOR allows map keys of any type, whereas JSON only allows\n             strings as keys in object values. Therefore, CBOR maps with keys\n             other than UTF-8 strings are rejected (parse_error.113).\n\n    @note Any CBOR output created @ref to_cbor can be successfully parsed by\n          @ref from_cbor.\n\n    @param[in] i  an input in CBOR format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] tag_handler how to treat CBOR tags (optional, error by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from CBOR were\n    used in the given input @a v or if the input is not valid CBOR\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in CBOR\n    format to a JSON value.,from_cbor}\n\n    @sa http://cbor.io\n    @sa see @ref to_cbor(const basic_json&) for the analogous serialization\n    @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the\n        related MessagePack format\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the\n        related UBJSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0; added @a tag_handler parameter since 3.9.0.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);\n    }\n\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in MessagePack format\n\n    Deserializes a given input @a i to a JSON value using the MessagePack\n    serialization format.\n\n    The library maps MessagePack types to JSON value types as follows:\n\n    MessagePack type | JSON value type | first byte\n    ---------------- | --------------- | ----------\n    positive fixint  | number_unsigned | 0x00..0x7F\n    fixmap           | object          | 0x80..0x8F\n    fixarray         | array           | 0x90..0x9F\n    fixstr           | string          | 0xA0..0xBF\n    nil              | `null`          | 0xC0\n    false            | `false`         | 0xC2\n    true             | `true`          | 0xC3\n    float 32         | number_float    | 0xCA\n    float 64         | number_float    | 0xCB\n    uint 8           | number_unsigned | 0xCC\n    uint 16          | number_unsigned | 0xCD\n    uint 32          | number_unsigned | 0xCE\n    uint 64          | number_unsigned | 0xCF\n    int 8            | number_integer  | 0xD0\n    int 16           | number_integer  | 0xD1\n    int 32           | number_integer  | 0xD2\n    int 64           | number_integer  | 0xD3\n    str 8            | string          | 0xD9\n    str 16           | string          | 0xDA\n    str 32           | string          | 0xDB\n    array 16         | array           | 0xDC\n    array 32         | array           | 0xDD\n    map 16           | object          | 0xDE\n    map 32           | object          | 0xDF\n    bin 8            | binary          | 0xC4\n    bin 16           | binary          | 0xC5\n    bin 32           | binary          | 0xC6\n    ext 8            | binary          | 0xC7\n    ext 16           | binary          | 0xC8\n    ext 32           | binary          | 0xC9\n    fixext 1         | binary          | 0xD4\n    fixext 2         | binary          | 0xD5\n    fixext 4         | binary          | 0xD6\n    fixext 8         | binary          | 0xD7\n    fixext 16        | binary          | 0xD8\n    negative fixint  | number_integer  | 0xE0-0xFF\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @param[in] i  an input in MessagePack format convertible to an input\n                  adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from MessagePack were\n    used in the given input @a i or if the input is not valid MessagePack\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    MessagePack format to a JSON value.,from_msgpack}\n\n    @sa http://msgpack.org\n    @sa see @ref to_msgpack(const basic_json&) for the analogous serialization\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for\n        the related UBJSON format\n    @sa see @ref from_bson(InputType&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(InputType&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_msgpack(InputType&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(IteratorType first, IteratorType last,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(const T* ptr, std::size_t len,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(detail::span_input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in UBJSON format\n\n    Deserializes a given input @a i to a JSON value using the UBJSON (Universal\n    Binary JSON) serialization format.\n\n    The library maps UBJSON types to JSON value types as follows:\n\n    UBJSON type | JSON value type                         | marker\n    ----------- | --------------------------------------- | ------\n    no-op       | *no value, next value is read*          | `N`\n    null        | `null`                                  | `Z`\n    false       | `false`                                 | `F`\n    true        | `true`                                  | `T`\n    float32     | number_float                            | `d`\n    float64     | number_float                            | `D`\n    uint8       | number_unsigned                         | `U`\n    int8        | number_integer                          | `i`\n    int16       | number_integer                          | `I`\n    int32       | number_integer                          | `l`\n    int64       | number_integer                          | `L`\n    high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H'\n    string      | string                                  | `S`\n    char        | string                                  | `C`\n    array       | array (optimized values are supported)  | `[`\n    object      | object (optimized values are supported) | `{`\n\n    @note The mapping is **complete** in the sense that any UBJSON value can\n          be converted to a JSON value.\n\n    @param[in] i  an input in UBJSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if a parse error occurs\n    @throw parse_error.113 if a string could not be parsed successfully\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    UBJSON format to a JSON value.,from_ubjson}\n\n    @sa http://ubjson.org\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             analogous serialization\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa see @ref from_msgpack(InputType&&, const bool, const bool) for\n        the related MessagePack format\n    @sa see @ref from_bson(InputType&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_ubjson(InputType&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(const T* ptr, std::size_t len,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(detail::span_input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief Create a JSON value from an input in BSON format\n\n    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)\n    serialization format.\n\n    The library maps BSON record types to JSON value types as follows:\n\n    BSON type       | BSON marker byte | JSON value type\n    --------------- | ---------------- | ---------------------------\n    double          | 0x01             | number_float\n    string          | 0x02             | string\n    document        | 0x03             | object\n    array           | 0x04             | array\n    binary          | 0x05             | binary\n    undefined       | 0x06             | still unsupported\n    ObjectId        | 0x07             | still unsupported\n    boolean         | 0x08             | boolean\n    UTC Date-Time   | 0x09             | still unsupported\n    null            | 0x0A             | null\n    Regular Expr.   | 0x0B             | still unsupported\n    DB Pointer      | 0x0C             | still unsupported\n    JavaScript Code | 0x0D             | still unsupported\n    Symbol          | 0x0E             | still unsupported\n    JavaScript Code | 0x0F             | still unsupported\n    int32           | 0x10             | number_integer\n    Timestamp       | 0x11             | still unsupported\n    128-bit decimal float | 0x13       | still unsupported\n    Max Key         | 0x7F             | still unsupported\n    Min Key         | 0xFF             | still unsupported\n\n    @warning The mapping is **incomplete**. The unsupported mappings\n             are indicated in the table above.\n\n    @param[in] i  an input in BSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.114 if an unsupported BSON record type is encountered\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    BSON format to a JSON value.,from_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa see @ref to_bson(const basic_json&) for the analogous serialization\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa see @ref from_msgpack(InputType&&, const bool, const bool) for\n        the related MessagePack format\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the\n        related UBJSON format\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_bson(InputType&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        return from_bson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. Similar to @ref operator[](const typename\n    object_t::key_type&), `null` values are created in arrays and objects if\n    necessary.\n\n    In particular:\n    - If the JSON pointer points to an object key that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned.\n    - If the JSON pointer points to an array index that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned. All indices between the current maximum and the given\n      index are also filled with `null`.\n    - The special value `-` is treated as a synonym for the index past the\n      end.\n\n    @param[in] ptr  a JSON pointer\n\n    @return reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer}\n\n    @since version 2.0.0\n    */\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. The function does not change the JSON\n    value; no `null` values are created. In particular, the special value\n    `-` yields an exception.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return const reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}\n\n    @since version 2.0.0\n    */\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a reference to the element at with specified JSON pointer @a ptr,\n    with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer}\n    */\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a const reference to the element at with specified JSON pointer @a\n    ptr, with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer_const}\n    */\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief return flattened JSON value\n\n    The function creates a JSON object whose keys are JSON pointers (see [RFC\n    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all\n    primitive. The original JSON value can be restored using the @ref\n    unflatten() function.\n\n    @return an object that maps JSON pointers to primitive values\n\n    @note Empty objects and arrays are flattened to `null` and will not be\n          reconstructed correctly by the @ref unflatten() function.\n\n    @complexity Linear in the size the JSON value.\n\n    @liveexample{The following code shows how a JSON object is flattened to an\n    object whose keys consist of JSON pointers.,flatten}\n\n    @sa see @ref unflatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /*!\n    @brief unflatten a previously flattened JSON value\n\n    The function restores the arbitrary nesting of a JSON value that has been\n    flattened before using the @ref flatten() function. The JSON value must\n    meet certain constraints:\n    1. The value must be an object.\n    2. The keys must be JSON pointers (see\n       [RFC 6901](https://tools.ietf.org/html/rfc6901))\n    3. The mapped values must be primitive JSON types.\n\n    @return the original JSON from a flattened version\n\n    @note Empty objects and arrays are flattened by @ref flatten() to `null`\n          values and can not unflattened to their original type. Apart from\n          this example, for a JSON value `j`, the following is always true:\n          `j == j.flatten().unflatten()`.\n\n    @complexity Linear in the size the JSON value.\n\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n\n    @liveexample{The following code shows how a flattened JSON object is\n    unflattened into the original nested JSON object.,unflatten}\n\n    @sa see @ref flatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON patch\n\n    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for\n    expressing a sequence of operations to apply to a JSON) document. With\n    this function, a JSON Patch is applied to the current JSON value by\n    executing all operations from the patch.\n\n    @param[in] json_patch  JSON patch document\n    @return patched document\n\n    @note The application of a patch is atomic: Either all operations succeed\n          and the patched document is returned or an exception is thrown. In\n          any case, the original value is not changed: the patch is applied\n          to a copy of the value.\n\n    @throw parse_error.104 if the JSON patch does not consist of an array of\n    objects\n\n    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory\n    attributes are missing); example: `\"operation add must have member path\"`\n\n    @throw out_of_range.401 if an array index is out of range.\n\n    @throw out_of_range.403 if a JSON pointer inside the patch could not be\n    resolved successfully in the current JSON value; example: `\"key baz not\n    found\"`\n\n    @throw out_of_range.405 if JSON pointer has no parent (\"add\", \"remove\",\n    \"move\")\n\n    @throw other_error.501 if \"test\" operation was unsuccessful\n\n    @complexity Linear in the size of the JSON value and the length of the\n    JSON patch. As usually only a fraction of the JSON value is affected by\n    the patch, the complexity can usually be neglected.\n\n    @liveexample{The following code shows how a JSON patch is applied to a\n    value.,patch}\n\n    @sa see @ref diff -- create a JSON patch by comparing two JSON values\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)\n\n    @since version 2.0.0\n    */\n    basic_json patch(const basic_json& json_patch) const\n    {\n        // make a working copy to apply the patch to\n        basic_json result = *this;\n\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result[ptr];\n\n            switch (parent.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::array_index(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", parent));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent it cannot be primitive\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [this, &result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, \"key '\" + last_path + \"' not found\", *this));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(json_pointer::array_index(last_path));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", json_patch));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : \"operation '\" + op + \"'\";\n\n                // check if desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have member '\" + member + \"'\", val));\n                }\n\n                // check if result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have string member '\" + member + \"'\", val));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", val));\n            }\n\n            // collect mandatory members\n            const auto op = get_value(\"op\", \"op\", true).template get<std::string>();\n            const auto path = get_value(op, \"path\", true).template get<std::string>();\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const auto from_path = get_value(\"move\", \"from\", true).template get<std::string>();\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const auto from_path = get_value(\"copy\", \"from\", true).template get<std::string>();\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_HEDLEY_UNLIKELY(!success))\n                    {\n                        JSON_THROW(other_error::create(501, \"unsuccessful: \" + val.dump(), val));\n                    }\n\n                    break;\n                }\n\n                case patch_operations::invalid:\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, \"operation value '\" + op + \"' is invalid\", val));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief creates a diff as a JSON patch\n\n    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can\n    be changed into the value @a target by calling @ref patch function.\n\n    @invariant For two JSON values @a source and @a target, the following code\n    yields always `true`:\n    @code {.cpp}\n    source.patch(diff(source, target)) == target;\n    @endcode\n\n    @note Currently, only `remove`, `add`, and `replace` operations are\n          generated.\n\n    @param[in] source  JSON value to compare from\n    @param[in] target  JSON value to compare against\n    @param[in] path    helper value to create JSON pointers\n\n    @return a JSON patch to convert the @a source to @a target\n\n    @complexity Linear in the lengths of @a source and @a target.\n\n    @liveexample{The following code shows how a JSON patch is created as a\n    diff for two JSON values.,diff}\n\n    @sa see @ref patch -- apply a JSON patch\n    @sa see @ref merge_patch -- apply a JSON Merge Patch\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n\n    @since version 2.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() && i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], path + \"/\" + std::to_string(i));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // i now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", path + \"/\" + std::to_string(i)}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", path + \"/-\"},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto path_key = path + \"/\" + detail::escape(it.key());\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path_key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path_key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto path_key = path + \"/\" + detail::escape(it.key());\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path_key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // both primitive type: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON Merge Patch\n\n    The merge patch format is primarily intended for use with the HTTP PATCH\n    method as a means of describing a set of modifications to a target\n    resource's content. This function applies a merge patch to the current\n    JSON value.\n\n    The function implements the following algorithm from Section 2 of\n    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):\n\n    ```\n    define MergePatch(Target, Patch):\n      if Patch is an Object:\n        if Target is not an Object:\n          Target = {} // Ignore the contents and set it to an empty Object\n        for each Name/Value pair in Patch:\n          if Value is null:\n            if Name exists in Target:\n              remove the Name/Value pair from Target\n          else:\n            Target[Name] = MergePatch(Target[Name], Value)\n        return Target\n      else:\n        return Patch\n    ```\n\n    Thereby, `Target` is the current object; that is, the patch is applied to\n    the current value.\n\n    @param[in] apply_patch  the patch to apply\n\n    @complexity Linear in the lengths of @a patch.\n\n    @liveexample{The following code shows how a JSON Merge Patch is applied to\n    a JSON document.,merge_patch}\n\n    @sa see @ref patch -- apply a JSON patch\n    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)\n\n    @since version 3.0.0\n    */\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (!is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/*!\n@brief user-defined to_string function for JSON values\n\nThis function implements a user-defined to_string  for JSON objects.\n\n@param[in] j  a JSON object\n@return a std::string object\n*/\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n} // namespace nlohmann\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\n// specialization of std::swap, and std::hash\nnamespace std\n{\n\n/// hash value for JSON objects\ntemplate<>\nstruct hash<nlohmann::json>\n{\n    /*!\n    @brief return a hash value for a JSON object\n\n    @since version 1.0.0\n    */\n    std::size_t operator()(const nlohmann::json& j) const\n    {\n        return nlohmann::detail::hash(j);\n    }\n};\n\n/// specialization for std::less<value_t>\n/// @note: do not remove the space after '<',\n///        see https://github.com/nlohmann/json/pull/679\ntemplate<>\nstruct less<::nlohmann::detail::value_t>\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(nlohmann::detail::value_t lhs,\n                    nlohmann::detail::value_t rhs) const noexcept\n    {\n        return nlohmann::detail::operator<(lhs, rhs);\n    }\n};\n\n// C++20 prohibit function specialization in the std namespace.\n#ifndef JSON_HAS_CPP_20\n\n/*!\n@brief exchanges the values of two JSON objects\n\n@since version 1.0.0\n*/\ntemplate<>\ninline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name)\n    is_nothrow_move_constructible<nlohmann::json>::value&&  // NOLINT(misc-redundant-expression)\n    is_nothrow_move_assignable<nlohmann::json>::value\n                              )\n{\n    j1.swap(j2);\n}\n\n#endif\n\n} // namespace std\n\n/*!\n@brief user-defined string literal for JSON values\n\nThis operator implements a user-defined string literal for JSON objects. It\ncan be used by adding `\"_json\"` to a string literal and returns a JSON object\nif no parse error occurred.\n\n@param[in] s  a string representation of a JSON object\n@param[in] n  the length of string @a s\n@return a JSON object\n\n@since version 1.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/*!\n@brief user-defined string literal for JSON pointer\n\nThis operator implements a user-defined string literal for JSON Pointers. It\ncan be used by adding `\"_json_pointer\"` to a string literal and returns a JSON pointer\nobject if no parse error occurred.\n\n@param[in] s  a string representation of a JSON Pointer\n@param[in] n  the length of string @a s\n@return a JSON pointer object\n\n@since version 2.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n// #include <nlohmann/detail/macro_unscope.hpp>\n\n\n// restore clang diagnostic settings\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n\n// clean up\n#undef JSON_ASSERT\n#undef JSON_INTERNAL_CATCH\n#undef JSON_CATCH\n#undef JSON_THROW\n#undef JSON_TRY\n#undef JSON_PRIVATE_UNLESS_TESTED\n#undef JSON_HAS_CPP_11\n#undef JSON_HAS_CPP_14\n#undef JSON_HAS_CPP_17\n#undef JSON_HAS_CPP_20\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n#undef JSON_EXPLICIT\n#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL\n\n// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n\n\n#undef JSON_HEDLEY_ALWAYS_INLINE\n#undef JSON_HEDLEY_ARM_VERSION\n#undef JSON_HEDLEY_ARM_VERSION_CHECK\n#undef JSON_HEDLEY_ARRAY_PARAM\n#undef JSON_HEDLEY_ASSUME\n#undef JSON_HEDLEY_BEGIN_C_DECLS\n#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#undef JSON_HEDLEY_CLANG_HAS_WARNING\n#undef JSON_HEDLEY_COMPCERT_VERSION\n#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#undef JSON_HEDLEY_CONCAT\n#undef JSON_HEDLEY_CONCAT3\n#undef JSON_HEDLEY_CONCAT3_EX\n#undef JSON_HEDLEY_CONCAT_EX\n#undef JSON_HEDLEY_CONST\n#undef JSON_HEDLEY_CONSTEXPR\n#undef JSON_HEDLEY_CONST_CAST\n#undef JSON_HEDLEY_CPP_CAST\n#undef JSON_HEDLEY_CRAY_VERSION\n#undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#undef JSON_HEDLEY_C_DECL\n#undef JSON_HEDLEY_DEPRECATED\n#undef JSON_HEDLEY_DEPRECATED_FOR\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#undef JSON_HEDLEY_DIAGNOSTIC_POP\n#undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#undef JSON_HEDLEY_DMC_VERSION\n#undef JSON_HEDLEY_DMC_VERSION_CHECK\n#undef JSON_HEDLEY_EMPTY_BASES\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#undef JSON_HEDLEY_END_C_DECLS\n#undef JSON_HEDLEY_FLAGS\n#undef JSON_HEDLEY_FLAGS_CAST\n#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#undef JSON_HEDLEY_GCC_HAS_FEATURE\n#undef JSON_HEDLEY_GCC_HAS_WARNING\n#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#undef JSON_HEDLEY_GCC_VERSION\n#undef JSON_HEDLEY_GCC_VERSION_CHECK\n#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#undef JSON_HEDLEY_GNUC_HAS_WARNING\n#undef JSON_HEDLEY_GNUC_VERSION\n#undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#undef JSON_HEDLEY_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_BUILTIN\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_EXTENSION\n#undef JSON_HEDLEY_HAS_FEATURE\n#undef JSON_HEDLEY_HAS_WARNING\n#undef JSON_HEDLEY_IAR_VERSION\n#undef JSON_HEDLEY_IAR_VERSION_CHECK\n#undef JSON_HEDLEY_IBM_VERSION\n#undef JSON_HEDLEY_IBM_VERSION_CHECK\n#undef JSON_HEDLEY_IMPORT\n#undef JSON_HEDLEY_INLINE\n#undef JSON_HEDLEY_INTEL_CL_VERSION\n#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#undef JSON_HEDLEY_INTEL_VERSION\n#undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#undef JSON_HEDLEY_IS_CONSTANT\n#undef JSON_HEDLEY_IS_CONSTEXPR_\n#undef JSON_HEDLEY_LIKELY\n#undef JSON_HEDLEY_MALLOC\n#undef JSON_HEDLEY_MCST_LCC_VERSION\n#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#undef JSON_HEDLEY_MESSAGE\n#undef JSON_HEDLEY_MSVC_VERSION\n#undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#undef JSON_HEDLEY_NEVER_INLINE\n#undef JSON_HEDLEY_NON_NULL\n#undef JSON_HEDLEY_NO_ESCAPE\n#undef JSON_HEDLEY_NO_RETURN\n#undef JSON_HEDLEY_NO_THROW\n#undef JSON_HEDLEY_NULL\n#undef JSON_HEDLEY_PELLES_VERSION\n#undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#undef JSON_HEDLEY_PGI_VERSION\n#undef JSON_HEDLEY_PGI_VERSION_CHECK\n#undef JSON_HEDLEY_PREDICT\n#undef JSON_HEDLEY_PRINTF_FORMAT\n#undef JSON_HEDLEY_PRIVATE\n#undef JSON_HEDLEY_PUBLIC\n#undef JSON_HEDLEY_PURE\n#undef JSON_HEDLEY_REINTERPRET_CAST\n#undef JSON_HEDLEY_REQUIRE\n#undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#undef JSON_HEDLEY_REQUIRE_MSG\n#undef JSON_HEDLEY_RESTRICT\n#undef JSON_HEDLEY_RETURNS_NON_NULL\n#undef JSON_HEDLEY_SENTINEL\n#undef JSON_HEDLEY_STATIC_ASSERT\n#undef JSON_HEDLEY_STATIC_CAST\n#undef JSON_HEDLEY_STRINGIFY\n#undef JSON_HEDLEY_STRINGIFY_EX\n#undef JSON_HEDLEY_SUNPRO_VERSION\n#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#undef JSON_HEDLEY_TINYC_VERSION\n#undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#undef JSON_HEDLEY_TI_ARMCL_VERSION\n#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL2000_VERSION\n#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL430_VERSION\n#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL6X_VERSION\n#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL7X_VERSION\n#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CLPRU_VERSION\n#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#undef JSON_HEDLEY_TI_VERSION\n#undef JSON_HEDLEY_TI_VERSION_CHECK\n#undef JSON_HEDLEY_UNAVAILABLE\n#undef JSON_HEDLEY_UNLIKELY\n#undef JSON_HEDLEY_UNPREDICTABLE\n#undef JSON_HEDLEY_UNREACHABLE\n#undef JSON_HEDLEY_UNREACHABLE_RETURN\n#undef JSON_HEDLEY_VERSION\n#undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#undef JSON_HEDLEY_VERSION_ENCODE\n#undef JSON_HEDLEY_WARNING\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#undef JSON_HEDLEY_FALL_THROUGH\n\n\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\nset(SIBR_PROJECT \"hierarchyviewer\")\nproject(sibr_${SIBR_PROJECT} LANGUAGES CXX)\n\nsibr_gitlibrary(TARGET CudaDiffRasterizer\n    GIT_REPOSITORY \t\"https://github.com/graphdeco-inria/hierarchy-rasterizer.git\"\n    GIT_TAG\t\t\t\"75d513869f2d60ba205240e1a77012127e5ea142\"\n)\n\nsibr_gitlibrary(TARGET GaussianHierarchy\n    GIT_REPOSITORY \t\"https://github.com/graphdeco-inria/gaussian-hierarchy.git\"\n    GIT_TAG\t\t\t\"677c8553dc64dfd62c272eca94a291a277733113\"\n)\n\nfind_package(CUDAToolkit REQUIRED)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS} .)\nif(WIN32)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n\tCUDA::cudart\n\tCudaDiffRasterizer\n\tGaussianHierarchy\n)\nelse()\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n\tCUDA::cudart\n\tCudaDiffRasterizer\n\tGaussianHierarchy\n)\nendif()\n\nadd_definitions( -DSIBR_EXP_ULR_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/renderer\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS ${SHADERS}\n\tRSC_FOLDER ${SIBR_PROJECT}\n\n    #STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n# include <core/system/CommandLineArgs.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_EXP_ULR_EXPORT\n#      ifdef SIBR_EXP_ULR_EXPORTS\n/* We are building this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllexport)\n#      else\n/* We are using this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_EXP_ULR_EXPORT\n# endif\n\nnamespace sibr {\n\n\t/// Arguments for all ULR applications.\n\tstruct GaussianAppArgs :\n\t\tvirtual BasicIBRAppArgs {\n\t\tArg<int> version = { \"v\", 3, \"ULR implementation version\" };\n\t\tArgSwitch softVisibility = { \"soft-visibility\", false, \"generate and use soft visibility masks\" };\n\t\tArg<bool> masks = { \"masks\" , \"use binary masks\" };\n\t\tArg<std::string> snaplabel = { \"snap-label\" , \"top\" };\n\t\tArg<std::string> maskParams = { \"masks-param\" , \"\" };\n\t\tArg<std::string> maskParamsExtra = { \"masks-param-extra\" , \"\" };\n\t\tArg<bool> invert = { \"invert\", \"invert the masks\" };\n\t\tArg<bool> alphas = { \"alphas\", \"\" };\n\t\tArg<std::string> modelPath = { \"model-path\", \"\" };\n\t\tArg<std::string> scaffoldPath = { \"scaffold\", \"\" };\n\t\tArg<bool> poisson = { \"poisson-blend\", \"apply Poisson-filling to the ULR result\" };\n\t\tArg<int> budget = { \"budget\", 16000, \"Hierarchy memory budget (MB)\" };\n\t\tArg<std::string> imagesPath = { \"images-path\", \"\", \"path to images\" };\n\t};\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/HierarchyView.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use\n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n#include <projects/hierarchyviewer/renderer/HierarchyView.hpp>\n#include <core/graphics/GUI.hpp>\n#include <thread>\n#include <boost/asio.hpp>\n\n#include <runtime_maintenance.h>\n#include <runtime_switching.h>\n#include <hierarchy_loader.h>\n#include <cuda_rasterizer/rasterizer.h>\n\n#include <algorithm>\n\n#include <Eigen/Dense>\n#include <Eigen/Eigenvalues>\n#include <algorithm>\n\nnamespace sibr\n{\n\t/** Copy the content of an input texture to another rendertarget or to the window.\n\tIf you need a basic copy, prefer using blit.\n\t\\sa sibr::blit\n\t\\ingroup sibr_renderer\n\t*/\n\tclass BufferCopyRenderer\n\t{\n\n\tpublic:\n\n\t\t/** Constructor. You can specify custom shaders, refer to noproj.vert and copy.frag for examples.\n\t\t\\param vertFile pah to the vertex shader file\n\t\t\\param fragFile pah to the fragment shader file\n\t\t*/\n\t\tBufferCopyRenderer()\n\t\t{\n\t\t\t_shader.init(\"CopyShader\",\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"hierarchyviewer\") + \"/copy.vert\"),\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"hierarchyviewer\") + \"/copy.frag\"));\n\n\t\t\t_flip.init(_shader, \"flip\");\n\t\t\t_width.init(_shader, \"width\");\n\t\t\t_height.init(_shader, \"height\");\n\t\t}\n\n\t\t/** Copy input texture to the output texture, copy also the input alpha into depth.\n\t\t\\param textureID the texture to copy\n\t\t\\param dst the destination\n\t\t\\param disableTest disable depth testing (depth won't be written)\n\t\t*/\n\t\tvoid\tprocess(uint bufferID, IRenderTarget& dst, int width, int height, bool disableTest = true)\n\t\t{\n\t\t\tif (disableTest)\n\t\t\t\tglDisable(GL_DEPTH_TEST);\n\t\t\telse\n\t\t\t\tglEnable(GL_DEPTH_TEST);\n\n\t\t\t_shader.begin();\n\t\t\t_flip.send();\n\t\t\t_width.send();\n\t\t\t_height.send();\n\n\t\t\tdst.clear();\n\t\t\tdst.bind();\n\n\t\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, bufferID);\n\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t\tdst.unbind();\n\t\t\t_shader.end();\n\t\t}\n\n\t\t/** Copy input texture to a window.\n\t\t\\param textureID the texture to copy\n\t\t\\param dst the destination window\n\t\t*/\n\t\tvoid\tcopyToWindow(uint textureID, Window& dst)\n\t\t{\n\t\t\tglDisable(GL_DEPTH_TEST);\n\n\t\t\t_shader.begin();\n\n\t\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID);\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t\t_shader.end();\n\t\t}\n\n\t\t/** \\return option to flip the texture when copying. */\n\t\tbool& flip() { return _flip.get(); }\n\n\t\tint& width() { return _width.get(); }\n\n\t\tint& height() { return _height.get(); }\n\n\tprivate:\n\n\t\tGLShader\t\t\t_shader; ///< Copy shader.\n\t\tGLuniform<bool>\t\t_flip = false; ///< Flip the texture when copying.\n\t\tGLuniform<int>\t\t_width = 1000;\n\t\tGLuniform<int>\t\t_height = 800;\n\t};\n\n\t/** Copy the content of an input texture to another rendertarget or to the window.\n\tIf you need a basic copy, prefer using blit.\n\t\\sa sibr::blit\n\t\\ingroup sibr_renderer\n\t*/\n\tclass BufferCopyRenderer2\n\t{\n\n\tpublic:\n\n\t\t/** Constructor. You can specify custom shaders, refer to noproj.vert and copy.frag for examples.\n\t\t\\param vertFile pah to the vertex shader file\n\t\t\\param fragFile pah to the fragment shader file\n\t\t*/\n\t\tBufferCopyRenderer2()\n\t\t{\n\t\t\t_shader.init(\"CopyShader\",\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"hierarchyviewer\") + \"/copy2.vert\"),\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"hierarchyviewer\") + \"/copy2.frag\"));\n\n\t\t\t_flip.init(_shader, \"flip\");\n\t\t\t_width.init(_shader, \"width\");\n\t\t\t_height.init(_shader, \"height\");\n\t\t}\n\n\t\t/** Copy input texture to the output texture, copy also the input alpha into depth.\n\t\t\\param textureID the texture to copy\n\t\t\\param dst the destination\n\t\t\\param disableTest disable depth testing (depth won't be written)\n\t\t*/\n\t\tvoid\tprocess(uint bufferID, IRenderTarget& dst, int width, int height, bool disableTest = true)\n\t\t{\n\t\t\tif (disableTest)\n\t\t\t\tglDisable(GL_DEPTH_TEST);\n\t\t\telse\n\t\t\t\tglEnable(GL_DEPTH_TEST);\n\n\t\t\t_shader.begin();\n\t\t\t_flip.send();\n\t\t\t_width.send();\n\t\t\t_height.send();\n\n\t\t\tdst.clear();\n\t\t\tdst.bind();\n\n\t\t\tglBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, bufferID);\n\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t\tdst.unbind();\n\t\t\t_shader.end();\n\t\t}\n\n\t\t/** Copy input texture to a window.\n\t\t\\param textureID the texture to copy\n\t\t\\param dst the destination window\n\t\t*/\n\t\tvoid\tcopyToWindow(uint textureID, Window& dst)\n\t\t{\n\t\t\tglDisable(GL_DEPTH_TEST);\n\n\t\t\t_shader.begin();\n\n\t\t\tglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID);\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n\n\t\t\t_shader.end();\n\t\t}\n\n\t\t/** \\return option to flip the texture when copying. */\n\t\tbool& flip() { return _flip.get(); }\n\n\t\tint& width() { return _width.get(); }\n\n\t\tint& height() { return _height.get(); }\n\n\tprivate:\n\n\t\tGLShader\t\t\t_shader; ///< Copy shader.\n\t\tGLuniform<bool>\t\t_flip = false; ///< Flip the texture when copying.\n\t\tGLuniform<int>\t\t_width = 1000;\n\t\tGLuniform<int>\t\t_height = 800;\n\t};\n}\n\n\ntypedef float SmallSHs[12];\ntypedef float Arr3[3];\ntypedef float Arr4[4];\n\nstruct RichPoint\n{\n\tArr3 pos;\n\tfloat n[3];\n\tSmallSHs shs;\n\tfloat alpha;\n\tArr3 scale;\n\tArr4 rot;\n};\n\nfloat sigmoidy(const float m1)\n{\n\treturn 1.0 / (1.0 + exp(-m1));\n}\n\nint loadScaffold(const char* path,\n\tstd::vector<sibr::Vector3f>& pos,\n\tstd::vector<SHs>& shs,\n\tstd::vector<float>& alphas,\n\tstd::vector<sibr::Vector3f>& scales,\n\tstd::vector<sibr::Vector4f>& rot)\n{\n\tstd::string txtfile = (std::string(path) + \"/pc_info.txt\").c_str();\n\tstd::string plyfile = (std::string(path) + \"/point_cloud.ply\").c_str();\n\n\tstd::ifstream descfile(txtfile.c_str());\n\tstd::string line;\n\n\tif (!descfile.good())\n\t\tthrow std::runtime_error(\"Scaffold description not found! \" + txtfile);\n\n\tstd::getline(descfile, line);\n\tint count = std::atoi(line.c_str());\n\n\tstd::ifstream infile(plyfile.c_str(), std::ios_base::binary);\n\n\tif (!infile.good())\n\t\tthrow std::runtime_error(\"Scaffold not found! \" + plyfile);\n\n\tstd::string buff;\n\tstd::getline(infile, buff);\n\tstd::getline(infile, buff);\n\n\tstd::string dummy;\n\tstd::getline(infile, buff);\n\tstd::stringstream ss(buff);\n\tint noerp;\n\tss >> dummy >> dummy >> noerp;\n\n\twhile (std::getline(infile, buff))\n\t{\n\t\tif (buff.compare(\"end_header\") == 0)\n\t\t\tbreak;\n\t}\n\n\tstd::vector<RichPoint> points(count);\n\n\tinfile.read((char*)points.data(), count * sizeof(RichPoint));\n\n\tpos.resize(count);\n\tshs.resize(count);\n\tscales.resize(count);\n\trot.resize(count);\n\talphas.resize(count);\n\n\tfor (int k = 0; k < count; k++)\n\t{\n\t\tint i = k;\n\t\tpos[k] = { points[i].pos[0], points[i].pos[1], points[i].pos[2] };\n\t\trot[k] = { points[i].rot[0], points[i].rot[1], points[i].rot[2], points[i].rot[3] };\n\t\tscales[k] = {\n\t\t\texpf(points[i].scale[0]),\n\t\t\texpf(points[i].scale[1]),\n\t\t\texpf(points[i].scale[2])\n\t\t};\n\t\talphas[k] = sigmoidy(points[i].alpha);\n\t\tshs[k][0] = points[i].shs[0];\n\t\tshs[k][1] = points[i].shs[1];\n\t\tshs[k][2] = points[i].shs[2];\n\t\tfor (int j = 1; j < 4; j++)\n\t\t{\n\t\t\tshs[k][(j - 1) + 3] = points[i].shs[j * 3 + 0];\n\t\t\tshs[k][(j - 1) + 18] = points[i].shs[j * 3 + 1];\n\t\t\tshs[k][(j - 1) + 33] = points[i].shs[j * 3 + 2];\n\t\t}\n\t\tfor (int j = 4; j < 16; j++)\n\t\t{\n\t\t\tshs[k][(j - 1) + 3] = 0;\n\t\t\tshs[k][(j - 1) + 18] = 0;\n\t\t\tshs[k][(j - 1) + 33] = 0;\n\t\t}\n\t}\n\n\treturn pos.size();\n}\n\nint loadHierarchy(const char* filename,\n\tstd::vector<Eigen::Vector3f>& pos,\n\tstd::vector<SHs>& shs,\n\tstd::vector<float>& alphas,\n\tstd::vector<Eigen::Vector3f>& scales,\n\tstd::vector<Eigen::Vector4f>& rot,\n\tstd::vector<Node>& nodes,\n\tstd::vector<Box>& boxes)\n{\n\tHierarchyLoader loader;\n\tloader.load(filename, pos, shs, alphas, scales, rot, nodes, boxes);\n\n\tint P = pos.size();\n\tfor (int i = 0; i < P; i++)\n\t{\n\t\tfor (int j = 0; j < 3; j++)\n\t\t{\n\t\t\tscales[i][j] = exp(scales[i][j]);\n\t\t}\n\t}\n\n\treturn P;\n}\n\nbool sibr::HierarchyView::addNodePackage(\n\tconst std::vector<int>& node_indices,\n\tconst std::vector<int>& cuda_parent_indices,\n\tMemSet* useMem\n)\n{\n\tint node_copy_count = node_indices.size();\n\tint gaussian_copy_count = 0;\n\tfor (const int& id : node_indices)\n\t{\n\t\tNode node = nodes[id];\n\t\tgaussian_copy_count += node.count_leafs + node.count_merged;\n\t}\n\n\tif (node_copy_count + cuda_nodes_offset > GAUSS_MEMLIMIT ||\n\t\tgaussian_copy_count + cuda_gaussians_offset > GAUSS_MEMLIMIT)\n\t{\n\t\t//std::cout << \"Out of mem!\" << std::endl;\n\t\t//sizeLimit = std::max(sizeLimit, 0.00001f);\n\t\tif (tau == 0)\n\t\t\ttau = 1.0f;\n\t\ttau *= 1.05f;\n\t\treturn false;\n\t}\n\n\tint copied_gaussians = 0;\n\tfor (int i = 0; i < node_indices.size(); i++)\n\t{\n\t\tint id = node_indices[i];\n\t\tint parent = cuda_parent_indices[i];\n\t\tNode node = nodes[id];\n\n\t\tint count = node.count_leafs + node.count_merged;\n\t\tfor (int j = 0; j < count; j++)\n\t\t{\n\t\t\tint src = node.start + j;\n\t\t\tint dst = copied_gaussians + j;\n\t\t\tpos_to_copy[dst] = pos[src];\n\t\t\trot_to_copy[dst] = rot[src];\n\t\t\tshs_to_copy[dst] = shs[src];\n\t\t\talpha_to_copy[dst] = alpha[src];\n\t\t\tscale_to_copy[dst] = scale[src];\n\t\t}\n\n\t\tnode.start_children = -1;\n\t\tnode.start = cuda_gaussians_offset + copied_gaussians;\n\t\tnode.parent = parent;\n\n\t\tnodes_to_copy[i] = node;\n\t\tboxes_to_copy[i] = boxes[id];\n\n\t\tcuda2cpu[cuda_nodes_offset + i] = id;\n\n\t\tcopied_gaussians += count;\n\t}\n\n\tcudaMemcpyAsync(useMem->pos_cuda + cuda_gaussians_offset, pos_to_copy, sizeof(sibr::Vector3f) * gaussian_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\tcudaMemcpyAsync(useMem->rot_cuda + cuda_gaussians_offset, rot_to_copy, sizeof(sibr::Vector4f) * gaussian_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\tcudaMemcpyAsync(useMem->shs_cuda + cuda_gaussians_offset, shs_to_copy, sizeof(SHs) * gaussian_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\tcudaMemcpyAsync(useMem->alpha_cuda + cuda_gaussians_offset, alpha_to_copy, sizeof(float) * gaussian_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\tcudaMemcpyAsync(useMem->scale_cuda + cuda_gaussians_offset, scale_to_copy, sizeof(sibr::Vector3f) * gaussian_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\tcudaMemcpyAsync(useMem->nodes_cuda + cuda_nodes_offset, nodes_to_copy, sizeof(Node) * node_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\tcudaMemcpyAsync(useMem->boxes_cuda + cuda_nodes_offset, boxes_to_copy, sizeof(Box) * node_copy_count, cudaMemcpyHostToDevice, maintenanceStream);\n\n\tcuda_gaussians_offset += gaussian_copy_count;\n\tcuda_nodes_offset += node_copy_count;\n\n\treturn true;\n}\n\nfloat tau2Limit(float tau, float tanfovx, int width)\n{\n\tif (tau == 0)\n\t\treturn 0;\n\n\treturn (2.f * (tau + 0.5f)) * tanfovx / (0.5f * width);\n}\n\nint sibr::HierarchyView::createNodePackage(\n\tint num_to_expand,\n\tconst int* indices_to_expand_cuda,\n\tstd::vector<int>& node_indices,\n\tstd::vector<int>& cuda_parent_indices,\n\tint* cuda_parent_starts)\n{\n\tcudaMemcpyAsync(need_children, nodes_to_expand_cuda, sizeof(int) * num_to_expand, cudaMemcpyDeviceToHost, maintenanceStream);\n\tcudaStreamSynchronize(maintenanceStream);\n\n\tint num_get_children = 0;\n\tint node_package_count = 0;\n\tfor (int i = 0; i < num_to_expand; i++)\n\t{\n\t\tint cuda_id = need_children[i];\n\t\tint node_id = cuda2cpu[cuda_id];\n\t\tnode_package_count += nodes[node_id].count_children;\n\t\t\n\t\tnum_get_children++;\n\t}\n\n\tnode_indices.resize(node_package_count);\n\tcuda_parent_indices.resize(node_package_count);\n\n\tint nodes_expanded = 0;\n\tfor (int k = 0; k < num_get_children; k++)\n\t{\n\t\tint cuda_id = need_children[k];\n\t\tint node_id = cuda2cpu[cuda_id];\n\t\tNode node = nodes[node_id];\n\t\tfor (int i = 0; i < node.count_children; i++)\n\t\t{\n\t\t\tnode_indices[nodes_expanded + i] = node.start_children + i;\n\t\t\tcuda_parent_indices[nodes_expanded + i] = cuda_id;\n\t\t}\n\t\tcuda_parent_starts[k] = cuda_nodes_offset + nodes_expanded;\n\t\tnodes_expanded += node.count_children;\n\t}\n\n\treturn num_get_children;\n}\n\nsize_t totalAlloc = 0;\ntemplate<typename T>\nvoid allocTracked(T** t, size_t Size)\n{\n\ttotalAlloc += Size;\n\tcudaMalloc(t, Size);\n}\n\nstd::function<char* (size_t N)> resizeFunctional(void** ptr, size_t& S) {\n\tauto lambda = [ptr, &S](size_t N) {\n\t\tif (N > S)\n\t\t{\n\t\t\tif (*ptr)\n\t\t\t\tcudaFree(*ptr);\n\n\t\t\tS = 1.15f * N;\n\t\t\tcudaMalloc(ptr, S);\n\t\t}\n\t\treturn reinterpret_cast<char*>(*ptr);\n\t};\n\treturn lambda;\n}\n\n#define CUDA_SAFE(A)\\\nA;\\\nif(cudaDeviceSynchronize())\\\nSIBR_ERR << \"ERROR: \" << cudaDeviceSynchronize();\n\nint64_t basecost(int skyboxnum)\n{\n\treturn (3 + 48 + 1 + 3 + 4) * 4 * 2 * skyboxnum +\n\t\t(2 + 1) * 4 * skyboxnum +\n\t\t(16 + 16 + 3 + 3) * 4 +\n\t\t3 * 4 +\n\t\t4;\n}\n\nint64_t per_gauss_cost()\n{\n\treturn (3 + 48 + 1 + 3 + 4) * 4 * 2 +\n\t\t(7 + 8) * 4 * 2 +\n\t\t(1 + 1 + 1) * 4 * 2 +\n\t\t(1 + 1 + 1 + 1) * 4 +\n\t\t(1 + 1 + 1 + 1 + 1) * 4 +\n\t\t(2 + 1) * 4 +\n\t\t((1 + 1 + 1 + 1) * 4 + 1);\n}\n\nsibr::HierarchyView::HierarchyView(const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h, const char* file, const char* scaffoldfile, int64_t budget) :\n\t_scene(ibrScene),\n\tsibr::ViewBase(render_w, render_h)\n{\n\t_pointbasedrenderer.reset(new PointBasedRenderer());\n\t_copyRenderer = new BufferCopyRenderer();\n\t_copyRenderer->flip() = true;\n\t_copyRenderer->width() = render_w;\n\t_copyRenderer->height() = render_h;\n\n\n\t// Tell the scene we are a priori using all active cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto& cams = ibrScene->cameras()->inputCameras();\n\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif (cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n\n\tstd::vector<Eigen::Vector3f> eigenpos;\n\tstd::vector<Eigen::Vector3f> eigenscale;\n\tstd::vector<Eigen::Vector4f> eigenrot;\n\n\tloadHierarchy(file,\n\t\teigenpos,\n\t\tshs,\n\t\talpha,\n\t\teigenscale,\n\t\teigenrot,\n\t\tnodes,\n\t\tboxes);\n\n\tpos.resize(eigenpos.size());\n\trot.resize(eigenpos.size());\n\tscale.resize(eigenpos.size());\n\tfor (int i = 0; i < eigenpos.size(); i++)\n\t{\n\t\tpos[i] = eigenpos[i];\n\t\trot[i] = eigenrot[i];\n\t\tscale[i] = eigenscale[i];\n\t}\n\n\tstd::vector<sibr::Vector3f> skyboxpos;\n\tstd::vector<sibr::Vector4f> skyboxrot;\n\tstd::vector<SHs> skyboxsh;\n\tstd::vector<float> skyboxalpha;\n\tstd::vector<sibr::Vector3f> skyboxscale;\n\n\tif (strlen(scaffoldfile))\n\t{\n\t\tskyboxnum = loadScaffold(scaffoldfile,\n\t\t\tskyboxpos,\n\t\t\tskyboxsh,\n\t\t\tskyboxalpha,\n\t\t\tskyboxscale,\n\t\t\tskyboxrot);\n\t}\n\n\tGAUSS_MEMLIMIT = (budget*1000000 - basecost(skyboxnum)) / per_gauss_cost();\n\tif (GAUSS_MEMLIMIT < 0)\n\t{\n\t\tthrow std::runtime_error(\"Memory budget insufficient\");\n\t}\n\n\tGAUSS_MEMLIMIT = std::min(GAUSS_MEMLIMIT, std::max((int)pos.size(), (int)nodes.size()));\n\n\tSIBR_LOG << \"Allowing up to \" << GAUSS_MEMLIMIT << \" Gaussians in VRAM\" << std::endl;\n\n\tsplits = std::vector<int>(GAUSS_MEMLIMIT, 0);\n\n\tint ALLGAUSS = (GAUSS_MEMLIMIT + skyboxnum);\n\n\tfor (int i = 0; i < 2; i++)\n\t{\n\t\tsibr::Vector3f *allPos, *allScales;\n\t\tSHs* allSHs;\n\t\tfloat* allAlpha;\n\t\tsibr::Vector4f* allRot;\n\t\tCUDA_SAFE(allocTracked((void**)&allPos, sizeof(sibr::Vector3f) * ALLGAUSS));\n\t\tcudaMemcpy(allPos, skyboxpos.data(), sizeof(sibr::Vector3f) * skyboxnum, cudaMemcpyHostToDevice);\n\t\tCUDA_SAFE(allocTracked((void**)&allSHs, sizeof(SHs) * ALLGAUSS));\n\t\tcudaMemcpy(allSHs, skyboxsh.data(), sizeof(SHs) * skyboxnum, cudaMemcpyHostToDevice);\n\t\tCUDA_SAFE(allocTracked((void**)&allAlpha, sizeof(float) * ALLGAUSS));\n\t\tcudaMemcpy(allAlpha, skyboxalpha.data(), sizeof(float) * skyboxnum, cudaMemcpyHostToDevice);\n\t\tCUDA_SAFE(allocTracked((void**)&allScales, sizeof(sibr::Vector3f) * ALLGAUSS));\n\t\tcudaMemcpy(allScales, skyboxscale.data(), sizeof(sibr::Vector3f) * skyboxnum, cudaMemcpyHostToDevice);\n\t\tCUDA_SAFE(allocTracked((void**)&allRot, sizeof(sibr::Vector4f) * ALLGAUSS));\n\t\tcudaMemcpy(allRot, skyboxrot.data(), sizeof(sibr::Vector4f) * skyboxnum, cudaMemcpyHostToDevice);\n\t\tmems[i].pos_cuda = allPos + skyboxnum;\n\t\tmems[i].shs_cuda = allSHs + skyboxnum;\n\t\tmems[i].alpha_cuda = allAlpha + skyboxnum;\n\t\tmems[i].scale_cuda = allScales + skyboxnum;\n\t\tmems[i].rot_cuda = allRot + skyboxnum;\n\n\t\tCUDA_SAFE(allocTracked((void**)&mems[i].nodes_cuda, sizeof(Node) * GAUSS_MEMLIMIT));\n\t\tCUDA_SAFE(allocTracked((void**)&mems[i].boxes_cuda, sizeof(Box) * GAUSS_MEMLIMIT));\n\t}\n\n\tcudaHostAlloc((void**)&nodes_to_copy, sizeof(Node) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&boxes_to_copy, sizeof(Box) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&pos_to_copy, sizeof(sibr::Vector3f) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&rot_to_copy, sizeof(sibr::Vector4f) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&shs_to_copy, sizeof(SHs) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&alpha_to_copy, sizeof(float) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&scale_to_copy, sizeof(sibr::Vector3f) * GAUSS_MEMLIMIT, 0);\n\n\tcudaHostAlloc((void**)&cam_pos, sizeof(Point), 0);\n\tcudaHostAlloc((void**)&cam_pos_old, sizeof(Point), 0);\n\n\tcudaHostAlloc((void**)&new_node_count, sizeof(int), 0);\n\tcudaHostAlloc((void**)&new_gauss_count, sizeof(int), 0);\n\n\tcudaHostAlloc((void**)&newN, sizeof(int), 0);\n\tcudaHostAlloc((void**)&newG, sizeof(int), 0);\n\tcudaHostAlloc((void**)&newE, sizeof(int), 0);\n\tcudaHostAlloc((void**)&renderhelper, sizeof(int), 0);\n\tcudaHostAlloc((void**)&view_mat_ptr, sizeof(sibr::Matrix4f), 0);\n\tcudaHostAlloc((void**)&proj_mat_ptr, sizeof(sibr::Matrix4f), 0);\n\n\tcudaHostAlloc((void**)&cuda2cpu, sizeof(int) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&package_parent_cuda_starts, sizeof(int) * GAUSS_MEMLIMIT, 0);\n\tcudaHostAlloc((void**)&need_children, sizeof(int) * GAUSS_MEMLIMIT, 0);\n\n\tfor (int i = 0; i < 2; i++)\n\t{\n\t\tcudaHostAlloc((void**)&lights[i].to_render, sizeof(int), 0);\n\t\tCUDA_SAFE(allocTracked((void**)&lights[i].render_indices, sizeof(int) * GAUSS_MEMLIMIT));\n\t\tCUDA_SAFE(allocTracked((void**)&lights[i].parent_indices, sizeof(int) * GAUSS_MEMLIMIT));\n\t\tCUDA_SAFE(allocTracked((void**)&lights[i].nodes_of_render_indices, sizeof(int) * GAUSS_MEMLIMIT));\n\t}\n\n\tcudaHostAlloc((void**)&num_active_nodes_gpu, sizeof(int), 0);\n\tcudaHostAlloc((void**)&num_need_children, sizeof(int), 0);\n\n\tCUDA_SAFE(allocTracked((void**)&splits1_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(cudaMemcpy(splits1_cuda, splits.data(), sizeof(int), cudaMemcpyHostToDevice));\n\tCUDA_SAFE(allocTracked((void**)&splits2_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(cudaMemcpy(splits2_cuda, splits.data(), sizeof(int), cudaMemcpyHostToDevice));\n\tCUDA_SAFE(allocTracked((void**)&activenodes1_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(allocTracked((void**)&activenodes2_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\n\tcurrSet = &lights[0];\n\totherSet = &lights[1];\n\n\tcurrMem = &mems[0];\n\totherMem = &mems[1];\n\n\tactivenodes1.resize(GAUSS_MEMLIMIT);\n\tactivenodes2.resize(GAUSS_MEMLIMIT);\n\trender_indices.resize(GAUSS_MEMLIMIT);\n\n\tCUDA_SAFE(allocTracked(&cuda2cpu1_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(allocTracked(&cuda2cpu2_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(allocTracked((void**)&nodes_to_expand_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(allocTracked((void**)&ts_cuda, sizeof(float) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(allocTracked((void**)&kids_cuda, sizeof(int) * GAUSS_MEMLIMIT));\n\tCUDA_SAFE(allocTracked((void**)&rect_cuda, 2 * sizeof(int) * ALLGAUSS));\n\tCUDA_SAFE(allocTracked((void**)&radii_cuda, sizeof(int) * ALLGAUSS));\n\n\tactivenodes1[0] = 0;\n\tnum_active_nodes_cpu = 1;\n\t*num_active_nodes_gpu = 1;\n\n\tCUDA_SAFE(cudaMemcpy(activenodes1_cuda, activenodes1.data(), sizeof(int), cudaMemcpyHostToDevice));\n\n\tcudaStreamCreate(&renderStream);\n\tcudaStreamCreate(&maintenanceStream);\n\n\taddNodePackage({ 0 }, { -1 }, currMem);\n\n\tfor (int i = 0; i < 100; i++)\n\t\tusage_vals[i] = 0;\n\n\tCUDA_SAFE(allocTracked((void**)&view_cuda, sizeof(sibr::Matrix4f)));\n\tCUDA_SAFE(allocTracked((void**)&proj_cuda, sizeof(sibr::Matrix4f)));\n\tCUDA_SAFE(allocTracked((void**)&cam_pos_cuda, 3 * sizeof(float)));\n\tCUDA_SAFE(allocTracked((void**)&cam_pos_cuda_old, 3 * sizeof(float)));\n\n\tCUDA_SAFE(glCreateBuffers(1, &imageBuffer));\n\tCUDA_SAFE(glNamedBufferStorage(imageBuffer, render_w * render_h * 3 * sizeof(float), nullptr, GL_DYNAMIC_STORAGE_BIT));\n\tCUDA_SAFE(cudaGraphicsGLRegisterBuffer(&imageBufferCuda, imageBuffer, cudaGraphicsRegisterFlagsWriteDiscard));\n\n\tgeomBufferFunc = resizeFunctional(&geomPtr, allocdGeom);\n\tbinningBufferFunc = resizeFunctional(&binningPtr, allocdBinning);\n\timgBufferFunc = resizeFunctional(&imgPtr, allocdImg);\n\n\tallocTracked((void**)&background_cuda, 3 * sizeof(float));\n\tfloat bg[3] = { 0.0f, 0.0f, 0.0f };\n\tcudaMemcpy(background_cuda, bg, 3 * sizeof(float), cudaMemcpyHostToDevice);\n\n\tallocTracked(&NsrcI, sizeof(int) * GAUSS_MEMLIMIT);\n\tallocTracked(&NdstI, sizeof(int) * GAUSS_MEMLIMIT);\n\tallocTracked(&NsrcI2, sizeof(int)* GAUSS_MEMLIMIT);\n\tallocTracked(&NdstI2, sizeof(int)* GAUSS_MEMLIMIT);\n\tallocTracked(&NsrcC, sizeof(char)* GAUSS_MEMLIMIT);\n\tallocTracked(&numI, sizeof(int));\n\n\tstd::cout << \"Using \" << totalAlloc << \" bytes for scene\" << std::endl;\n}\n\nvoid sibr::HierarchyView::setScene(const sibr::BasicIBRScene::Ptr& newScene) {\n\t_scene = newScene;\n\n\t// Tell the scene we are a priori using all active cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto& cams = newScene->cameras()->inputCameras();\n\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif (cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n}\n\nstd::tuple<sibr::HierarchyView::MemSet*, int, int> sibr::HierarchyView::asyncTask(Point* campos, Point zdir, bool cleanup)\n{\n\tcudaMemcpyAsync(cam_pos_cuda_old, campos, sizeof(Point), cudaMemcpyHostToDevice, maintenanceStream);\n\n\tMemSet* useMem = currMem;\n\n\tint add_success;\n\n\tSwitching::changeToSizeStep(\n\t\tsizeLimit,\n\t\t*num_active_nodes_gpu,\n\t\tactivenodes1_cuda,\n\t\tactivenodes2_cuda,\n\t\t(int*)useMem->nodes_cuda,\n\t\t(float*)useMem->boxes_cuda,\n\t\tcam_pos_cuda_old,\n\t\tzdir.xyz[0], zdir.xyz[1], zdir.xyz[2],\n\t\tsplits1_cuda,\n\t\totherSet->render_indices,\n\t\totherSet->parent_indices,\n\t\totherSet->nodes_of_render_indices,\n\t\tnodes_to_expand_cuda,\n\t\tnullptr,\n\t\tscratchspace,\n\t\tscratchspacesize,\n\t\tNsrcI,\n\t\tNdstI,\n\t\tNsrcC,\n\t\tnumI,\n\t\tGAUSS_MEMLIMIT,\n\t\tadd_success,\n\t\tnum_active_nodes_gpu,\n\t\totherSet->to_render,\n\t\tnum_need_children,\n\t\tmaintenanceStream\n\t);\n\n\tcudaStreamSynchronize(maintenanceStream);\n\n\tbool success = add_success == 1;\n\n\tif (!success)\n\t\tthrow std::runtime_error(\"Doing a step didn't work\");\n\n\tstd::swap(activenodes1_cuda, activenodes2_cuda);\n\n\tint num_get_children = 0;\n\tint num_transferred = 0;\n\tif (!cleanup && *num_need_children > 0)\n\t{\n\t\tif (!ran_out)\n\t\t{\n\t\t\tstd::vector<int> package_indices;\n\t\t\tstd::vector<int> package_parent_cuda_indices;\n\n\t\t\tint num_new_parents = createNodePackage(\n\t\t\t\t*num_need_children,\n\t\t\t\tnodes_to_expand_cuda,\n\t\t\t\tpackage_indices,\n\t\t\t\tpackage_parent_cuda_indices,\n\t\t\t\tpackage_parent_cuda_starts);\n\n\t\t\tif (addNodePackage(package_indices, package_parent_cuda_indices, useMem))\n\t\t\t{\n\t\t\t\tcudaMemcpyAsync(NsrcI, package_parent_cuda_starts, sizeof(int) * num_new_parents, cudaMemcpyHostToDevice, maintenanceStream);\n\t\t\t\tcudaStreamSynchronize(maintenanceStream);\n\t\t\t\tnum_transferred = package_indices.size();\n\t\t\t\tnum_get_children = num_new_parents;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tran_out = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(cleanup || ran_out)\n\t{\n\t\tMaintenance::compactPart1(\n\t\t\tcuda_nodes_offset,\n\t\t\t*num_active_nodes_gpu,\n\t\t\tactivenodes1_cuda,\n\t\t\tactivenodes2_cuda,\n\t\t\t(int*)currMem->nodes_cuda,\n\t\t\t(float*)currMem->boxes_cuda,\n\t\t\t(float*)currMem->pos_cuda,\n\t\t\t(float*)currMem->rot_cuda,\n\t\t\t(float*)currMem->shs_cuda,\n\t\t\tcurrMem->alpha_cuda,\n\t\t\t(float*)currMem->scale_cuda,\n\t\t\tsplits1_cuda,\n\t\t\t(int*)otherMem->nodes_cuda,\n\t\t\t(float*)otherMem->boxes_cuda,\n\t\t\t(float*)otherMem->pos_cuda,\n\t\t\t(float*)otherMem->rot_cuda,\n\t\t\t(float*)otherMem->shs_cuda,\n\t\t\totherMem->alpha_cuda,\n\t\t\t(float*)otherMem->scale_cuda,\n\t\t\tsplits2_cuda,\n\t\t\tcuda2cpu1_cuda,\n\t\t\tcuda2cpu2_cuda,\n\t\t\tNsrcI,\n\t\t\tNsrcI2,\n\t\t\tNdstI,\n\t\t\tNdstI2,\n\t\t\tscratchspace,\n\t\t\tscratchspacesize,\n\t\t\tmaintenanceStream,\n\t\t\tnew_node_count\n\t\t);\n\n\t\tif (cuda_nodes_offset - *new_node_count > 10000 || ran_out) // not gonna bother otherwise\n\t\t{\n\t\t\tcudaMemcpyAsync(cuda2cpu1_cuda, cuda2cpu, sizeof(int) * cuda_nodes_offset, cudaMemcpyHostToDevice, maintenanceStream);\n\n\t\t\tMaintenance::compactPart2(\n\t\t\t\tcuda_nodes_offset,\n\t\t\t\t*num_active_nodes_gpu,\n\t\t\t\tactivenodes1_cuda,\n\t\t\t\tactivenodes2_cuda,\n\t\t\t\t(int*)currMem->nodes_cuda,\n\t\t\t\t(float*)currMem->boxes_cuda,\n\t\t\t\t(float*)currMem->pos_cuda,\n\t\t\t\t(float*)currMem->rot_cuda,\n\t\t\t\t(float*)currMem->shs_cuda,\n\t\t\t\tcurrMem->alpha_cuda,\n\t\t\t\t(float*)currMem->scale_cuda,\n\t\t\t\tsplits1_cuda,\n\t\t\t\t(int*)otherMem->nodes_cuda,\n\t\t\t\t(float*)otherMem->boxes_cuda,\n\t\t\t\t(float*)otherMem->pos_cuda,\n\t\t\t\t(float*)otherMem->rot_cuda,\n\t\t\t\t(float*)otherMem->shs_cuda,\n\t\t\t\totherMem->alpha_cuda,\n\t\t\t\t(float*)otherMem->scale_cuda,\n\t\t\t\tsplits2_cuda,\n\t\t\t\tcuda2cpu1_cuda,\n\t\t\t\tcuda2cpu2_cuda,\n\t\t\t\tNsrcI,\n\t\t\t\tNsrcI2,\n\t\t\t\tNdstI,\n\t\t\t\tNdstI2,\n\t\t\t\tscratchspace,\n\t\t\t\tscratchspacesize,\n\t\t\t\tmaintenanceStream,\n\t\t\t\tnew_gauss_count\n\t\t\t);\n\t\t\tcudaStreamSynchronize(maintenanceStream);\n\t\t\tcudaMemcpyAsync(cuda2cpu, cuda2cpu2_cuda, sizeof(int)* *num_active_nodes_gpu, cudaMemcpyDeviceToHost, maintenanceStream);\n\t\t\tcudaStreamSynchronize(maintenanceStream);\n\n\t\t\tstd::swap(cuda2cpu1_cuda, cuda2cpu2_cuda);\n\t\t\tstd::swap(activenodes1_cuda, activenodes2_cuda);\n\t\t\tstd::swap(splits1_cuda, splits2_cuda);\n\t\t\tuseMem = otherMem;\n\n\t\t\tcuda_nodes_offset = *new_node_count;\n\t\t\tcuda_gaussians_offset = *new_gauss_count;\n\n\t\t\tran_out = false;\n\n\t\t\tSwitching::changeToSizeStep(\n\t\t\t\tsizeLimit,\n\t\t\t\t*num_active_nodes_gpu,\n\t\t\t\tactivenodes1_cuda,\n\t\t\t\tactivenodes2_cuda,\n\t\t\t\t(int*)useMem->nodes_cuda,\n\t\t\t\t(float*)useMem->boxes_cuda,\n\t\t\t\tcam_pos_cuda_old,\n\t\t\t\tzdir.xyz[0], zdir.xyz[1], zdir.xyz[2],\n\t\t\t\tsplits1_cuda,\n\t\t\t\totherSet->render_indices,\n\t\t\t\totherSet->parent_indices,\n\t\t\t\totherSet->nodes_of_render_indices,\n\t\t\t\tnodes_to_expand_cuda,\n\t\t\t\tnullptr,\n\t\t\t\tscratchspace,\n\t\t\t\tscratchspacesize,\n\t\t\t\tNsrcI,\n\t\t\t\tNdstI,\n\t\t\t\tNsrcC,\n\t\t\t\tnumI,\n\t\t\t\tGAUSS_MEMLIMIT,\n\t\t\t\tadd_success,\n\t\t\t\tnum_active_nodes_gpu,\n\t\t\t\totherSet->to_render,\n\t\t\t\tnum_need_children,\n\t\t\t\tmaintenanceStream\n\t\t\t);\n\n\t\t\tcudaStreamSynchronize(maintenanceStream);\n\n\t\t\tbool success = add_success == 1;\n\n\t\t\tif (!success)\n\t\t\t\tthrow std::runtime_error(\"Doing a step didn't work\");\n\n\t\t\tstd::swap(activenodes1_cuda, activenodes2_cuda);\n\t\t}\n\t}\n\n\treturn std::make_tuple(useMem, num_get_children, num_transferred);\n}\n\nvoid sibr::HierarchyView::onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye)\n{\n\tauto view_mat = eye.view();\n\tauto proj_mat = eye.viewproj();\n\tview_mat.row(1) *= -1;\n\tview_mat.row(2) *= -1;\n\tproj_mat.row(1) *= -1;\n\n\tauto t = view_mat.row(2).transpose();\n\tPoint zdir = { t.x(), t.y(), t.z() };\n\n\tauto inv = view_mat.inverse();\n\t*cam_pos = { inv(0, 3), inv(1, 3), inv(2, 3) };\n\n\tfloat* image_cuda;\n\tsize_t bytes;\n\tcudaGraphicsMapResources(1, &imageBufferCuda, renderStream);\n\tcudaGraphicsResourceGetMappedPointer((void**)&image_cuda, &bytes, imageBufferCuda);\n\n\tframe++;\n\n\tbuffered |= frame % cleanupFrequency == 0;\n\n\tif (frame == 1 \n\t|| (frame % 2 == 0 && updateResult.wait_for(std::chrono::seconds(0)) == std::future_status::ready))\n\t{\n\t\tif (frame == 1)\n\t\t{\n\t\t\tupdateResult = std::async(\n\t\t\t\tstd::launch::async, &sibr::HierarchyView::asyncTask,\n\t\t\t\tthis,\n\t\t\t\tcam_pos,\n\t\t\t\tzdir,\n\t\t\t\tfalse);\n\t\t}\n\n\t\tauto res = updateResult.get();\n\n\t\tcudaStreamSynchronize(renderStream);\n\n\t\tstd::swap(currSet, otherSet);\n\t\tif (std::get<0>(res) == otherMem)\n\t\t\tstd::swap(otherMem, currMem);\n\n\t\tint num_get_children = std::get<1>(res);\n\t\tif (num_get_children)\n\t\t{\n\t\t\tMaintenance::updateStarts(\n\t\t\t\t(int*)currMem->nodes_cuda,\n\t\t\t\tnum_get_children,\n\t\t\t\tnodes_to_expand_cuda,\n\t\t\t\tNsrcI,\n\t\t\t\trenderStream);\n\t\t\tcudaStreamSynchronize(renderStream);\n\t\t}\n\n\t\tupdateResult = std::async(\n\t\t\tstd::launch::async, &sibr::HierarchyView::asyncTask,\n\t\t\tthis,\n\t\t\tcam_pos,\n\t\t\tzdir,\n\t\t\tbuffered);\n\t\tbuffered = false;\n\t}\n\n\tfloat fovy = eye.fovy();\n\tfloat fovx = 2.0f * atan(tan(eye.fovy() * 0.5f) * eye.aspect());\n\tfloat tan_fovx = tan(fovx * 0.5f);\n\tfloat tan_fovy = tan(fovy * 0.5f);\n\tsizeLimit = tau2Limit(tau, tan_fovx, _resolution.x());\n\n\tif (showSfm)\n\t{\n\t\t_pointbasedrenderer->process(_scene->proxies()->proxy(), eye, dst);\n\t}\n\telse\n\t{\n\t\t*view_mat_ptr = view_mat;\n\t\t*proj_mat_ptr = proj_mat;\n\n\t\tint* parent_ptr = nullptr;\n\t\tfloat* ts_ptr = nullptr;\n\t\tint* kids_ptr = nullptr;\n\t\tif (!disable_interp)\n\t\t{\n\t\t\tparent_ptr = currSet->parent_indices;\n\t\t\tts_ptr = ts_cuda;\n\t\t\tkids_ptr = kids_cuda;\n\t\t}\n\n\t\tSwitching::getTsIndexed(\n\t\t\t*currSet->to_render,\n\t\t\tcurrSet->nodes_of_render_indices,\n\t\t\tsizeLimit,\n\t\t\t(int*)currMem->nodes_cuda,\n\t\t\t(float*)currMem->boxes_cuda,\n\t\t\tcam_pos->xyz[0], cam_pos->xyz[1], cam_pos->xyz[2],\n\t\t\tzdir.xyz[0], zdir.xyz[1], zdir.xyz[2],\n\t\t\tts_cuda,\n\t\t\tkids_cuda,\n\t\t\trenderStream\n\t\t);\n\n\t\tCudaRasterizer::Rasterizer::forward(\n\t\t\tgeomBufferFunc,\n\t\t\tbinningBufferFunc,\n\t\t\timgBufferFunc,\n\t\t\t*currSet->to_render + skyboxnum,\n\t\t\t3,\n\t\t\t16,\n\t\t\tbackground_cuda,\n\t\t\t_resolution.x(), _resolution.y(),\n\t\t\tcurrSet->render_indices,\n\t\t\tparent_ptr,\n\t\t\tts_ptr,\n\t\t\tkids_ptr,\n\t\t\t(float*)currMem->pos_cuda,\n\t\t\t(float*)currMem->shs_cuda,\n\t\t\tnullptr,\n\t\t\t(float*)currMem->alpha_cuda,\n\t\t\t(float*)currMem->scale_cuda,\n\t\t\t_scalingModifier,\n\t\t\t(float*)currMem->rot_cuda,\n\t\t\tnullptr,\n\t\t\t(float*)view_mat_ptr,\n\t\t\t(float*)proj_mat_ptr,\n\t\t\t(float*)cam_pos,\n\t\t\ttan_fovx,\n\t\t\ttan_fovy,\n\t\t\tfalse,\n\t\t\timage_cuda,\n\t\t\tnullptr,\n\t\t\tradii_cuda,\n\t\t\trect_cuda,\n\t\t\tnullptr,\n\t\t\tnullptr,\n\t\t\tfalse,\n\t\t\tskyboxnum,\n\t\t\trenderStream,\n\t\t\trenderhelper,\n\t\t\tbiglimit,\n\t\t\ttrue\n\t\t);\n\t}\n\n\tcudaGraphicsUnmapResources(1, &imageBufferCuda, renderStream);\n\tif (!showSfm)\n\t{\n\t\t_copyRenderer->process(imageBuffer, dst, _resolution.x(), _resolution.y());\n\t}\n}\n\nvoid sibr::HierarchyView::onUpdate(Input& input)\n{\n}\n\nvoid sibr::HierarchyView::onGUI()\n{\n\tconst std::string guiName = \"3D Gaussians\";\n\tif (ImGui::Begin(guiName.c_str())) {\n\n\t\tImGui::Checkbox(\"Show SfM\", &showSfm);\n\t\tImGui::SliderFloat(\"Scaling Modifier\", &_scalingModifier, 0.001f, 1.0f);\n\t\tImGui::Checkbox(\"Use ZFar\", &_useZFar);\n\t\tif (_useZFar)\n\t\t{\n\t\t\tImGui::InputFloat(\"Zfar\", &_zfar);\n\t\t}\n\t\tImGui::InputFloat(\"Tau (size limit)\", &tau);\n\n\t\tImGui::InputInt(\"Cleanup Rate\", &cleanupFrequency);\n\t\tcleanupFrequency = std::max(10, cleanupFrequency);\n\n\t\tImGui::Checkbox(\"Show Level\", &show_level);\n\t\tImGui::Checkbox(\"Disable Interp\", &disable_interp);\n\n\t\tfor (int i = 0; i < 99; i++)\n\t\t\tusage_vals[i] = usage_vals[i + 1];\n\t\tusage_vals[99] = cuda_gaussians_offset;\n\t\tImGui::PlotLines(\"Active Gauss\", usage_vals, 100, 0, \"\", 0, GAUSS_MEMLIMIT, ImVec2(0, 80.f));\n\n\t\tImGui::InputFloat(\"Biglimit\", &biglimit);\n\t}\n\tImGui::End();\n}\n\nsibr::HierarchyView::~HierarchyView()\n{\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/HierarchyView.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use\n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n\n# include \"Config.hpp\"\n# include <core/renderer/RenderMaskHolder.hpp>\n# include <core/scene/BasicIBRScene.hpp>\n# include <core/system/SimpleTimer.hpp>\n# include <core/system/Config.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/view/ViewBase.hpp>\n# include <core/renderer/CopyRenderer.hpp>\n# include <core/renderer/PointBasedRenderer.hpp>\n# include <memory>\n# include <core/graphics/Texture.hpp>\n#include <cuda_runtime.h>\n#include <cuda_gl_interop.h>\n#include \"common.h\"\n#include <types.h>\n#include <chrono>\n#include <future>\n\ntypedef Eigen::Matrix<float, 48, 1> SHs;\n\nnamespace sibr {\n\n\tclass BufferCopyRenderer;\n\n\t/**\n\t * \\class RemotePointView\n\t * \\brief Wrap a ULR renderer with additional parameters and information.\n\t */\n\tclass SIBR_EXP_ULR_EXPORT HierarchyView : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(HierarchyView);\n\n\tpublic:\n\n\t\t/**\n\t\t * Constructor\n\t\t * \\param ibrScene The scene to use for rendering.\n\t\t * \\param render_w rendering width\n\t\t * \\param render_h rendering height\n\t\t */\n\t\tHierarchyView(const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h, const char* file, const char* scaffoldfile, int64_t budget);\n\n\t\t/** Replace the current scene.\n\t\t *\\param newScene the new scene to render */\n\t\tvoid setScene(const sibr::BasicIBRScene::Ptr& newScene);\n\n\t\t/**\n\t\t * Perform rendering. Called by the view manager or rendering mode.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param eye The novel viewpoint.\n\t\t */\n\t\tvoid onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye) override;\n\n\t\t/**\n\t\t * Update inputs (do nothing).\n\t\t * \\param input The inputs state.\n\t\t */\n\t\tvoid onUpdate(Input& input) override;\n\n\t\t/**\n\t\t * Update the GUI.\n\t\t */\n\t\tvoid onGUI() override;\n\n\t\t/** \\return a reference to the scene */\n\t\tconst std::shared_ptr<sibr::BasicIBRScene>& getScene() const { return _scene; }\n\n\t\tvirtual ~HierarchyView() override;\n\n\tprotected:\n\n\t\tfloat biglimit = 30.0f;\n\t\tint cleanupFrequency = 100;\n\n\t\tfloat tau = 9.0f;\n\t\tfloat sizeLimit = 0.03f;\n\n\t\tint GAUSS_MEMLIMIT = 16000000;\n\n\t\tbool buffered = false;\n\n\t\tstruct MemSet\n\t\t{\n\t\t\tNode* nodes_cuda;\n\t\t\tBox* boxes_cuda;\n\t\t\tsibr::Vector3f* pos_cuda;\n\t\t\tsibr::Vector4f* rot_cuda;\n\t\t\tsibr::Vector3f* scale_cuda;\n\t\t\tfloat* alpha_cuda;\n\t\t\tSHs* shs_cuda;\n\t\t};\n\n\t\tstruct LightSet\n\t\t{\n\t\t\tint *to_render;\n\t\t\tint* active_nodes;\n\t\t\tint* nodes_of_render_indices;\n\t\t\tint* parent_indices;\n\t\t\tint* render_indices;\n\t\t};\n\n\t\tstd::future< std::tuple<sibr::HierarchyView::MemSet*, int, int>> updateResult;\n\n\t\tMemSet mems[2];\n\t\tMemSet* currMem = nullptr;\n\t\tMemSet* otherMem = nullptr;\n\n\t\tLightSet lights[2];\n\t\tLightSet* currSet = nullptr;\n\t\tLightSet* otherSet = nullptr;\n\n\t\tbool ran_out = false;\n\n\t\tbool addNodePackage(const std::vector<int>& node_indices,\n\t\t\tconst std::vector<int>& parent_indices,\n\t\t\tMemSet* useMem);\n\n\t\tint sibr::HierarchyView::createNodePackage(\n\t\t\tint num_to_expand,\n\t\t\tconst int* indices_to_expand_cuda,\n\t\t\tstd::vector<int>& node_indices,\n\t\t\tstd::vector<int>& cuda_parent_indices,\n\t\t\tint* cuda_parent_starts\n\t\t);\n\n\t\tstd::vector<sibr::Vector3f> pos;\n\t\tstd::vector<sibr::Vector4f> rot;\n\t\tstd::vector<SHs> shs;\n\t\tstd::vector<float> alpha;\n\t\tstd::vector<sibr::Vector3f> scale;\n\n\t\tPoint* cam_pos;\n\t\tPoint* cam_pos_old;\n\n\t\tint* package_parent_cuda_starts;\n\t\tint* need_children;\n\n\t\tint* new_node_count;\n\t\tint* new_gauss_count;\n\n\t\tint* newN, * newG, *newE;\n\n\t\tint* cuda2cpu;\n\t\tint* cuda2cpu1_cuda;\n\t\tint* cuda2cpu2_cuda;\n\n\t\tfloat usage_vals[100];\n\t\tint frame = 0;\n\n\t\tint* radii_cuda;\n\n\t\tGLuint imageBuffer;\n\t\tcudaGraphicsResource_t imageBufferCuda;\n\n\t\tbool showSfm = false;\n\n\t\tfloat* view_cuda;\n\t\tfloat* proj_cuda;\n\t\tfloat* cam_pos_cuda;\n\t\tfloat* cam_pos_cuda_old;\n\t\tfloat* background_cuda;\n\t\tfloat* override_colors;\n\n\t\tfloat _scalingModifier = 1.0f;\n\t\tstd::function<char* (size_t N)> geomBufferFunc, binningBufferFunc, imgBufferFunc;\n\t\tsize_t allocdGeom = 0, allocdBinning = 0, allocdImg = 0;\n\t\tvoid* geomPtr = nullptr, * binningPtr = nullptr, * imgPtr = nullptr;\n\n\t\tbool _useZFar = false;\n\t\tfloat _zfar = -1;\n\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene; ///< The current scene.\n\t\tPointBasedRenderer::Ptr _pointbasedrenderer;\n\t\tBufferCopyRenderer* _copyRenderer;\n\n\t\tstd::vector<int> activenodes1;\n\t\tstd::vector<int> activenodes2;\n\t\tstd::vector<int> render_indices;\n\t\tstd::vector<int> splits;\n\t\tstd::vector<Node> nodes;\n\t\tstd::vector<Box> boxes;\n\n\t\tstd::tuple<sibr::HierarchyView::MemSet*, int, int> asyncTask(Point* campos, Point zdir, bool cleanup);\n\n\t\tint* activenodes1_cuda;\n\t\tint* activenodes2_cuda;\n\t\tint* splits1_cuda;\n\t\tint* splits2_cuda;\n\t\tint* nodes_to_expand_cuda;\n\t\tint* rect_cuda;\n\n\t\tfloat* ts_cuda;\n\t\tint* kids_cuda;\n\n\t\tint cuda_nodes_offset = 0;\n\t\tint cuda_gaussians_offset = 0;\n\n\t\tint* num_active_nodes_gpu;\n\t\tint* num_need_children;\n\t\tint num_active_nodes_cpu;\n\t\tint* renderhelper;\n\n\t\tsibr::Matrix4f* view_mat_ptr;\n\t\tsibr::Matrix4f* proj_mat_ptr;\n\n\t\tNode* nodes_to_copy;\n\t\tBox* boxes_to_copy;\n\t\tsibr::Vector3f* pos_to_copy;\n\t\tsibr::Vector4f* rot_to_copy;\n\t\tSHs* shs_to_copy;\n\t\tfloat* alpha_to_copy;\n\t\tsibr::Vector3f* scale_to_copy;\n\n\t\tint skyboxnum = 0;\n\n\t\tbool disable_interp = false;\n\t\tbool show_level = false;\n\t\tbool m_use_cpu = false;\n\n\t\tcudaStream_t renderStream;\n\t\tcudaStream_t maintenanceStream;\n\n\t\tint* NsrcI, *NsrcI2;\n\t\tint* NdstI, * NdstI2;\n\t\tint* numI;\n\t\tchar* NsrcC;\n\t\tchar* scratchspace = nullptr;\n\t\tsize_t scratchspacesize = 0;\n\t};\n\n} /*namespace sibr*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/common.h",
    "content": "/*\n * Copyright (C) 2024, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use\n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact  george.drettakis@inria.fr\n */\n\n#ifndef COMMON_H_INCLUDED\n#define COMMON_H_INCLUDED\n\nstruct Point\n{\n\tfloat xyz[3];\n};\n\nstruct Point4\n{\n\tfloat xyz[4];\n};\n\nstruct Range\n{\n\tint start;\n\tint end;\n};\n\n#endif"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/shaders/copy.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 450\n\nlayout(location = 0) out vec4 out_color;\n\nlayout(std430, binding = 0) buffer colorLayout\n{\n    float data[];\n} source;\n\nuniform bool flip = false;\nuniform int width = 1000;\nuniform int height = 800;\n\nin vec4 texcoord;\n\nvoid main(void)\n{\n\tint x = int(texcoord.x * width);\n\tint y;\n\t\n\tif(flip)\n\t\ty = height - 1 - int(texcoord.y * height);\n\telse\n\t\ty = int(texcoord.y * height);\n\t\n\tfloat r = source.data[0 * width * height + (y * width + x)];\n\tfloat g = source.data[1 * width * height + (y * width + x)];\n\tfloat b = source.data[2 * width * height + (y * width + x)];\n    vec4 color   = vec4(r, g, b, 1);\n    out_color    = color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/shaders/copy.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/** \\file ibr.vp\n *\n * Vertex shader WITHOUT projection and modelview transformations.\n */\n\n#version 450\n\nlayout(location = 0) in vec4 in_vertex;   /**< Input vertex coordinates */\nlayout(location = 1) in vec4 in_texcoord; /**< Input texture coordinates */\nlayout(location = 2) in vec4 in_color;    /**< Input colour value */\n\nout vec4 texcoord;                        /**< Output texture coordinates */\nout vec4 color;                           /**< Output color value */\n\nvoid main(void) {\n  gl_Position = in_vertex;\n  texcoord    = in_texcoord;\n  color       = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/shaders/copy2.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 450\n\nlayout(location = 0) out vec4 out_color;\n\nlayout(std430, binding = 0) buffer colorLayout\n{\n    uint data[];\n} source;\n\nuniform bool flip = false;\nuniform int width = 1000;\nuniform int height = 800;\n\nin vec4 texcoord;\n\nvoid main(void)\n{\n\tint x = int(texcoord.x * width);\n\tint y;\n\t\n\tif(flip)\n\t\ty = height - 1 - int(texcoord.y * height);\n\telse\n\t\ty = int(texcoord.y * height);\n\n\tuint rgba = source.data[(y * width + x)];\n\tfloat frac = 1.0f/255.0f;\n\tfloat r = (rgba & 0xFF) * frac;\n\tfloat g = ((rgba >> 8) & 0xFF) * frac;\n\tfloat b = ((rgba >> 16) & 0xFF) * frac;\n    out_color = vec4(r, g, b, 1);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/hierarchyviewer/renderer/shaders/copy2.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/** \\file ibr.vp\n *\n * Vertex shader WITHOUT projection and modelview transformations.\n */\n\n#version 450\n\nlayout(location = 0) in vec4 in_vertex;   /**< Input vertex coordinates */\nlayout(location = 1) in vec4 in_texcoord; /**< Input texture coordinates */\nlayout(location = 2) in vec4 in_color;    /**< Input colour value */\n\nout vec4 texcoord;                        /**< Output texture coordinates */\nout vec4 color;                           /**< Output color value */\n\nvoid main(void) {\n  gl_Position = in_vertex;\n  texcoord    = in_texcoord;\n  color       = in_color;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\nproject(sibr_remote_all)\n\nadd_subdirectory(apps)\nadd_subdirectory(renderer)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/remote\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/apps/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_remote_apps)\n\nadd_subdirectory(remoteGaussianUI/)"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/apps/remoteGaussianUI/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_remoteGaussian_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\tsibr_remote\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/remote/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"remote\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/apps/remoteGaussianUI/main.cpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <core/system/String.hpp>\n#include \"projects/remote/renderer/RemotePointView.hpp\" \n#include <core/renderer/DepthRenderer.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n#include <algorithm>\n\n#define PROGRAM_NAME \"SIBR Remote Gaussian Viewer\"\nusing namespace sibr;\n\nvoid resetScene(RemoteAppArgs myArgs,\n\tint rendering_width,\n\tint rendering_height,\n\tBasicIBRScene::Ptr& scene,\n\tRemotePointView::Ptr&\tpointBasedView,\n\tstd::shared_ptr<sibr::SceneDebugView>& topView,\n\tMultiViewManager& multiViewManager\n\t)\n{\n\tif (multiViewManager.numSubViews() > 0)\n\t{\n\t\tmultiViewManager.removeSubView(\"Point view\");\n\t\tmultiViewManager.removeSubView(\"Top view\");\n\t}\n\n\tBasicIBRScene::SceneOptions myOpts;\n\tmyOpts.renderTargets = myArgs.loadImages;\n\tmyOpts.mesh = true;\n\tmyOpts.images = myArgs.loadImages;\n\tmyOpts.cameras = true;\n\tmyOpts.texture = false;\n\n\ttry\n\t{\n\t\tscene.reset(new BasicIBRScene(myArgs, myOpts));\n\t}\n\tcatch (std::exception& ex)\n\t{\n\t\tSIBR_ERR << \"Problem loading model info from input path \" << myArgs.dataset_path\n\t\t\t<< \". Consider overriding path to model directory using --path.\";\n\t}\n\n\t// Setup the scene: load the proxy, create the texture arrays.\n\tconst uint flags = SIBR_GPU_LINEAR_SAMPLING | SIBR_FLIP_TEXTURE;\n\n\t// Fix rendering aspect ratio if user provided rendering size\n\tfloat divider = scene->cameras()->inputCameras()[0]->w() / std::min(1200.f, (float)scene->cameras()->inputCameras()[0]->w());\n\tuint scene_width = scene->cameras()->inputCameras()[0]->w();\n\tuint scene_height = scene->cameras()->inputCameras()[0]->h();\n\tfloat scene_aspect_ratio = scene_width * 1.0f / scene_height;\n\tfloat rendering_aspect_ratio = rendering_width * 1.0f / rendering_height;\n\n\tif ((rendering_width > 0) && !myArgs.force_aspect_ratio) {\n\t\tif (abs(scene_aspect_ratio - rendering_aspect_ratio) > 0.001f) {\n\t\t\tif (scene_width > scene_height) {\n\t\t\t\trendering_height = rendering_width / scene_aspect_ratio;\n\t\t\t}\n\t\t\telse {\n\t\t\t\trendering_width = rendering_height * scene_aspect_ratio;\n\t\t\t}\n\t\t}\n\t}\n\n\t// check rendering size\n\trendering_width = (rendering_width <= 0) ? scene->cameras()->inputCameras()[0]->w() / divider : rendering_width;\n\trendering_height = (rendering_height <= 0) ? scene->cameras()->inputCameras()[0]->h() / divider : rendering_height;\n\tVector2u usedResolution(rendering_width, rendering_height);\n\n\tconst unsigned int sceneResWidth = usedResolution.x();\n\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\tpointBasedView->setScene(scene);\n\tpointBasedView->setResolution({ sceneResWidth, sceneResHeight });\n\n\t// Raycaster.\n\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\traycaster->init();\n\traycaster->addMesh(scene->proxies()->proxy());\n\n\t// Camera handler for main view.\n\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), nullptr);\n\n\t// Top view\n\ttopView.reset(new sibr::SceneDebugView(scene, generalCamera, myArgs));\n\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\ttopView->active(false);\n\n\tmultiViewManager.addIBRSubView(\"Point view\", pointBasedView, { sceneResWidth, sceneResHeight }, ImGuiWindowFlags_NoBringToFrontOnFocus);\n\tmultiViewManager.addCameraForView(\"Point view\", generalCamera);\n\n\tCHECK_GL_ERROR;\n\n\t// save images\n\tgeneralCamera->getCameraRecorder().setViewPath(pointBasedView, myArgs.dataset_path.get());\n\tif (myArgs.pathFile.get() != \"\") {\n\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"Point view\"), \"\");\n\t\tif (!myArgs.noExit)\n\t\t\texit(0);\n\t}\n}\n\nint main(int ac, char** av) {\n\n\t// Parse Command-line Args\n\tCommandLineArgs::parseMainArgs(ac, av);\n\tRemoteAppArgs myArgs;\n\tmyArgs.displayHelpIfRequired();\n\n\tif(!myArgs.dataset_path.isInit() && myArgs.pathShort.isInit())\n\t\tmyArgs.dataset_path = myArgs.pathShort.get();\n\n\tconst bool doVSync = !myArgs.vsync;\n\t// rendering size\n\tuint rendering_width = myArgs.rendering_size.get()[0];\n\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\n\t// window size\n\tuint win_width = rendering_width; // myArgs.win_width;\n\tuint win_height = rendering_height; // myArgs.win_height;\n\n\t// Window setup\n\tsibr::Window\t\twindow(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/remote/\" + PROGRAM_NAME + \".ini\");\n\n\t// Add views to mvm.\n\tMultiViewManager        multiViewManager(window, false);\n\tBasicIBRScene::Ptr\t\tscene;\n\tRemotePointView::Ptr\tremoteView(new RemotePointView(myArgs.ip, myArgs.port));\n\tstd::shared_ptr<sibr::SceneDebugView> topView;\n\t\n\tstd::string currentName;\n\n\tbool pathOverride = myArgs.dataset_path.isInit();\n\tif (pathOverride)\n\t{\n\t\tresetScene(myArgs, rendering_width, rendering_height, scene, remoteView, topView, multiViewManager);\n\t}\n\n\t// Main looooooop.\n\twhile (window.isOpened()) \n\t{\n\t\tif (!pathOverride && remoteView->sceneName() != \"\" && remoteView->sceneName() != currentName)\n\t\t{\n\t\t\tcurrentName = remoteView->sceneName();\n\t\t\tmyArgs.dataset_path = currentName;\n\t\t\tresetScene(myArgs, rendering_width, rendering_height, scene, remoteView, topView, multiViewManager);\n\t\t}\n\n\t\tsibr::Input::poll();\n\t\twindow.makeContextCurrent();\n\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\n\t\t\twindow.close();\n\t\t}\n\n\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\tmultiViewManager.onRender(window);\n\n\t\twindow.swapBuffer();\n\t\tCHECK_GL_ERROR;\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/json.hpp",
    "content": "/*\n    __ _____ _____ _____\n __|  |   __|     |   | |  JSON for Modern C++\n|  |  |__   |  |  | | | |  version 3.10.4\n|_____|_____|_____|_|___|  https://github.com/nlohmann/json\n\nLicensed under the MIT License <http://opensource.org/licenses/MIT>.\nSPDX-License-Identifier: MIT\nCopyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.\n\nPermission is hereby  granted, free of charge, to any  person obtaining a copy\nof this software and associated  documentation files (the \"Software\"), to deal\nin the Software  without restriction, including without  limitation the rights\nto  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell\ncopies  of  the Software,  and  to  permit persons  to  whom  the Software  is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE  IS PROVIDED \"AS  IS\", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR\nIMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,\nFITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE\nAUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER\nLIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3\n#define NLOHMANN_JSON_VERSION_MINOR 10\n#define NLOHMANN_JSON_VERSION_PATCH 4\n\n#include <algorithm> // all_of, find, for_each\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#ifndef JSON_NO_IO\n    #include <iosfwd> // istream, ostream\n#endif  // JSON_NO_IO\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n// #include <nlohmann/adl_serializer.hpp>\n\n\n#include <type_traits>\n#include <utility>\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n\n#include <algorithm> // transform\n#include <array> // array\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n#include <vector> // vector\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n#include <string> // string\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    binary,           ///< binary array (ordered collection of bytes)\n    discarded         ///< discarded by the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string < binary\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n- binary is represented as a b\"\" string in python and directly comparable to a\n  string; however, making a binary array directly comparable with a string would\n  be surprising behavior in a JSON file.\n\n@since version 1.0.0\n*/\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    static constexpr std::array<std::uint8_t, 9> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,\n            6 /* binary */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n\n#include <string>\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n#include <utility> // declval, pair\n// #include <nlohmann/thirdparty/hedley/hedley.hpp>\n\n\n/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>.\n * SPDX-License-Identifier: CC0-1.0\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 15\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_CONCAT3_EX)\n    #undef JSON_HEDLEY_CONCAT3_EX\n#endif\n#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c\n\n#if defined(JSON_HEDLEY_CONCAT3)\n    #undef JSON_HEDLEY_CONCAT3\n#endif\n#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(JSON_HEDLEY_MSVC_VERSION)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)\n    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if \\\n    defined(__TI_COMPILER_VERSION__) && \\\n    ( \\\n      defined(__TMS470__) || defined(__TI_ARM__) || \\\n      defined(__MSP430__) || \\\n      defined(__TMS320C2000__) \\\n    )\n#if (__TI_COMPILER_VERSION__ >= 16000000)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)\n    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #undef JSON_HEDLEY_TI_CL430_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)\n    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))\n    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)\n    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)\n    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION\n#endif\n#if defined(__LCC__) && defined(__LCC_MINOR__)\n    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_CRAY_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \\\n    !defined(__COMPCERT__) && \\\n    !defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if \\\n  defined(__has_attribute) && \\\n  ( \\\n    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \\\n  )\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_IAR_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#    if JSON_HEDLEY_HAS_WARNING(\"-Wc++17-extensions\")\n#      if JSON_HEDLEY_HAS_WARNING(\"-Wc++1z-extensions\")\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++1z-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      else\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      endif\n#    else\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    endif\n#  endif\n#endif\n#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wold-style-cast\")\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wold-style-cast\\\"\") \\\n    ((T) (expr)) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"diag_suppress=Pe137\") \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))\n#  endif\n#else\n#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1216,1444,1445\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097,1098\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress=Pe1097\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunused-function\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"clang diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"GCC diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"diag_suppress 3142\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif \\\n    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))\n#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif defined(JSON_HEDLEY_ASSUME)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n#if !defined(JSON_HEDLEY_ASSUME)\n    #if defined(JSON_HEDLEY_UNREACHABLE)\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)\n    #endif\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #if  \\\n        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))\n    #else\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n    #endif\n#else\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))\n#endif\n#if \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))\n#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )\n#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )\n#elif \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \\\n  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n#  define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \\\n    )\n#  define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n#  define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \\\n    )\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define JSON_HEDLEY_PRIVATE\n#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n#  if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    ( \\\n      defined(__TI_EABI__) && \\\n      ( \\\n        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \\\n      ) \\\n    ) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n#  else\n#    define JSON_HEDLEY_PRIVATE\n#    define JSON_HEDLEY_PUBLIC\n#  endif\n#  define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       ( \\\n          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \\\n          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \\\n          !defined(JSON_HEDLEY_PGI_VERSION) && \\\n          !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \\\n       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \\\n       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING(\"-Wbitfield-enum-conversion\"))\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#else\n    #define JSON_HEDLEY_FLAGS\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if \\\n    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n\n#include <type_traits>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate<typename ...Ts> using void_t = typename make_void<Ts...>::type;\n} // namespace detail\n}  // namespace nlohmann\n\n\n// https://en.cppreference.com/w/cpp/experimental/is_detected\nnamespace nlohmann\n{\nnamespace detail\n{\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate<class Default,\n         class AlwaysVoid,\n         template<class...> class Op,\n         class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate<class Default, template<class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate<template<class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate<template<class...> class Op, class... Args>\nstruct is_detected_lazy : is_detected<Op, Args...> { };\n\ntemplate<template<class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate<class Expected, template<class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate<class To, template<class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n}  // namespace detail\n}  // namespace nlohmann\n\n\n// This file contains all internal macro definitions\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// C++ language standard detection\n// if the user manually specified the used c++ version this is skipped\n#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)\n    #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)\n        #define JSON_HAS_CPP_20\n        #define JSON_HAS_CPP_17\n        #define JSON_HAS_CPP_14\n    #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n        #define JSON_HAS_CPP_17\n        #define JSON_HAS_CPP_14\n    #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n        #define JSON_HAS_CPP_14\n    #endif\n    // the cpp 11 flag is always specified because it is the minimal required version\n    #define JSON_HAS_CPP_11\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wdocumentation\"\n    #pragma clang diagnostic ignored \"-Wdocumentation-unknown-command\"\n#endif\n\n// allow to disable exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #include <cstdlib>\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n// allow to override assert\n#if !defined(JSON_ASSERT)\n    #include <cassert> // assert\n    #define JSON_ASSERT(x) assert(x)\n#endif\n\n// allow to access some private functions (needed by the test suite)\n#if defined(JSON_TESTS_PRIVATE)\n    #define JSON_PRIVATE_UNLESS_TESTED public\n#else\n    #define JSON_PRIVATE_UNLESS_TESTED private\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \\\n    template<typename BasicJsonType>                                                            \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \\\n        {                                                                                       \\\n            return ej_pair.first == e;                                                          \\\n        });                                                                                     \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \\\n    }                                                                                           \\\n    template<typename BasicJsonType>                                                            \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                       \\\n            return ej_pair.second == j;                                                         \\\n        });                                                                                     \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer,     \\\n             class BinaryType>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer, BinaryType>\n\n// Macros to simplify conversion from/to types\n\n#define NLOHMANN_JSON_EXPAND( x ) x\n#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME\n#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\\n        NLOHMANN_JSON_PASTE64, \\\n        NLOHMANN_JSON_PASTE63, \\\n        NLOHMANN_JSON_PASTE62, \\\n        NLOHMANN_JSON_PASTE61, \\\n        NLOHMANN_JSON_PASTE60, \\\n        NLOHMANN_JSON_PASTE59, \\\n        NLOHMANN_JSON_PASTE58, \\\n        NLOHMANN_JSON_PASTE57, \\\n        NLOHMANN_JSON_PASTE56, \\\n        NLOHMANN_JSON_PASTE55, \\\n        NLOHMANN_JSON_PASTE54, \\\n        NLOHMANN_JSON_PASTE53, \\\n        NLOHMANN_JSON_PASTE52, \\\n        NLOHMANN_JSON_PASTE51, \\\n        NLOHMANN_JSON_PASTE50, \\\n        NLOHMANN_JSON_PASTE49, \\\n        NLOHMANN_JSON_PASTE48, \\\n        NLOHMANN_JSON_PASTE47, \\\n        NLOHMANN_JSON_PASTE46, \\\n        NLOHMANN_JSON_PASTE45, \\\n        NLOHMANN_JSON_PASTE44, \\\n        NLOHMANN_JSON_PASTE43, \\\n        NLOHMANN_JSON_PASTE42, \\\n        NLOHMANN_JSON_PASTE41, \\\n        NLOHMANN_JSON_PASTE40, \\\n        NLOHMANN_JSON_PASTE39, \\\n        NLOHMANN_JSON_PASTE38, \\\n        NLOHMANN_JSON_PASTE37, \\\n        NLOHMANN_JSON_PASTE36, \\\n        NLOHMANN_JSON_PASTE35, \\\n        NLOHMANN_JSON_PASTE34, \\\n        NLOHMANN_JSON_PASTE33, \\\n        NLOHMANN_JSON_PASTE32, \\\n        NLOHMANN_JSON_PASTE31, \\\n        NLOHMANN_JSON_PASTE30, \\\n        NLOHMANN_JSON_PASTE29, \\\n        NLOHMANN_JSON_PASTE28, \\\n        NLOHMANN_JSON_PASTE27, \\\n        NLOHMANN_JSON_PASTE26, \\\n        NLOHMANN_JSON_PASTE25, \\\n        NLOHMANN_JSON_PASTE24, \\\n        NLOHMANN_JSON_PASTE23, \\\n        NLOHMANN_JSON_PASTE22, \\\n        NLOHMANN_JSON_PASTE21, \\\n        NLOHMANN_JSON_PASTE20, \\\n        NLOHMANN_JSON_PASTE19, \\\n        NLOHMANN_JSON_PASTE18, \\\n        NLOHMANN_JSON_PASTE17, \\\n        NLOHMANN_JSON_PASTE16, \\\n        NLOHMANN_JSON_PASTE15, \\\n        NLOHMANN_JSON_PASTE14, \\\n        NLOHMANN_JSON_PASTE13, \\\n        NLOHMANN_JSON_PASTE12, \\\n        NLOHMANN_JSON_PASTE11, \\\n        NLOHMANN_JSON_PASTE10, \\\n        NLOHMANN_JSON_PASTE9, \\\n        NLOHMANN_JSON_PASTE8, \\\n        NLOHMANN_JSON_PASTE7, \\\n        NLOHMANN_JSON_PASTE6, \\\n        NLOHMANN_JSON_PASTE5, \\\n        NLOHMANN_JSON_PASTE4, \\\n        NLOHMANN_JSON_PASTE3, \\\n        NLOHMANN_JSON_PASTE2, \\\n        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))\n#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)\n#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)\n#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)\n#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)\n#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)\n#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)\n#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)\n#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)\n#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)\n#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)\n#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)\n#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)\n#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)\n#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)\n#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)\n#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)\n#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)\n#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)\n#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)\n#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)\n#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)\n#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)\n#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)\n#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)\n#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)\n#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)\n#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)\n#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)\n#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)\n#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)\n#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)\n#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)\n#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)\n#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)\n#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)\n#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)\n#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)\n#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)\n#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)\n#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)\n#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)\n#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)\n#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)\n#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)\n#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)\n#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)\n#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)\n#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)\n#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)\n#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)\n#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)\n#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)\n#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)\n#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)\n#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)\n#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)\n#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)\n#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)\n#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)\n#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)\n#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)\n#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)\n#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)\n\n#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;\n#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \\\n    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \\\n    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n\n// inspired from https://stackoverflow.com/a/26745591\n// allows to call any std function as if (e.g. with begin):\n// using std::begin; begin(x);\n//\n// it allows using the detected idiom to retrieve the return type\n// of such an expression\n#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name)                                 \\\n    namespace detail {                                                            \\\n    using std::std_name;                                                          \\\n    \\\n    template<typename... T>                                                       \\\n    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \\\n    }                                                                             \\\n    \\\n    namespace detail2 {                                                           \\\n    struct std_name##_tag                                                         \\\n    {                                                                             \\\n    };                                                                            \\\n    \\\n    template<typename... T>                                                       \\\n    std_name##_tag std_name(T&&...);                                              \\\n    \\\n    template<typename... T>                                                       \\\n    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \\\n    \\\n    template<typename... T>                                                       \\\n    struct would_call_std_##std_name                                              \\\n    {                                                                             \\\n        static constexpr auto const value = ::nlohmann::detail::                  \\\n                                            is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \\\n    };                                                                            \\\n    } /* namespace detail2 */ \\\n    \\\n    template<typename... T>                                                       \\\n    struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...>   \\\n    {                                                                             \\\n    }\n\n#ifndef JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_USE_IMPLICIT_CONVERSIONS 1\n#endif\n\n#if JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_EXPLICIT\n#else\n    #define JSON_EXPLICIT explicit\n#endif\n\n#ifndef JSON_DIAGNOSTICS\n    #define JSON_DIAGNOSTICS 0\n#endif\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief replace all occurrences of a substring by another string\n\n@param[in,out] s  the string to manipulate; changed so that all\n               occurrences of @a f are replaced with @a t\n@param[in]     f  the substring to replace with @a t\n@param[in]     t  the string to replace @a f\n\n@pre The search string @a f must not be empty. **This precondition is\nenforced with an assertion.**\n\n@since version 2.0.0\n*/\ninline void replace_substring(std::string& s, const std::string& f,\n                              const std::string& t)\n{\n    JSON_ASSERT(!f.empty());\n    for (auto pos = s.find(f);                // find first occurrence of f\n            pos != std::string::npos;         // make sure f was found\n            s.replace(pos, f.size(), t),      // replace with t, and\n            pos = s.find(f, pos + t.size()))  // find next occurrence of f\n    {}\n}\n\n/*!\n * @brief string escaping as described in RFC 6901 (Sect. 4)\n * @param[in] s string to escape\n * @return    escaped string\n *\n * Note the order of escaping \"~\" to \"~0\" and \"/\" to \"~1\" is important.\n */\ninline std::string escape(std::string s)\n{\n    replace_substring(s, \"~\", \"~0\");\n    replace_substring(s, \"/\", \"~1\");\n    return s;\n}\n\n/*!\n * @brief string unescaping as described in RFC 6901 (Sect. 4)\n * @param[in] s string to unescape\n * @return    unescaped string\n *\n * Note the order of escaping \"~1\" to \"/\" and \"~0\" to \"~\" is important.\n */\nstatic void unescape(std::string& s)\n{\n    replace_substring(s, \"~1\", \"/\");\n    replace_substring(s, \"~0\", \"~\");\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n\n#include <cstddef> // size_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////////\n// exceptions //\n////////////////\n\n/*!\n@brief general exception of the @ref basic_json class\n\nThis class is an extension of `std::exception` objects with a member @a id for\nexception ids. It is used as the base class for all exceptions thrown by the\n@ref basic_json class. This class can hence be used as \"wildcard\" to catch\nexceptions.\n\nSubclasses:\n- @ref parse_error for exceptions indicating a parse error\n- @ref invalid_iterator for exceptions indicating errors with iterators\n- @ref type_error for exceptions indicating executing a member function with\n                  a wrong type\n- @ref out_of_range for exceptions indicating access out of the defined range\n- @ref other_error for exceptions indicating other library errors\n\n@internal\n@note To have nothrow-copy-constructible exceptions, we internally use\n      `std::runtime_error` which can cope with arbitrary-length error messages.\n      Intermediate strings are built with static functions and then passed to\n      the actual constructor.\n@endinternal\n\n@liveexample{The following code shows how arbitrary library exceptions can be\ncaught.,exception}\n\n@since version 3.0.0\n*/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return \"[json.exception.\" + ename + \".\" + std::to_string(id_) + \"] \";\n    }\n\n    template<typename BasicJsonType>\n    static std::string diagnostics(const BasicJsonType& leaf_element)\n    {\n#if JSON_DIAGNOSTICS\n        std::vector<std::string> tokens;\n        for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent)\n        {\n            switch (current->m_parent->type())\n            {\n                case value_t::array:\n                {\n                    for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)\n                    {\n                        if (&current->m_parent->m_value.array->operator[](i) == current)\n                        {\n                            tokens.emplace_back(std::to_string(i));\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::object:\n                {\n                    for (const auto& element : *current->m_parent->m_value.object)\n                    {\n                        if (&element.second == current)\n                        {\n                            tokens.emplace_back(element.first.c_str());\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::null: // LCOV_EXCL_LINE\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:   // LCOV_EXCL_LINE\n                    break; // LCOV_EXCL_LINE\n            }\n        }\n\n        if (tokens.empty())\n        {\n            return \"\";\n        }\n\n        return \"(\" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},\n                                     [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + detail::escape(b);\n        }) + \") \";\n#else\n        static_cast<void>(leaf_element);\n        return \"\";\n#endif\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/*!\n@brief exception indicating a parse error\n\nThis exception is thrown by the library when a parse error occurs. Parse errors\ncan occur during the deserialization of JSON text, CBOR, MessagePack, as well\nas when using JSON Patch.\n\nMember @a byte holds the byte index of the last read character in the input\nfile.\n\nExceptions have ids 1xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.\njson.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\\uxxxx` entries (\"surrogate pairs\"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.\njson.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.\njson.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.\njson.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one \"op\" member, whose value indicates the operation to perform. Its value must be one of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\"; other values are errors.\njson.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.\njson.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.\njson.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.\njson.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.\njson.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.\njson.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.\njson.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.\njson.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).\njson.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed.\n\n@note For an input with n bytes, 1 is the index of the first character and n+1\n      is the index of the terminating null byte or the end of file. This also\n      holds true when reading a byte vector (CBOR or MessagePack).\n\n@liveexample{The following code shows how a `parse_error` exception can be\ncaught.,parse_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    template<typename BasicJsonType>\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        position_string(pos) + \": \" + exception::diagnostics(context) + what_arg;\n        return {id_, pos.chars_read_total, w.c_str()};\n    }\n\n    template<typename BasicJsonType>\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        (byte_ != 0 ? (\" at byte \" + std::to_string(byte_)) : \"\") +\n                        \": \" + exception::diagnostics(context) + what_arg;\n        return {id_, byte_, w.c_str()};\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return \" at line \" + std::to_string(pos.lines_read + 1) +\n               \", column \" + std::to_string(pos.chars_read_current_line);\n    }\n};\n\n/*!\n@brief exception indicating errors with iterators\n\nThis exception is thrown if iterators passed to a library function do not match\nthe expected semantics.\n\nExceptions have ids 2xx.\n\nname / id                           | example message | description\n----------------------------------- | --------------- | -------------------------\njson.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.\njson.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.\njson.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.\njson.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.\njson.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.\njson.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.\njson.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.\njson.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.\njson.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.\njson.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().\n\n@liveexample{The following code shows how an `invalid_iterator` exception can be\ncaught.,invalid_iterator}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass invalid_iterator : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"invalid_iterator\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating executing a member function with a wrong type\n\nThis exception is thrown in case of a type error; that is, a library function is\nexecuted on a JSON value whose type does not match the expected semantics.\n\nExceptions have ids 3xx.\n\nname / id                     | example message | description\n----------------------------- | --------------- | -------------------------\njson.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.\njson.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.\njson.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.\njson.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.\njson.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.\njson.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.\njson.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.\njson.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.\njson.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.\njson.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.\njson.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.\njson.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.\njson.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.\njson.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.\njson.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.\njson.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |\njson.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |\n\n@liveexample{The following code shows how a `type_error` exception can be\ncaught.,type_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass type_error : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"type_error\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating access out of the defined range\n\nThis exception is thrown in case a library function is called on an input\nparameter that exceeds the expected range, for instance in case of array\nindices or nonexisting object keys.\n\nExceptions have ids 4xx.\n\nname / id                       | example message | description\n------------------------------- | --------------- | -------------------------\njson.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.\njson.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.\njson.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.\njson.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.\njson.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.\njson.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.\njson.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |\njson.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |\njson.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |\n\n@liveexample{The following code shows how an `out_of_range` exception can be\ncaught.,out_of_range}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass out_of_range : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"out_of_range\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating other library errors\n\nThis exception is thrown in case of errors that cannot be classified with the\nother exception types.\n\nExceptions have ids 5xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.other_error.501 | unsuccessful: {\"op\":\"test\",\"path\":\"/baz\", \"value\":\"bar\"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n\n@liveexample{The following code shows how an `other_error` exception can be\ncaught.,other_error}\n\n@since version 3.0.0\n*/\nclass other_error : public exception\n{\n  public:\n    template<typename BasicJsonType>\n    static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)\n    {\n        std::string w = exception::name(\"other_error\", id_) + exception::diagnostics(context) + what_arg;\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n#include <utility> // index_sequence, make_index_sequence, index_sequence_for\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n#ifdef JSON_HAS_CPP_14\n\n// the following utilities are natively available in C++14\nusing std::enable_if_t;\nusing std::index_sequence;\nusing std::make_index_sequence;\nusing std::index_sequence_for;\n\n#else\n\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\n// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h\n// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.\n\n//// START OF CODE FROM GOOGLE ABSEIL\n\n// integer_sequence\n//\n// Class template representing a compile-time integer sequence. An instantiation\n// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its\n// type through its template arguments (which is a common need when\n// working with C++11 variadic templates). `absl::integer_sequence` is designed\n// to be a drop-in replacement for C++14's `std::integer_sequence`.\n//\n// Example:\n//\n//   template< class T, T... Ints >\n//   void user_function(integer_sequence<T, Ints...>);\n//\n//   int main()\n//   {\n//     // user_function's `T` will be deduced to `int` and `Ints...`\n//     // will be deduced to `0, 1, 2, 3, 4`.\n//     user_function(make_integer_sequence<int, 5>());\n//   }\ntemplate <typename T, T... Ints>\nstruct integer_sequence\n{\n    using value_type = T;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\n// index_sequence\n//\n// A helper template for an `integer_sequence` of `size_t`,\n// `absl::index_sequence` is designed to be a drop-in replacement for C++14's\n// `std::index_sequence`.\ntemplate <size_t... Ints>\nusing index_sequence = integer_sequence<size_t, Ints...>;\n\nnamespace utility_internal\n{\n\ntemplate <typename Seq, size_t SeqSize, size_t Rem>\nstruct Extend;\n\n// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 0>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;\n};\n\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 1>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;\n};\n\n// Recursion helper for 'make_integer_sequence<T, N>'.\n// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.\ntemplate <typename T, size_t N>\nstruct Gen\n{\n    using type =\n        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;\n};\n\ntemplate <typename T>\nstruct Gen<T, 0>\n{\n    using type = integer_sequence<T>;\n};\n\n}  // namespace utility_internal\n\n// Compile-time sequences of integers\n\n// make_integer_sequence\n//\n// This template alias is equivalent to\n// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in\n// replacement for C++14's `std::make_integer_sequence`.\ntemplate <typename T, T N>\nusing make_integer_sequence = typename utility_internal::Gen<T, N>::type;\n\n// make_index_sequence\n//\n// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,\n// and is designed to be a drop-in replacement for C++14's\n// `std::make_index_sequence`.\ntemplate <size_t N>\nusing make_index_sequence = make_integer_sequence<size_t, N>;\n\n// index_sequence_for\n//\n// Converts a typename pack into an index sequence of the same length, and\n// is designed to be a drop-in replacement for C++14's\n// `std::index_sequence_for()`\ntemplate <typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n//// END OF CODE FROM GOOGLE ABSEIL\n\n#endif\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static constexpr T value{};\n};\n\ntemplate<typename T>\nconstexpr T static_const<T>::value;\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/identity_tag.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// dispatching helper struct\ntemplate <class T> struct identity_tag {};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n#include <tuple> // tuple\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n\n#include <iterator> // random_access_iterator_tag\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename It, typename = void>\nstruct iterator_types {};\n\ntemplate<typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate<typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n            : iterator_types<T>\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/meta/call_std/begin.hpp>\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);\n} // namespace nlohmann\n\n// #include <nlohmann/detail/meta/call_std/end.hpp>\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n#define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer,\n         class BinaryType = std::vector<std::uint8_t>>\nclass basic_json;\n\n/*!\n@brief JSON Pointer\n\nA JSON pointer defines a string syntax for identifying a specific value\nwithin a JSON document. It can be used with functions `at` and\n`operator[]`. Furthermore, JSON pointers are the base for JSON patches.\n\n@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)\n\n@since version 2.0.0\n*/\ntemplate<typename BasicJsonType>\nclass json_pointer;\n\n/*!\n@brief default JSON class\n\nThis type is the default specialization of the @ref basic_json class which\nuses the standard template types.\n\n@since version 1.0.0\n*/\nusing json = basic_json<>;\n\ntemplate<class Key, class T, class IgnoredLess, class Allocator>\nstruct ordered_map;\n\n/*!\n@brief ordered JSON class\n\nThis type preserves the insertion order of object keys.\n\n@since version 3.9.0\n*/\nusing ordered_json = basic_json<nlohmann::ordered_map>;\n\n}  // namespace nlohmann\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n\nnamespace nlohmann\n{\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n//////////////////////\n// json_ref helpers //\n//////////////////////\n\ntemplate<typename>\nclass json_ref;\n\ntemplate<typename>\nstruct is_json_ref : std::false_type {};\n\ntemplate<typename T>\nstruct is_json_ref<json_ref<T>> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate<typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate<typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate<typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate<typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate<typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate<typename T>\nusing reference_t = typename T::reference;\n\ntemplate<typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate<typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate<typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate<typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\n// trait checking if j.get<T> is valid\n// use this trait instead of std::is_constructible or std::is_convertible,\n// both rely on, or make use of implicit conversions, and thus fail when T\n// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)\ntemplate <typename BasicJsonType, typename T>\nstruct is_getable\n{\n    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;\n};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\n\n///////////////////\n// is_ functions //\n///////////////////\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B1> struct conjunction<B1> : B1 { };\ntemplate<class B1, class... Bn>\nstruct conjunction<B1, Bn...>\n: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};\n\n// https://en.cppreference.com/w/cpp/types/negation\ntemplate<class B> struct negation : std::integral_constant < bool, !B::value > { };\n\n// Reimplementation of is_constructible and is_default_constructible, due to them being broken for\n// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).\n// This causes compile errors in e.g. clang 3.5 or gcc 4.9.\ntemplate <typename T>\nstruct is_default_constructible : std::is_default_constructible<T> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<std::pair<T1, T2>>\n            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<const std::pair<T1, T2>>\n            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<std::tuple<Ts...>>\n            : conjunction<is_default_constructible<Ts>...> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<const std::tuple<Ts...>>\n            : conjunction<is_default_constructible<Ts>...> {};\n\n\ntemplate <typename T, typename... Args>\nstruct is_constructible : std::is_constructible<T, Args...> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};\n\n\ntemplate<typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate<typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\ntemplate<typename T>\nstruct is_range\n{\n  private:\n    using t_ref = typename std::add_lvalue_reference<T>::type;\n\n    using iterator = detected_t<result_of_begin, t_ref>;\n    using sentinel = detected_t<result_of_end, t_ref>;\n\n    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator\n    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for\n    // but reimplementing these would be too much work, as a lot of other concepts are used underneath\n    static constexpr auto is_iterator_begin =\n        is_iterator_traits<iterator_traits<iterator>>::value;\n\n  public:\n    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;\n};\n\ntemplate<typename R>\nusing iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;\n\ntemplate<typename T>\nusing range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;\n\n// The following implementation of is_complete_type is taken from\n// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/\n// and is written by Xiang Fan who agreed to using it in this library.\n\ntemplate<typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate<typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value &&\n        is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (is_default_constructible<ConstructibleObjectType>::value &&\n         (std::is_move_assignable<ConstructibleObjectType>::value ||\n          std::is_copy_assignable<ConstructibleObjectType>::value) &&\n         (is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value &&\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) ||\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type\n{\n    static constexpr auto value =\n        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n{\n    static constexpr auto value =\n        is_constructible<ConstructibleStringType,\n        typename BasicJsonType::string_t>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t <\n    is_detected<iterator_t, CompatibleArrayType>::value&&\n    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        is_constructible<BasicJsonType,\n        range_value_t<CompatibleArrayType>>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t < !std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value&&\n    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n    is_default_constructible<ConstructibleArrayType>::value&&\n(std::is_move_assignable<ConstructibleArrayType>::value ||\n std::is_copy_assignable<ConstructibleArrayType>::value)&&\nis_detected<iterator_t, ConstructibleArrayType>::value&&\nis_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&\nis_detected<range_value_t, ConstructibleArrayType>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&\n        is_complete_type <\n        detected_t<range_value_t, ConstructibleArrayType >>::value >>\n{\n    using value_type = range_value_t<ConstructibleArrayType>;\n\n    static constexpr bool value =\n        std::is_same<value_type,\n        typename BasicJsonType::array_t::value_type>::value ||\n        has_from_json<BasicJsonType,\n        value_type>::value ||\n        has_non_default_from_json <\n        BasicJsonType,\n        value_type >::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType,\n         typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t < std::is_integral<RealIntegerType>::value&&\n    std::is_integral<CompatibleNumberIntegerType>::value&&\n    !std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value &&\n        CompatibleLimits::is_integer &&\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\ntemplate<typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate<typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};\n\n// a naive helper to check if a type is an ordered_map (exploits the fact that\n// ordered_map inherits capacity() from std::vector)\ntemplate <typename T>\nstruct is_ordered_map\n{\n    using one = char;\n\n    struct two\n    {\n        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    };\n\n    template <typename C> static one test( decltype(&C::capacity) ) ;\n    template <typename C> static two test(...);\n\n    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n};\n\n// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)\ntemplate < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >\nT conditional_static_cast(U value)\n{\n    return static_cast<T>(value);\n}\n\ntemplate<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>\nT conditional_static_cast(U value)\n{\n    return value;\n}\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#ifdef JSON_HAS_CPP_17\n    #include <filesystem>\n#endif\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be null, but is \" + std::string(j.type_name()), j));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&\n                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                         int > = 0 >\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::boolean:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name()), j));\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(j.type_name()), j));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name()), j));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename ConstructibleStringType,\n    enable_if_t <\n        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&\n        !std::is_same<typename BasicJsonType::string_t,\n                      ConstructibleStringType>::value,\n        int > = 0 >\nvoid from_json(const BasicJsonType& j, ConstructibleStringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name()), j));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    l.resize(j.size());\n    std::transform(j.begin(), j.end(), std::begin(l),\n                   [](const BasicJsonType & elem)\n    {\n        return elem.template get<T>();\n    });\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\nvoid from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                          priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate < typename BasicJsonType, typename ConstructibleArrayType,\n           enable_if_t <\n               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&\n               !is_basic_json<ConstructibleArrayType>::value,\n               int > = 0 >\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t... Idx >\nstd::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,\n        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)\n{\n    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t N >\nauto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)\n-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n\n    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(j.type_name()), j));\n    }\n\n    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be object, but is \" + std::string(j.type_name()), j));\n    }\n\n    ConstructibleObjectType ret;\n    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t <\n               std::is_arithmetic<ArithmeticType>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n               int > = 0 >\nvoid from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name()), j));\n    }\n}\n\ntemplate<typename BasicJsonType, typename... Args, std::size_t... Idx>\nstd::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)\n{\n    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);\n}\n\ntemplate < typename BasicJsonType, class A1, class A2 >\nstd::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)\n{\n    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),\n            std::forward<BasicJsonType>(j).at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\nvoid from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)\n{\n    p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nstd::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)\n{\n    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)\n{\n    t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename TupleRelated>\nauto from_json(BasicJsonType&& j, TupleRelated&& t)\n-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n\n    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name()), j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name()), j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name()), j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\n#ifdef JSON_HAS_CPP_17\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, std::filesystem::path& p)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name()), j));\n    }\n    p = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n#endif\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T&& val) const\n    noexcept(noexcept(from_json(j, std::forward<T>(val))))\n    -> decltype(from_json(j, std::forward<T>(val)))\n    {\n        return from_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\nconstexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)\n} // namespace\n} // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n\n#include <algorithm> // copy\n#include <iterator> // begin, end\n#include <string> // string\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n\n#include <cstddef> // size_t\n#include <iterator> // input_iterator_tag\n#include <string> // string, to_string\n#include <tuple> // tuple_size, get, tuple_element\n#include <utility> // move\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename string_type>\nvoid int_to_string( string_type& target, std::size_t value )\n{\n    // For ADL\n    using std::to_string;\n    target = to_string(value);\n}\ntemplate<typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type * ;\n    using reference = value_type & ;\n    using iterator_category = std::input_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor;\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    const string_type empty_str{};\n\n  public:\n    explicit iteration_proxy_value(IteratorType it) noexcept\n        : anchor(std::move(it))\n    {}\n\n    /// dereference operator (needed for range-based for)\n    iteration_proxy_value& operator*()\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        JSON_ASSERT(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::reference container;\n\n  public:\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(cont) {}\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.end());\n    }\n};\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate<typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>\n            : public std::integral_constant<std::size_t, 2> {};\n\ntemplate<std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n} // namespace std\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#ifdef JSON_HAS_CPP_17\n    #include <filesystem>\n#endif\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////\n// constructors //\n//////////////////\n\n/*\n * Note all external_constructor<>::construct functions need to call\n * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an\n * allocated value (e.g., a string). See bug issue\n * https://github.com/nlohmann/json/issues/2865 for more information.\n */\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleStringType,\n               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::binary>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::binary;\n        j.m_value = typename BasicJsonType::binary_t(b);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::binary;\n        j.m_value = typename BasicJsonType::binary_t(std::move(b));\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleArrayType,\n               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n            j.set_parent(j.m_value.array->back());\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        }\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleObjectType,\n               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\nvoid to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate < typename BasicJsonType, typename CompatibleArrayType,\n           enable_if_t < is_compatible_array_type<BasicJsonType,\n                         CompatibleArrayType>::value&&\n                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&\n                         !is_basic_json<CompatibleArrayType>::value,\n                         int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)\n{\n    external_constructor<value_t::binary>::construct(j, bin);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate < typename BasicJsonType, typename CompatibleObjectType,\n           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,\n                  const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n                  int > = 0 >\nvoid to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\nvoid to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\n#ifdef JSON_HAS_CPP_17\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::filesystem::path& p)\n{\n    j = p.string();\n}\n#endif\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `to_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\nconstexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)\n} // namespace\n} // namespace nlohmann\n\n// #include <nlohmann/detail/meta/identity_tag.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\n\ntemplate<typename ValueType, typename>\nstruct adl_serializer\n{\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @note This function is chosen for default-constructible value types.\n\n    @param[in] j        JSON value to read from\n    @param[in,out] val  value to write to\n    */\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @note This function is chosen for value types which are not default-constructible.\n\n    @param[in] j  JSON value to read from\n\n    @return copy of the JSON value, converted to @a ValueType\n    */\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j) noexcept(\n    noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))\n    {\n        return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});\n    }\n\n    /*!\n    @brief convert any value type to a JSON value\n\n    This function is usually called by the constructors of the @ref basic_json\n    class.\n\n    @param[in,out] j  JSON value to write to\n    @param[in] val    value to read from\n    */\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<TargetType>(val));\n    }\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/byte_container_with_subtype.hpp>\n\n\n#include <cstdint> // uint8_t, uint64_t\n#include <tuple> // tie\n#include <utility> // move\n\nnamespace nlohmann\n{\n\n/*!\n@brief an internal type for a backed binary type\n\nThis type extends the template parameter @a BinaryType provided to `basic_json`\nwith a subtype used by BSON and MessagePack. This type exists so that the user\ndoes not have to specify a type themselves with a specific naming scheme in\norder to override the binary type.\n\n@tparam BinaryType container to store bytes (`std::vector<std::uint8_t>` by\n                   default)\n\n@since version 3.8.0; changed type of subtypes to std::uint64_t in 3.10.0.\n*/\ntemplate<typename BinaryType>\nclass byte_container_with_subtype : public BinaryType\n{\n  public:\n    /// the type of the underlying container\n    using container_type = BinaryType;\n    /// the type of the subtype\n    using subtype_type = std::uint64_t;\n\n    byte_container_with_subtype() noexcept(noexcept(container_type()))\n        : container_type()\n    {}\n\n    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n    {}\n\n    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n    {}\n\n    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    bool operator==(const byte_container_with_subtype& rhs) const\n    {\n        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==\n               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);\n    }\n\n    bool operator!=(const byte_container_with_subtype& rhs) const\n    {\n        return !(rhs == *this);\n    }\n\n    /*!\n    @brief sets the binary subtype\n\n    Sets the binary subtype of the value, also flags a binary JSON value as\n    having a subtype, which has implications for serialization.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref subtype() -- return the binary subtype\n    @sa see @ref clear_subtype() -- clears the binary subtype\n    @sa see @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void set_subtype(subtype_type subtype_) noexcept\n    {\n        m_subtype = subtype_;\n        m_has_subtype = true;\n    }\n\n    /*!\n    @brief return the binary subtype\n\n    Returns the numerical subtype of the value if it has a subtype. If it does\n    not have a subtype, this function will return subtype_type(-1) as a sentinel\n    value.\n\n    @return the numerical subtype of the binary value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref set_subtype() -- sets the binary subtype\n    @sa see @ref clear_subtype() -- clears the binary subtype\n    @sa see @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0; fixed return value to properly return\n           subtype_type(-1) as documented in version 3.10.0\n    */\n    constexpr subtype_type subtype() const noexcept\n    {\n        return m_has_subtype ? m_subtype : subtype_type(-1);\n    }\n\n    /*!\n    @brief return whether the value has a subtype\n\n    @return whether the value has a subtype\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref subtype() -- return the binary subtype\n    @sa see @ref set_subtype() -- sets the binary subtype\n    @sa see @ref clear_subtype() -- clears the binary subtype\n\n    @since version 3.8.0\n    */\n    constexpr bool has_subtype() const noexcept\n    {\n        return m_has_subtype;\n    }\n\n    /*!\n    @brief clears the binary subtype\n\n    Clears the binary subtype and flags the value as not having a subtype, which\n    has implications for serialization; for instance MessagePack will prefer the\n    bin family over the ext family.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa see @ref subtype() -- return the binary subtype\n    @sa see @ref set_subtype() -- sets the binary subtype\n    @sa see @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void clear_subtype() noexcept\n    {\n        m_subtype = 0;\n        m_has_subtype = false;\n    }\n\n  private:\n    subtype_type m_subtype = 0;\n    bool m_has_subtype = false;\n};\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/hash.hpp>\n\n\n#include <cstdint> // uint8_t\n#include <cstddef> // size_t\n#include <functional> // hash\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n// boost::hash_combine\ninline std::size_t combine(std::size_t seed, std::size_t h) noexcept\n{\n    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);\n    return seed;\n}\n\n/*!\n@brief hash a JSON value\n\nThe hash function tries to rely on std::hash where possible. Furthermore, the\ntype of the JSON value is taken into account to have different hash values for\nnull, 0, 0U, and false, etc.\n\n@tparam BasicJsonType basic_json specialization\n@param j JSON value to hash\n@return hash value of j\n*/\ntemplate<typename BasicJsonType>\nstd::size_t hash(const BasicJsonType& j)\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n    const auto type = static_cast<std::size_t>(j.type());\n    switch (j.type())\n    {\n        case BasicJsonType::value_t::null:\n        case BasicJsonType::value_t::discarded:\n        {\n            return combine(type, 0);\n        }\n\n        case BasicJsonType::value_t::object:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j.items())\n            {\n                const auto h = std::hash<string_t> {}(element.key());\n                seed = combine(seed, h);\n                seed = combine(seed, hash(element.value()));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::array:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j)\n            {\n                seed = combine(seed, hash(element));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::string:\n        {\n            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::boolean:\n        {\n            const auto h = std::hash<bool> {}(j.template get<bool>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_integer:\n        {\n            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_unsigned:\n        {\n            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_float:\n        {\n            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::binary:\n        {\n            auto seed = combine(type, j.get_binary().size());\n            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());\n            seed = combine(seed, h);\n            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));\n            for (const auto byte : j.get_binary())\n            {\n                seed = combine(seed, std::hash<std::uint8_t> {}(byte));\n            }\n            return seed;\n        }\n\n        default:                   // LCOV_EXCL_LINE\n            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            return 0;              // LCOV_EXCL_LINE\n    }\n}\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstring> // strlen\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n#ifndef JSON_NO_IO\n    #include <cstdio>   // FILE *\n    #include <istream>  // istream\n#endif                  // JSON_NO_IO\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson };\n\n////////////////////\n// input adapters //\n////////////////////\n\n#ifndef JSON_NO_IO\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter\n{\n  public:\n    using char_type = char;\n\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f) noexcept\n        : m_file(f)\n    {}\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) noexcept = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = delete;\n    ~file_input_adapter() = default;\n\n    std::char_traits<char>::int_type get_character() noexcept\n    {\n        return std::fgetc(m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter\n{\n  public:\n    using char_type = char;\n\n    ~input_stream_adapter()\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        if (is != nullptr)\n        {\n            is->clear(is->rdstate() & std::ios::eofbit);\n        }\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(&i), sb(i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&&) = delete;\n\n    input_stream_adapter(input_stream_adapter&& rhs) noexcept\n        : is(rhs.is), sb(rhs.sb)\n    {\n        rhs.is = nullptr;\n        rhs.sb = nullptr;\n    }\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, eg. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character()\n    {\n        auto res = sb->sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream* is = nullptr;\n    std::streambuf* sb = nullptr;\n};\n#endif  // JSON_NO_IO\n\n// General-purpose iterator-based adapter. It might not be as fast as\n// theoretically possible for some containers, but it is extremely versatile.\ntemplate<typename IteratorType>\nclass iterator_input_adapter\n{\n  public:\n    using char_type = typename std::iterator_traits<IteratorType>::value_type;\n\n    iterator_input_adapter(IteratorType first, IteratorType last)\n        : current(std::move(first)), end(std::move(last))\n    {}\n\n    typename std::char_traits<char_type>::int_type get_character()\n    {\n        if (JSON_HEDLEY_LIKELY(current != end))\n        {\n            auto result = std::char_traits<char_type>::to_int_type(*current);\n            std::advance(current, 1);\n            return result;\n        }\n\n        return std::char_traits<char_type>::eof();\n    }\n\n  private:\n    IteratorType current;\n    IteratorType end;\n\n    template<typename BaseInputAdapter, size_t T>\n    friend struct wide_string_input_helper;\n\n    bool empty() const\n    {\n        return current == end;\n    }\n};\n\n\ntemplate<typename BaseInputAdapter, size_t T>\nstruct wide_string_input_helper;\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 4>\n{\n    // UTF-32\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 2>\n{\n    // UTF-16\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc || wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (JSON_HEDLEY_UNLIKELY(!input.empty()))\n                {\n                    const auto wc2 = static_cast<unsigned int>(input.get_character());\n                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\n// Wraps another input apdater to convert wide character types into individual bytes.\ntemplate<typename BaseInputAdapter, typename WideCharType>\nclass wide_string_input_adapter\n{\n  public:\n    using char_type = char;\n\n    wide_string_input_adapter(BaseInputAdapter base)\n        : base_adapter(base) {}\n\n    typename std::char_traits<char>::int_type get_character() noexcept\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(WideCharType)>();\n\n            JSON_ASSERT(utf8_bytes_filled > 0);\n            JSON_ASSERT(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        JSON_ASSERT(utf8_bytes_filled > 0);\n        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    BaseInputAdapter base_adapter;\n\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\n\ntemplate<typename IteratorType, typename Enable = void>\nstruct iterator_input_adapter_factory\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using adapter_type = iterator_input_adapter<iterator_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(std::move(first), std::move(last));\n    }\n};\n\ntemplate<typename T>\nstruct is_iterator_of_multibyte\n{\n    using value_type = typename std::iterator_traits<T>::value_type;\n    enum\n    {\n        value = sizeof(value_type) > 1\n    };\n};\n\ntemplate<typename IteratorType>\nstruct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using base_adapter_type = iterator_input_adapter<iterator_type>;\n    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(base_adapter_type(std::move(first), std::move(last)));\n    }\n};\n\n// General purpose iterator-based input\ntemplate<typename IteratorType>\ntypename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)\n{\n    using factory_type = iterator_input_adapter_factory<IteratorType>;\n    return factory_type::create(first, last);\n}\n\n// Convenience shorthand from container to iterator\n// Enables ADL on begin(container) and end(container)\n// Encloses the using declarations in namespace for not to leak them to outside scope\n\nnamespace container_input_adapter_factory_impl\n{\n\nusing std::begin;\nusing std::end;\n\ntemplate<typename ContainerType, typename Enable = void>\nstruct container_input_adapter_factory {};\n\ntemplate<typename ContainerType>\nstruct container_input_adapter_factory< ContainerType,\n       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>\n       {\n           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));\n\n           static adapter_type create(const ContainerType& container)\n{\n    return input_adapter(begin(container), end(container));\n}\n       };\n\n} // namespace container_input_adapter_factory_impl\n\ntemplate<typename ContainerType>\ntypename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)\n{\n    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);\n}\n\n#ifndef JSON_NO_IO\n// Special cases with fast paths\ninline file_input_adapter input_adapter(std::FILE* file)\n{\n    return file_input_adapter(file);\n}\n\ninline input_stream_adapter input_adapter(std::istream& stream)\n{\n    return input_stream_adapter(stream);\n}\n\ninline input_stream_adapter input_adapter(std::istream&& stream)\n{\n    return input_stream_adapter(stream);\n}\n#endif  // JSON_NO_IO\n\nusing contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));\n\n// Null-delimited strings, and the like.\ntemplate < typename CharT,\n           typename std::enable_if <\n               std::is_pointer<CharT>::value&&\n               !std::is_array<CharT>::value&&\n               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n               sizeof(typename std::remove_pointer<CharT>::type) == 1,\n               int >::type = 0 >\ncontiguous_bytes_input_adapter input_adapter(CharT b)\n{\n    auto length = std::strlen(reinterpret_cast<const char*>(b));\n    const auto* ptr = reinterpret_cast<const char*>(b);\n    return input_adapter(ptr, ptr + length);\n}\n\ntemplate<typename T, std::size_t N>\nauto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    return input_adapter(array, array + N);\n}\n\n// This class only handles inputs of input_buffer_adapter type.\n// It's required so that expressions like {ptr, len} can be implicitely casted\n// to the correct adapter.\nclass span_input_adapter\n{\n  public:\n    template < typename CharT,\n               typename std::enable_if <\n                   std::is_pointer<CharT>::value&&\n                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n                   sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                   int >::type = 0 >\n    span_input_adapter(CharT b, std::size_t l)\n        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}\n\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    span_input_adapter(IteratorType first, IteratorType last)\n        : ia(input_adapter(first, last)) {}\n\n    contiguous_bytes_input_adapter&& get()\n    {\n        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)\n    }\n\n  private:\n    contiguous_bytes_input_adapter ia;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n\n#include <cstddef>\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief an floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief a binary string was read\n    @param[in] val  binary value\n    @return whether parsing should proceed\n    @note It is safe to move the passed binary.\n    */\n    virtual bool binary(binary_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    json_sax() = default;\n    json_sax(const json_sax&) = default;\n    json_sax(json_sax&&) noexcept = default;\n    json_sax& operator=(const json_sax&) = default;\n    json_sax& operator=(json_sax&&) noexcept = default;\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @param[in,out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_object());\n        JSON_ASSERT(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep && ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back())\n        {\n            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n            {\n                // discard object\n                *ref_stack.back() = discarded;\n            }\n            else\n            {\n                ref_stack.back()->set_parents();\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len), *ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (keep)\n            {\n                ref_stack.back()->set_parents();\n            }\n            else\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        JSON_ASSERT(!keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (!keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (!keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (!ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n\n        // object\n        JSON_ASSERT(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        JSON_ASSERT(!key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (!store_element)\n        {\n            return {false, nullptr};\n        }\n\n        JSON_ASSERT(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {};\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool binary(binary_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n}  // namespace detail\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////\n// lexer //\n///////////\n\ntemplate<typename BasicJsonType>\nclass lexer_base\n{\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case token_type::value_unsigned:\n            case token_type::value_integer:\n            case token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n};\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass lexer : public lexer_base<BasicJsonType>\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    using token_type = typename lexer_base<BasicJsonType>::token_type;\n\n    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept\n        : ia(std::move(adapter))\n        , ignore_comments(ignore_comments_)\n        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))\n    {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto* loc = localeconv();\n        JSON_ASSERT(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        JSON_ASSERT(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' && current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' && current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' && current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)\n    {\n        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 8259. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        JSON_ASSERT(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char_type>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' && get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                                        // in the result so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(static_cast<char_int_type>(codepoint));\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    /*!\n     * @brief scan a comment\n     * @return whether comment could be scanned successfully\n     */\n    bool scan_comment()\n    {\n        switch (get())\n        {\n            // single-line comments skip input until a newline or EOF is read\n            case '/':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case '\\n':\n                        case '\\r':\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                            return true;\n\n                        default:\n                            break;\n                    }\n                }\n            }\n\n            // multi-line comments skip input until */ is read\n            case '*':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                        {\n                            error_message = \"invalid comment; missing closing '*/'\";\n                            return false;\n                        }\n\n                        case '*':\n                        {\n                            switch (get())\n                            {\n                                case '/':\n                                    return true;\n\n                                default:\n                                {\n                                    unget();\n                                    continue;\n                                }\n                            }\n                        }\n\n                        default:\n                            continue;\n                    }\n                }\n            }\n\n            // unexpected character after reading '/'\n            default:\n            {\n                error_message = \"invalid comment; expecting '/' or '*' after '/'\";\n                return false;\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 8259.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 8259. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char_type* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia.get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            JSON_ASSERT(!token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(char_int_type c)\n    {\n        token_buffer.push_back(static_cast<typename string_t::value_type>(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if (static_cast<unsigned char>(c) <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                (std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(static_cast<std::string::value_type>(c));\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB && get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    void skip_whitespace()\n    {\n        do\n        {\n            get();\n        }\n        while (current == ' ' || current == '\\t' || current == '\\n' || current == '\\r');\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 && !skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        skip_whitespace();\n\n        // ignore comments\n        while (ignore_comments && current == '/')\n        {\n            if (!scan_comment())\n            {\n                return token_type::parse_error;\n            }\n\n            // skip following whitespace\n            skip_whitespace();\n        }\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n            {\n                std::array<char_type, 4> true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}};\n                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);\n            }\n            case 'f':\n            {\n                std::array<char_type, 5> false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}};\n                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);\n            }\n            case 'n':\n            {\n                std::array<char_type, 4> null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}};\n                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);\n            }\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char_type>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// whether comments should be ignored (true) or signaled as errors (false)\n    const bool ignore_comments = false;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char_type> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char_int_type decimal_point_char = '.';\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n\n#include <cstdint> // size_t\n#include <utility> // declval\n#include <string> // string\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate<typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate<typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate<typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate<typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate<typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate<typename T, typename Binary>\nusing binary_function_t =\n    decltype(std::declval<T&>().binary(std::declval<Binary&>()));\n\ntemplate<typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate<typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate<typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate<typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate<typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate<typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,\n        \"Missing/invalid function: bool binary(binary_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/// how to treat CBOR tags\nenum class cbor_tag_handler_t\n{\n    error,   ///< throw a parse_error exception in case of a tag\n    ignore,  ///< ignore tags\n    store    ///< store tags as binary type\n};\n\n/*!\n@brief determine system byte order\n\n@return true if and only if system's byte order is little endian\n\n@note from https://stackoverflow.com/a/1001328/266378\n*/\nstatic inline bool little_endianess(int num = 1) noexcept\n{\n    return *reinterpret_cast<char*>(&num) == 1;\n}\n\n\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using json_sax_t = SAX;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter))\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n    @param[in] tag_handler  how to treat CBOR tags\n\n    @return whether parsing was successful\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true,\n                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal(true, tag_handler);\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n                result = parse_ubjson_internal();\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result && strict)\n        {\n            if (format == input_format_t::ubjson)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(),\n                                        parse_error::create(110, chars_read, exception_message(format, \"expected end of input; last byte: 0x\" + get_token_string(), \"value\"), BasicJsonType()));\n            }\n        }\n\n        return result;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<typename string_t::value_type>(current);\n        }\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"string length must be at least 1, is \" + std::to_string(len), \"string\"), BasicJsonType()));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();\n    }\n\n    /*!\n    @brief Parses a byte array input of length @a len from the BSON input.\n    @param[in] len  The length of the byte array to be read.\n    @param[in,out] result  A reference to the binary variable where the read\n                            array is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 0\n    @return `true` if the byte array was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_binary(const NumberType len, binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 0))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"byte array length cannot be negative, is \" + std::to_string(len), \"binary\"), BasicJsonType()));\n        }\n\n        // All BSON binary values have a subtype\n        std::uint8_t subtype{};\n        get_number<std::uint8_t>(input_format_t::bson, subtype);\n        result.set_subtype(subtype);\n\n        return get_binary(input_format_t::bson, len, result);\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const char_int_type element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number{};\n                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len{};\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x05: // binary\n            {\n                std::int32_t len{};\n                binary_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value{};\n                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value{};\n                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, \"Unsupported BSON record type 0x\" + std::string(cr.data()), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n\n        while (auto element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (!is_array && !sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true) or whether the last read character should\n                         be considered instead (false)\n    @param[in] tag_handler how CBOR tags should be treated\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char,\n                             const cbor_tag_handler_t tag_handler)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            case 0x5F: // Binary data (indefinite length)\n            {\n                binary_t b;\n                return get_cbor_binary(b) && sax->binary(b);\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) && sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(std::size_t(-1), tag_handler);\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(std::size_t(-1), tag_handler);\n\n            case 0xC6: // tagged item\n            case 0xC7:\n            case 0xC8:\n            case 0xC9:\n            case 0xCA:\n            case 0xCB:\n            case 0xCC:\n            case 0xCD:\n            case 0xCE:\n            case 0xCF:\n            case 0xD0:\n            case 0xD1:\n            case 0xD2:\n            case 0xD3:\n            case 0xD4:\n            case 0xD8: // tagged item (1 bytes follow)\n            case 0xD9: // tagged item (2 bytes follow)\n            case 0xDA: // tagged item (4 bytes follow)\n            case 0xDB: // tagged item (8 bytes follow)\n            {\n                switch (tag_handler)\n                {\n                    case cbor_tag_handler_t::error:\n                    {\n                        auto last_token = get_token_string();\n                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n                    }\n\n                    case cbor_tag_handler_t::ignore:\n                    {\n                        // ignore binary subtype\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        return parse_cbor_internal(true, tag_handler);\n                    }\n\n                    case cbor_tag_handler_t::store:\n                    {\n                        binary_t b;\n                        // use binary subtype and store in binary container\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            default:\n                                return parse_cbor_internal(true, tag_handler);\n                        }\n                        get();\n                        return get_cbor_binary(b) && sax->binary(b);\n                    }\n\n                    default:                 // LCOV_EXCL_LINE\n                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                        return false;        // LCOV_EXCL_LINE\n                }\n            }\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (!get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\" + last_token, \"string\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into the byte array.\n    Additionally, CBOR's byte arrays with indefinite lengths are supported.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_cbor_binary(binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"binary\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            {\n                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5F: // Binary data (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    binary_t chunk;\n                    if (!get_cbor_binary(chunk))\n                    {\n                        return false;\n                    }\n                    result.insert(result.end(), chunk.begin(), chunk.end());\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x\" + last_token, \"binary\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or std::size_t(-1) for an\n                    array of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len,\n                        const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or std::size_t(-1) for an\n                    object of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len,\n                         const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        if (len != 0)\n        {\n            string_t key;\n            if (len != std::size_t(-1))\n            {\n                for (std::size_t i = 0; i < len; ++i)\n                {\n                    get();\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                while (get() != 0xFF)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) && sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xC4: // bin 8\n            case 0xC5: // bin 16\n            case 0xC6: // bin 32\n            case 0xC7: // ext 8\n            case 0xC8: // ext 16\n            case 0xC9: // ext 32\n            case 0xD4: // fixext 1\n            case 0xD5: // fixext 2\n            case 0xD6: // fixext 4\n            case 0xD7: // fixext 8\n            case 0xD8: // fixext 16\n            {\n                binary_t b;\n                return get_msgpack_binary(b) && sax->binary(b);\n            }\n\n            case 0xCA: // float 32\n            {\n                float number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, \"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\" + last_token, \"string\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into a byte array.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_msgpack_binary(binary_t& result)\n    {\n        // helper function to set the subtype\n        auto assign_and_return_true = [&result](std::int8_t subtype)\n        {\n            result.set_subtype(static_cast<std::uint8_t>(subtype));\n            return true;\n        };\n\n        switch (current)\n        {\n            case 0xC4: // bin 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC5: // bin 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC6: // bin 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC7: // ext 8\n            {\n                std::uint8_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC8: // ext 16\n            {\n                std::uint16_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC9: // ext 32\n            {\n                std::uint32_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD4: // fixext 1\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 1, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD5: // fixext 2\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 2, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD6: // fixext 4\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 4, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD7: // fixext 8\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 8, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD8: // fixext 16\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 16, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            default:           // LCOV_EXCL_LINE\n                return false;  // LCOV_EXCL_LINE\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            default:\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token, \"string\"), BasicJsonType()));\n        }\n    }\n\n    /*!\n    @param[out] result  determined size\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result)\n    {\n        switch (get_ignore_noop())\n        {\n            case 'U':\n            {\n                std::uint8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token, \"size\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result)\n    {\n        result.first = string_t::npos; // size\n        result.second = 0; // type\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"expected '#' after type information; last byte: 0x\" + last_token, \"size\"), BasicJsonType()));\n            }\n\n            return get_ubjson_size_value(result.first);\n        }\n\n        if (current == '#')\n        {\n            return get_ubjson_size_value(result.first);\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const char_int_type prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char_type>::eof():  // EOF\n                return unexpect_eof(input_format_t::ubjson, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'd':\n            {\n                float number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'H':\n            {\n                return get_ubjson_high_precision_number();\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\" + last_token, \"char\"), BasicJsonType()));\n                }\n                string_t s(1, static_cast<typename string_t::value_type>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) && sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"invalid byte: 0x\" + last_token, \"value\"), BasicJsonType()));\n            }\n        }\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    // Note, no reader for UBJSON binary types is implemented because they do\n    // not exist\n\n    bool get_ubjson_high_precision_number()\n    {\n        // get size of following number string\n        std::size_t size{};\n        auto res = get_ubjson_size_value(size);\n        if (JSON_HEDLEY_UNLIKELY(!res))\n        {\n            return res;\n        }\n\n        // get number string\n        std::vector<char> number_vector;\n        for (std::size_t i = 0; i < size; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"number\")))\n            {\n                return false;\n            }\n            number_vector.push_back(static_cast<char>(current));\n        }\n\n        // parse number string\n        using ia_type = decltype(detail::input_adapter(number_vector));\n        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);\n        const auto result_number = number_lexer.scan();\n        const auto number_string = number_lexer.get_token_string();\n        const auto result_remainder = number_lexer.scan();\n\n        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;\n\n        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))\n        {\n            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\"), BasicJsonType()));\n        }\n\n        switch (result_number)\n        {\n            case token_type::value_integer:\n                return sax->number_integer(number_lexer.get_number_integer());\n            case token_type::value_unsigned:\n                return sax->number_unsigned(number_lexer.get_number_unsigned());\n            case token_type::value_float:\n                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));\n            case token_type::uninitialized:\n            case token_type::literal_true:\n            case token_type::literal_false:\n            case token_type::literal_null:\n            case token_type::value_string:\n            case token_type::begin_array:\n            case token_type::begin_object:\n            case token_type::end_array:\n            case token_type::end_object:\n            case token_type::name_separator:\n            case token_type::value_separator:\n            case token_type::parse_error:\n            case token_type::end_of_input:\n            case token_type::literal_or_value:\n            default:\n                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\"), BasicJsonType()));\n        }\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char_type>::eof()` in that case.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++chars_read;\n        return current = ia.get_character();\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    char_int_type get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianess, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<std::uint8_t, sizeof(NumberType)> vec{};\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian != InputIsLittleEndian)\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"string\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<typename string_t::value_type>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @brief create a byte array by reading bytes from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of bytes to read\n    @param[out] result byte array created by reading @a len bytes\n\n    @return whether byte array creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of memory.\n    */\n    template<typename NumberType>\n    bool get_binary(const input_format_t format,\n                    const NumberType len,\n                    binary_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"binary\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<std::uint8_t>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context), BasicJsonType()));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        return error_msg + \" \" + context + \": \" + detail;\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/input/parser.hpp>\n\n\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\nenum class parse_event_t : std::uint8_t\n{\n    /// the parser read `{` and started to process a JSON object\n    object_start,\n    /// the parser read `}` and finished processing a JSON object\n    object_end,\n    /// the parser read `[` and started to process a JSON array\n    array_start,\n    /// the parser read `]` and finished processing a JSON array\n    array_end,\n    /// the parser read a key of a value in an object\n    key,\n    /// the parser finished reading a JSON value\n    value\n};\n\ntemplate<typename BasicJsonType>\nusing parser_callback_t =\n    std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive descent parser.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    /// a parser reading from an input adapter\n    explicit parser(InputAdapterType&& adapter,\n                    const parser_callback_t<BasicJsonType> cb = nullptr,\n                    const bool allow_exceptions_ = true,\n                    const bool skip_comments = false)\n        : callback(cb)\n        , m_lexer(std::move(adapter), skip_comments)\n        , allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\"), BasicJsonType()));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, \"value\"), BasicJsonType()));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n\n        result.assert_invariant();\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result && strict && (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, \"value\"), BasicJsonType()));\n        }\n\n        return result;\n    }\n\n  private:\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (!skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, \"object key\"), BasicJsonType()));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, \"object separator\"), BasicJsonType()));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, \"number overflow parsing '\" + m_lexer.get_token_string() + \"'\", BasicJsonType()));\n                        }\n\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        break;\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, \"value\"), BasicJsonType()));\n                    }\n\n                    case token_type::uninitialized:\n                    case token_type::end_array:\n                    case token_type::end_object:\n                    case token_type::name_separator:\n                    case token_type::value_separator:\n                    case token_type::end_of_input:\n                    case token_type::literal_or_value:\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, \"value\"), BasicJsonType()));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n\n            if (states.back())  // array\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse a new value\n                    get_token();\n                    continue;\n                }\n\n                // closing ]\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this array. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, \"array\"), BasicJsonType()));\n            }\n\n            // states.back() is false -> object\n\n            // comma -> next value\n            if (get_token() == token_type::value_separator)\n            {\n                // parse key\n                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))\n                {\n                    return sax->parse_error(m_lexer.get_position(),\n                                            m_lexer.get_token_string(),\n                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, \"object key\"), BasicJsonType()));\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                {\n                    return false;\n                }\n\n                // parse separator (:)\n                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                {\n                    return sax->parse_error(m_lexer.get_position(),\n                                            m_lexer.get_token_string(),\n                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, \"object separator\"), BasicJsonType()));\n                }\n\n                // parse values\n                get_token();\n                continue;\n            }\n\n            // closing }\n            if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                {\n                    return false;\n                }\n\n                // We are done with this object. Before we can parse a\n                // new value, we need to evaluate the new state first.\n                // By setting skip_to_state_evaluation to false, we\n                // are effectively jumping to the beginning of this if.\n                JSON_ASSERT(!states.empty());\n                states.pop_back();\n                skip_to_state_evaluation = true;\n                continue;\n            }\n\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, \"object\"), BasicJsonType()));\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return last_token = m_lexer.scan();\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (!context.empty())\n        {\n            error_msg += \"while parsing \" + context + \" \";\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += std::string(m_lexer.get_error_message()) + \"; last read: '\" +\n                         m_lexer.get_token_string() + \"'\";\n        }\n        else\n        {\n            error_msg += \"unexpected \" + std::string(lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += \"; expected \" + std::string(lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t<BasicJsonType> callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iter_impl.hpp>\n\n\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n    /// the iterator with BasicJsonType of different const-ness\n    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    /// allow basic_json to access private members\n    friend other_iter_impl;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n\n  public:\n\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    iter_impl() = default;\n    ~iter_impl() = default;\n    iter_impl(iter_impl&&) noexcept = default;\n    iter_impl& operator=(iter_impl&&) noexcept = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        if (&other != this)\n        {\n            m_object = other.m_object;\n            m_it = other.m_it;\n        }\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator++(int) // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator--(int) // NOLINT(readability-const-return-type)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator==(const IterImpl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", *m_object));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator!=(const IterImpl& other) const\n    {\n        return !operator==(other);\n    }\n\n    /*!\n    @brief comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", *m_object));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\", *m_object));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return !other.operator < (*this);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return !operator<=(other);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return !operator<(other);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", *m_object));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", *m_object));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\", *m_object));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", *m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\", *m_object));\n    }\n\n    /*!\n    @brief return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/json_pointer.hpp>\n\n\n#include <algorithm> // all_of\n#include <cctype> // isdigit\n#include <limits> // max\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\ntemplate<typename BasicJsonType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n  public:\n    /*!\n    @brief create JSON pointer\n\n    Create a JSON pointer according to the syntax described in\n    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).\n\n    @param[in] s  string representing the JSON pointer; if omitted, the empty\n                  string is assumed which references the whole JSON value\n\n    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does\n                           not begin with a slash (`/`); see example below\n\n    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is\n    not followed by `0` (representing `~`) or `1` (representing `/`); see\n    example below\n\n    @liveexample{The example shows the construction several valid JSON pointers\n    as well as the exceptional behavior.,json_pointer}\n\n    @since version 2.0.0\n    */\n    explicit json_pointer(const std::string& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /*!\n    @brief return a string representation of the JSON pointer\n\n    @invariant For each JSON pointer `ptr`, it holds:\n    @code {.cpp}\n    ptr == json_pointer(ptr.to_string());\n    @endcode\n\n    @return a string representation of the JSON pointer\n\n    @liveexample{The example shows the result of `to_string`.,json_pointer__to_string}\n\n    @since version 2.0.0\n    */\n    std::string to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               std::string{},\n                               [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + detail::escape(b);\n        });\n    }\n\n    /// @copydoc to_string()\n    operator std::string() const\n    {\n        return to_string();\n    }\n\n    /*!\n    @brief append another JSON pointer at the end of this JSON pointer\n\n    @param[in] ptr  JSON pointer to append\n    @return JSON pointer with @a ptr appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa see @ref operator/=(std::string) to append a reference token\n    @sa see @ref operator/=(std::size_t) to append an array index\n    @sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /*!\n    @brief append an unescaped reference token at the end of this JSON pointer\n\n    @param[in] token  reference token to append\n    @return JSON pointer with @a token appended without escaping @a token\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa see @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa see @ref operator/=(std::size_t) to append an array index\n    @sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::string token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /*!\n    @brief append an array index at the end of this JSON pointer\n\n    @param[in] array_idx  array index to append\n    @return JSON pointer with @a array_idx appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa see @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa see @ref operator/=(std::string) to append a reference token\n    @sa see @ref operator/(const json_pointer&, std::string) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::size_t array_idx)\n    {\n        return *this /= std::to_string(array_idx);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n\n    @param[in] lhs  JSON pointer\n    @param[in] rhs  JSON pointer\n    @return a new JSON pointer with @a rhs appended to @a lhs\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a lhs and @a rhs.\n\n    @sa see @ref operator/=(const json_pointer&) to append a JSON pointer\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] token  reference token\n    @return a new JSON pointer with unescaped @a token appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa see @ref operator/=(std::string) to append a reference token\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param)\n    {\n        return json_pointer(ptr) /= std::move(token);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] array_idx  array index\n    @return a new JSON pointer with @a array_idx appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa see @ref operator/=(std::size_t) to append an array index\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)\n    {\n        return json_pointer(ptr) /= array_idx;\n    }\n\n    /*!\n    @brief returns the parent of this JSON pointer\n\n    @return parent of this JSON pointer; in case this JSON pointer is the root,\n            the root itself is returned\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @liveexample{The example shows the result of `parent_pointer` for different\n    JSON Pointers.,json_pointer__parent_pointer}\n\n    @since version 3.6.0\n    */\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /*!\n    @brief remove last reference token\n\n    @pre not `empty()`\n\n    @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", BasicJsonType()));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /*!\n    @brief return last reference token\n\n    @pre not `empty()`\n    @return last reference token\n\n    @liveexample{The example shows the usage of `back`.,json_pointer__back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    const std::string& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", BasicJsonType()));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /*!\n    @brief append an unescaped token at the end of the reference pointer\n\n    @param[in] token  token to add\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows the result of `push_back` for different\n    JSON Pointers.,json_pointer__push_back}\n\n    @since version 3.6.0\n    */\n    void push_back(const std::string& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @copydoc push_back(const std::string&)\n    void push_back(std::string&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /*!\n    @brief return whether pointer points to the root document\n\n    @return true iff the JSON pointer points to the root document\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example shows the result of `empty` for different JSON\n    Pointers.,json_pointer__empty}\n\n    @since version 3.6.0\n    */\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw parse_error.106  if an array index begins with '0'\n    @throw parse_error.109  if an array index begins not with a digit\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    @throw out_of_range.410 if an array index exceeds size_type\n    */\n    static typename BasicJsonType::size_type array_index(const std::string& s)\n    {\n        using size_type = typename BasicJsonType::size_type;\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))\n        {\n            JSON_THROW(detail::parse_error::create(106, 0, \"array index '\" + s + \"' must not begin with '0'\", BasicJsonType()));\n        }\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))\n        {\n            JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + s + \"' is not a number\", BasicJsonType()));\n        }\n\n        std::size_t processed_chars = 0;\n        unsigned long long res = 0;  // NOLINT(runtime/int)\n        JSON_TRY\n        {\n            res = std::stoull(s, &processed_chars);\n        }\n        JSON_CATCH(std::out_of_range&)\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\", BasicJsonType()));\n        }\n\n        // check if the string was completely read\n        if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\", BasicJsonType()));\n        }\n\n        // only triggered on special platforms (like 32bit), see also\n        // https://github.com/nlohmann/json/pull/2203\n        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)\n        {\n            JSON_THROW(detail::out_of_range::create(410, \"array index \" + s + \" exceeds size_type\", BasicJsonType())); // LCOV_EXCL_LINE\n        }\n\n        return static_cast<size_type>(res);\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", BasicJsonType()));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n  private:\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        auto* result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    result = &result->operator[](array_index(reference_token));\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\", j));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums || reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        ptr = &ptr->operator[](array_index(reference_token));\n                    }\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\", *ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402, \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) + \") is out of range\", *ptr));\n                    }\n\n                    // use unchecked array access\n                    ptr = &ptr->operator[](array_index(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\", *ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\", *ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    bool contains(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (!ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !(\"0\" <= reference_token && reference_token <= \"9\")))\n                    {\n                        // invalid char\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))\n                        {\n                            // first char should be between '1' and '9'\n                            return false;\n                        }\n                        for (std::size_t i = 1; i < reference_token.size(); i++)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))\n                            {\n                                // other char should be between '0' and '9'\n                                return false;\n                            }\n                        }\n                    }\n\n                    const auto idx = array_index(reference_token);\n                    if (idx >= ptr->size())\n                    {\n                        // index out of range\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](idx);\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<std::string> split(const std::string& reference_string)\n    {\n        std::vector<std::string> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1, \"JSON pointer must be empty or begin with '/' - was: '\" + reference_string + \"'\", BasicJsonType()));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == std::string::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == std::string::npos)\n            start = (slash == std::string::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != std::string::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                JSON_ASSERT(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||\n                                         (reference_token[pos + 1] != '0' &&\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\", BasicJsonType()));\n                }\n            }\n\n            // finally, store the reference token\n            detail::unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n  private:\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    static void flatten(const std::string& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(reference_string + \"/\" + std::to_string(i),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(reference_string + \"/\" + detail::escape(element.first), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::null:\n            case detail::value_t::string:\n            case detail::value_t::boolean:\n            case detail::value_t::number_integer:\n            case detail::value_t::number_unsigned:\n            case detail::value_t::number_float:\n            case detail::value_t::binary:\n            case detail::value_t::discarded:\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\", value));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\", element.second));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief compares two JSON pointers for equality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is equal to @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator==(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return lhs.reference_tokens == rhs.reference_tokens;\n    }\n\n    /*!\n    @brief compares two JSON pointers for inequality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is not equal @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator!=(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /// the reference tokens\n    std::vector<std::string> reference_tokens;\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/json_ref.hpp>\n\n\n#include <initializer_list>\n#include <utility>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value))\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(&value)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...)\n    {}\n\n    // class should be movable only\n    json_ref(json_ref&&) noexcept = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (value_ref == nullptr)\n        {\n            return std::move(owned_value);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return value_ref ? *value_ref : owned_value;\n    }\n\n    value_type const* operator->() const\n    {\n        return &** this;\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type const* value_ref = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <cmath> // isnan, isinf\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n#include <string> // string\n#include <utility> // move\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <string> // basic_string\n#include <vector> // vector\n\n#ifndef JSON_NO_IO\n    #include <ios>      // streamsize\n    #include <ostream>  // basic_ostream\n#endif  // JSON_NO_IO\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n\n    output_adapter_protocol() = default;\n    output_adapter_protocol(const output_adapter_protocol&) = default;\n    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;\n    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;\n    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType, typename AllocatorType = std::allocator<CharType>>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        std::copy(s, s + length, std::back_inserter(v));\n    }\n\n  private:\n    std::vector<CharType, AllocatorType>& v;\n};\n\n#ifndef JSON_NO_IO\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n#endif  // JSON_NO_IO\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    template<typename AllocatorType = std::allocator<CharType>>\n    output_adapter(std::vector<CharType, AllocatorType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}\n\n#ifndef JSON_NO_IO\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n#endif  // JSON_NO_IO\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))\n    {\n        JSON_ASSERT(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::array:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                JSON_THROW(type_error::create(317, \"to serialize to BSON, top-level type must be object, but is \" + std::string(j.type_name()), j));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<std::uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<std::uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<std::uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<std::uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                if (std::isnan(j.m_value.number_float))\n                {\n                    // NaN is 0xf97e00 in CBOR\n                    oa->write_character(to_char_type(0xF9));\n                    oa->write_character(to_char_type(0x7E));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else if (std::isinf(j.m_value.number_float))\n                {\n                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00\n                    oa->write_character(to_char_type(0xf9));\n                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else\n                {\n                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);\n                }\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (j.m_value.binary->has_subtype())\n                {\n                    if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xd8));\n                        write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xd9));\n                        write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xda));\n                        write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xdb));\n                        write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));\n                    }\n                }\n\n                // step 1: write control byte and the binary array size\n                const auto N = j.m_value.binary->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x40 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x58));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x59));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<std::uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<std::uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                // step 0: determine if the binary type has a set subtype to\n                // determine whether or not to use the ext or fixext types\n                const bool use_ext = j.m_value.binary->has_subtype();\n\n                // step 1: write control byte and the byte string length\n                const auto N = j.m_value.binary->size();\n                if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    std::uint8_t output_type{};\n                    bool fixed = true;\n                    if (use_ext)\n                    {\n                        switch (N)\n                        {\n                            case 1:\n                                output_type = 0xD4; // fixext 1\n                                break;\n                            case 2:\n                                output_type = 0xD5; // fixext 2\n                                break;\n                            case 4:\n                                output_type = 0xD6; // fixext 4\n                                break;\n                            case 8:\n                                output_type = 0xD7; // fixext 8\n                                break;\n                            case 16:\n                                output_type = 0xD8; // fixext 16\n                                break;\n                            default:\n                                output_type = 0xC7; // ext 8\n                                fixed = false;\n                                break;\n                        }\n\n                    }\n                    else\n                    {\n                        output_type = 0xC4; // bin 8\n                        fixed = false;\n                    }\n\n                    oa->write_character(to_char_type(output_type));\n                    if (!fixed)\n                    {\n                        write_number(static_cast<std::uint8_t>(N));\n                    }\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC8 // ext 16\n                                               : 0xC5; // bin 16\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC9 // ext 32\n                                               : 0xC6; // bin 32\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 1.5: if this is an ext type, write the subtype\n                if (use_ext)\n                {\n                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));\n                }\n\n                // step 2: write the byte string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.array->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                if (use_type && !j.m_value.binary->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    oa->write_character(to_char_type('$'));\n                    oa->write_character('U');\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true);\n                }\n\n                if (use_type)\n                {\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                        j.m_value.binary->size());\n                }\n                else\n                {\n                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)\n                    {\n                        oa->write_character(to_char_type('U'));\n                        oa->write_character(j.m_value.binary->data()[i]);\n                    }\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.object->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409, \"BSON key cannot contain code point U+0000 (at byte \" + std::to_string(it) + \")\", j));\n            static_cast<void>(j);\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double, true>(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const BasicJsonType& j)\n    {\n        if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.number_unsigned));\n        }\n        else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_unsigned));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(j.m_value.number_unsigned) + \" cannot be represented by BSON as it does not fit int64\", j));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t array_index = 0ul;\n\n        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)\n        {\n            return result + calc_bson_element_size(std::to_string(array_index++), el);\n        });\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @return The size of the BSON-encoded binary array @a value\n    */\n    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and binary value @a value\n    */\n    void write_bson_binary(const string_t& name,\n                           const binary_t& value)\n    {\n        write_bson_entry_header(name, 0x05);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size()));\n        write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : std::uint8_t(0x00));\n\n        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name, j);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::binary:\n                return header_size + calc_bson_binary_size(*j.m_value.binary);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            case value_t::discarded:\n            default:\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)\n                return 0ul;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::binary:\n                return write_bson_binary(name, *j.m_value.binary);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            case value_t::discarded:\n            default:\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)\n                return;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] value  JSON value to serialize\n    @pre       value.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0),\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] value  JSON value to serialize\n    @pre       value.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= (std::numeric_limits<std::uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template < typename NumberType, typename std::enable_if <\n                   std::is_signed<NumberType>::value&&\n                   !std::is_floating_point<NumberType>::value, int >::type = 0 >\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::int8_t>(n));\n        }\n        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n    */\n    CharType ubjson_prefix(const BasicJsonType& j) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                {\n                    return 'l';\n                }\n                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n                {\n                    return 'I';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n                {\n                    return 'l';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array: // fallthrough\n            case value_t::binary:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            case value_t::discarded:\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @tparam NumberType the type of the number\n    @tparam OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n\n    @note This function needs to respect the system's endianess, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool OutputIsLittleEndian = false>\n    void write_number(const NumberType n)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec{};\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian != OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n    void write_compact_float(const number_float_t n, detail::input_format_t format)\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&\n                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&\n                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(static_cast<float>(n))\n                                : get_msgpack_float_prefix(static_cast<float>(n)));\n            write_number(static_cast<float>(n));\n        }\n        else\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(n)\n                                : get_msgpack_float_prefix(n));\n            write_number(n);\n        }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_trivial<CharType>::value, \"CharType must be trivial\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value &&\n                   std::is_signed<char>::value &&\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/output/serializer.hpp>\n\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string, char_traits\n#include <iomanip> // setfill, setw\n#include <sstream> // stringstream\n#include <type_traits> // is_same\n#include <utility> // move\n\n// #include <nlohmann/detail/conversions/to_chars.hpp>\n\n\n#include <array> // array\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate<typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        JSON_ASSERT(x.e == y.e);\n        JSON_ASSERT(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        JSON_ASSERT(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        JSON_ASSERT(delta >= 0);\n        JSON_ASSERT(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate<typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 && E > 1;\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    JSON_ASSERT(e >= -1500);\n    JSON_ASSERT(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    JSON_ASSERT(index >= 0);\n    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    JSON_ASSERT(kAlpha <= cached.e + e + 64);\n    JSON_ASSERT(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n\n    pow10 = 1;\n    return 1;\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    JSON_ASSERT(len >= 1);\n    JSON_ASSERT(dist <= delta);\n    JSON_ASSERT(rest <= delta);\n    JSON_ASSERT(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            && delta - rest >= ten_k\n            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))\n    {\n        JSON_ASSERT(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    JSON_ASSERT(M_plus.e >= kAlpha);\n    JSON_ASSERT(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    JSON_ASSERT(p1 > 0);\n\n    std::uint32_t pow10{};\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    JSON_ASSERT(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    JSON_ASSERT(m_plus.e == m_minus.e);\n    JSON_ASSERT(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    JSON_ASSERT(e > -1000);\n    JSON_ASSERT(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    JSON_ASSERT(min_exp < 0);\n    JSON_ASSERT(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n && n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (static_cast<size_t>(n) + 2);\n    }\n\n    if (0 < n && n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        JSON_ASSERT(k > n);\n\n        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));\n        buf[n] = '.';\n        return buf + (static_cast<size_t>(k) + 1U);\n    }\n\n    if (min_exp < n && n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);\n        buf[1] = '.';\n        buf += 1 + static_cast<size_t>(k);\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n} // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    JSON_ASSERT(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    JSON_ASSERT(last - first >= kMaxExp + 2);\n    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using binary_char_t = typename BasicJsonType::binary_t::value_type;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n    - binary values are serialized as objects containing the subtype and the\n      byte array\n\n    @param[in] val               value to serialize\n    @param[in] pretty_print      whether the output shall be pretty-printed\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] indent_step       the indent level\n    @param[in] current_indent    the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val,\n              const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::binary:\n            {\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"bytes\\\": [\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_characters(\", \", 2);\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\n\", 3);\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"subtype\\\": \", 11);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                    }\n                    else\n                    {\n                        o->write_characters(\"null\", 4);\n                    }\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_characters(\"{\\\"bytes\\\":[\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_character(',');\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\\"subtype\\\":\", 12);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                        o->write_character('}');\n                    }\n                    else\n                    {\n                        o->write_characters(\"null}\", 5);\n                    }\n                }\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint{};\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<std::uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    (std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(codepoint));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    (std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                    static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            std::stringstream ss;\n                            ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0);\n                            JSON_THROW(type_error::create(316, \"invalid UTF-8 byte at index \" + std::to_string(i) + \": 0x\" + ss.str(), BasicJsonType()));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (!ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    std::stringstream ss;\n                    ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast<std::uint8_t>(s.back()) | 0);\n                    JSON_THROW(type_error::create(316, \"incomplete UTF-8 string; last byte: 0x\" + ss.str(), BasicJsonType()));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        }\n    }\n\n  private:\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    inline unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template < typename NumberType, detail::enable_if_t <\n                   std::is_integral<NumberType>::value ||\n                   std::is_same<NumberType, number_unsigned_t>::value ||\n                   std::is_same<NumberType, number_integer_t>::value ||\n                   std::is_same<NumberType, binary_char_t>::value,\n                   int > = 0 >\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n\n        const bool is_negative = std::is_signed<NumberType>::value && !(x >= 0); // see issue #755\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars{};\n\n        if (is_negative)\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(static_cast<number_integer_t>(x));\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        JSON_ASSERT(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward\n        // so we later avoid reversing the result\n        buffer_ptr += n_chars;\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (!std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||\n              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        auto* begin = number_buffer.data();\n        auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        JSON_ASSERT(len > 0);\n        // check if buffer was large enough\n        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            JSON_ASSERT((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' && decimal_point != '.')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' || c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        JSON_ASSERT(byte < utf8d.size());\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);\n        JSON_ASSERT(index < 400);\n        state = utf8d[index];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as unsigned integer. The plus/minus shuffling is necessary as we can\n     * not directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    inline number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n\n// #include <nlohmann/ordered_map.hpp>\n\n\n#include <functional> // less\n#include <initializer_list> // initializer_list\n#include <iterator> // input_iterator_tag, iterator_traits\n#include <memory> // allocator\n#include <stdexcept> // for out_of_range\n#include <type_traits> // enable_if, is_convertible\n#include <utility> // pair\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n\n/// ordered_map: a minimal map-like container that preserves insertion order\n/// for use within nlohmann::basic_json<ordered_map>\ntemplate <class Key, class T, class IgnoredLess = std::less<Key>,\n          class Allocator = std::allocator<std::pair<const Key, T>>>\n                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>\n{\n    using key_type = Key;\n    using mapped_type = T;\n    using Container = std::vector<std::pair<const Key, T>, Allocator>;\n    using typename Container::iterator;\n    using typename Container::const_iterator;\n    using typename Container::size_type;\n    using typename Container::value_type;\n\n    // Explicit constructors instead of `using Container::Container`\n    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)\n    ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {}\n    template <class It>\n    ordered_map(It first, It last, const Allocator& alloc = Allocator())\n        : Container{first, last, alloc} {}\n    ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )\n        : Container{init, alloc} {}\n\n    std::pair<iterator, bool> emplace(const key_type& key, T&& t)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return {it, false};\n            }\n        }\n        Container::emplace_back(key, t);\n        return {--this->end(), true};\n    }\n\n    T& operator[](const Key& key)\n    {\n        return emplace(key, T{}).first->second;\n    }\n\n    const T& operator[](const Key& key) const\n    {\n        return at(key);\n    }\n\n    T& at(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    const T& at(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    size_type erase(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                // Since we cannot move const Keys, re-construct them in place\n                for (auto next = it; ++next != this->end(); ++it)\n                {\n                    it->~value_type(); // Destroy but keep allocation\n                    new (&*it) value_type{std::move(*next)};\n                }\n                Container::pop_back();\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator erase(iterator pos)\n    {\n        auto it = pos;\n\n        // Since we cannot move const Keys, re-construct them in place\n        for (auto next = it; ++next != this->end(); ++it)\n        {\n            it->~value_type(); // Destroy but keep allocation\n            new (&*it) value_type{std::move(*next)};\n        }\n        Container::pop_back();\n        return pos;\n    }\n\n    size_type count(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator find(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    const_iterator find(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    std::pair<iterator, bool> insert( value_type&& value )\n    {\n        return emplace(value.first, std::move(value.second));\n    }\n\n    std::pair<iterator, bool> insert( const value_type& value )\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == value.first)\n            {\n                return {it, false};\n            }\n        }\n        Container::push_back(value);\n        return {--this->end(), true};\n    }\n\n    template<typename InputIt>\n    using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,\n            std::input_iterator_tag>::value>::type;\n\n    template<typename InputIt, typename = require_input_iter<InputIt>>\n    void insert(InputIt first, InputIt last)\n    {\n        for (auto it = first; it != last; ++it)\n        {\n            insert(*it);\n        }\n    }\n};\n\n}  // namespace nlohmann\n\n\n#if defined(JSON_HAS_CPP_17)\n    #include <string_view>\n#endif\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n\n/*!\n@brief a class to store JSON values\n\n@tparam ObjectType type for JSON objects (`std::map` by default; will be used\nin @ref object_t)\n@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used\nin @ref array_t)\n@tparam StringType type for JSON strings and object keys (`std::string` by\ndefault; will be used in @ref string_t)\n@tparam BooleanType type for JSON booleans (`bool` by default; will be used\nin @ref boolean_t)\n@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by\ndefault; will be used in @ref number_integer_t)\n@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c\n`uint64_t` by default; will be used in @ref number_unsigned_t)\n@tparam NumberFloatType type for JSON floating-point numbers (`double` by\ndefault; will be used in @ref number_float_t)\n@tparam BinaryType type for packed binary data for compatibility with binary\nserialization formats (`std::vector<std::uint8_t>` by default; will be used in\n@ref binary_t)\n@tparam AllocatorType type of the allocator to use (`std::allocator` by\ndefault)\n@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`\nand `from_json()` (@ref adl_serializer by default)\n\n@requirement The class satisfies the following concept requirements:\n- Basic\n - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):\n   JSON values can be default constructed. The result will be a JSON null\n   value.\n - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):\n   A JSON value can be constructed from an rvalue argument.\n - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):\n   A JSON value can be copy-constructed from an lvalue expression.\n - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):\n   A JSON value van be assigned from an rvalue argument.\n - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):\n   A JSON value can be copy-assigned from an lvalue expression.\n - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):\n   JSON values can be destructed.\n- Layout\n - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):\n   JSON values have\n   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):\n   All non-static data members are private and standard layout types, the\n   class has no virtual functions or (virtual) base classes.\n- Library-wide\n - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):\n   JSON values can be compared with `==`, see @ref\n   operator==(const_reference,const_reference).\n - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):\n   JSON values can be compared with `<`, see @ref\n   operator<(const_reference,const_reference).\n - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):\n   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of\n   other compatible types, using unqualified function call @ref swap().\n - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):\n   JSON values can be compared against `std::nullptr_t` objects which are used\n   to model the `null` value.\n- Container\n - [Container](https://en.cppreference.com/w/cpp/named_req/Container):\n   JSON values can be used like STL containers and provide iterator access.\n - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);\n   JSON values can be used like STL containers and provide reverse iterator\n   access.\n\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@internal\n@note ObjectType trick from https://stackoverflow.com/a/9860911\n@endinternal\n\n@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange\nFormat](https://tools.ietf.org/html/rfc8259)\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n    friend ::nlohmann::json_pointer<basic_json>;\n\n    template<typename BasicJsonType, typename InputType>\n    friend class ::nlohmann::detail::parser;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename InputType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n    friend class ::nlohmann::detail::exception;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer_base<basic_json>;\n\n    template<typename InputAdapterType>\n    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(\n        InputAdapterType adapter,\n        detail::parser_callback_t<basic_json>cb = nullptr,\n        const bool allow_exceptions = true,\n        const bool ignore_comments = false\n                                 )\n    {\n        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),\n                std::move(cb), allow_exceptions, ignore_comments);\n    }\n\n  private:\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    template<typename InputType>\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<basic_json>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// how to treat CBOR tags\n    using cbor_tag_handler_t = detail::cbor_tag_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    /// @copydoc detail::exception\n    using exception = detail::exception;\n    /// @copydoc detail::parse_error\n    using parse_error = detail::parse_error;\n    /// @copydoc detail::invalid_iterator\n    using invalid_iterator = detail::invalid_iterator;\n    /// @copydoc detail::type_error\n    using type_error = detail::type_error;\n    /// @copydoc detail::out_of_range\n    using out_of_range = detail::out_of_range;\n    /// @copydoc detail::other_error\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /*!\n    @brief returns the allocator associated with the container\n    */\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /*!\n    @brief returns version information on the library\n\n    This function returns a JSON object with information about the library,\n    including the version number and information on the platform and compiler.\n\n    @return JSON object holding version information\n    key         | description\n    ----------- | ---------------\n    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).\n    `copyright` | The copyright line for the library as string.\n    `name`      | The name of the library as string.\n    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.\n    `url`       | The URL of the project as string.\n    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).\n\n    @liveexample{The following code shows an example output of the `meta()`\n    function.,meta}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @complexity Constant.\n\n    @since 2.1.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2021 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_PATCH);\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", std::to_string(__GNUC__) + \".\" + std::to_string(__GNUC_MINOR__) + \".\" + std::to_string(__GNUC_PATCHLEVEL__)}};\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#ifdef __cplusplus\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n#if defined(JSON_HAS_CPP_14)\n    // Use transparent comparator if possible, combined with perfect forwarding\n    // on find() and count() calls prevents unnecessary string construction.\n    using object_comparator_t = std::less<>;\n#else\n    using object_comparator_t = std::less<StringType>;\n#endif\n\n    /*!\n    @brief a type for an object\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows:\n    > An object is an unordered collection of zero or more name/value pairs,\n    > where a name is a string and a value is a string, number, boolean, null,\n    > object, or array.\n\n    To store objects in C++, a type is defined by the template parameters\n    described below.\n\n    @tparam ObjectType  the container to store objects (e.g., `std::map` or\n    `std::unordered_map`)\n    @tparam StringType the type of the keys or names (e.g., `std::string`).\n    The comparison function `std::less<StringType>` is used to order elements\n    inside the container.\n    @tparam AllocatorType the allocator to use for objects (e.g.,\n    `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ObjectType (`std::map`), @a StringType\n    (`std::string`), and @a AllocatorType (`std::allocator`), the default\n    value for @a object_t is:\n\n    @code {.cpp}\n    std::map<\n      std::string, // key_type\n      basic_json, // value_type\n      std::less<std::string>, // key_compare\n      std::allocator<std::pair<const std::string, basic_json>> // allocator_type\n    >\n    @endcode\n\n    #### Behavior\n\n    The choice of @a object_t influences the behavior of the JSON class. With\n    the default type, objects have the following behavior:\n\n    - When all names are unique, objects will be interoperable in the sense\n      that all software implementations receiving that object will agree on\n      the name-value mappings.\n    - When the names within an object are not unique, it is unspecified which\n      one of the values for a given key will be chosen. For instance,\n      `{\"key\": 2, \"key\": 1}` could be equal to either `{\"key\": 1}` or\n      `{\"key\": 2}`.\n    - Internally, name/value pairs are stored in lexicographical order of the\n      names. Objects will also be serialized (see @ref dump) in this order.\n      For instance, `{\"b\": 1, \"a\": 2}` and `{\"a\": 2, \"b\": 1}` will be stored\n      and serialized as `{\"a\": 2, \"b\": 1}`.\n    - When comparing objects, the order of the name/value pairs is irrelevant.\n      This makes objects interoperable in the sense that they will not be\n      affected by these differences. For instance, `{\"b\": 1, \"a\": 2}` and\n      `{\"a\": 2, \"b\": 1}` will be treated as equal.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the object's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON object.\n\n    #### Storage\n\n    Objects are stored as pointers in a @ref basic_json type. That is, for any\n    access to object values, a pointer of type `object_t*` must be\n    dereferenced.\n\n    @sa see @ref array_t -- type for an array value\n\n    @since version 1.0.0\n\n    @note The order name/value pairs are added to the object is *not*\n    preserved by the library. Therefore, iterating an object may return\n    name/value pairs in a different order than they were originally stored. In\n    fact, keys will be traversed in alphabetical order as `std::map` with\n    `std::less` is used by default. Please note this behavior conforms to [RFC\n    8259](https://tools.ietf.org/html/rfc8259), because any order implements the\n    specified \"unordered\" nature of JSON objects.\n    */\n    using object_t = ObjectType<StringType,\n          basic_json,\n          object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /*!\n    @brief a type for an array\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows:\n    > An array is an ordered sequence of zero or more values.\n\n    To store objects in C++, a type is defined by the template parameters\n    explained below.\n\n    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or\n    `std::list`)\n    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ArrayType (`std::vector`) and @a\n    AllocatorType (`std::allocator`), the default value for @a array_t is:\n\n    @code {.cpp}\n    std::vector<\n      basic_json, // value_type\n      std::allocator<basic_json> // allocator_type\n    >\n    @endcode\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the array's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON array.\n\n    #### Storage\n\n    Arrays are stored as pointers in a @ref basic_json type. That is, for any\n    access to array values, a pointer of type `array_t*` must be dereferenced.\n\n    @sa see @ref object_t -- type for an object value\n\n    @since version 1.0.0\n    */\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /*!\n    @brief a type for a string\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows:\n    > A string is a sequence of zero or more Unicode characters.\n\n    To store objects in C++, a type is defined by the template parameter\n    described below. Unicode values are split by the JSON class into\n    byte-sized characters during deserialization.\n\n    @tparam StringType  the container to store strings (e.g., `std::string`).\n    Note this container is used for keys/names in objects, see @ref object_t.\n\n    #### Default type\n\n    With the default values for @a StringType (`std::string`), the default\n    value for @a string_t is:\n\n    @code {.cpp}\n    std::string\n    @endcode\n\n    #### Encoding\n\n    Strings are stored in UTF-8 encoding. Therefore, functions like\n    `std::string::size()` or `std::string::length()` return the number of\n    bytes in the string rather than the number of characters or glyphs.\n\n    #### String comparison\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) states:\n    > Software implementations are typically required to test names of object\n    > members for equality. Implementations that transform the textual\n    > representation into sequences of Unicode code units and then perform the\n    > comparison numerically, code unit by code unit, are interoperable in the\n    > sense that implementations will agree in all cases on equality or\n    > inequality of two strings. For example, implementations that compare\n    > strings with escaped characters unconverted may incorrectly find that\n    > `\"a\\\\b\"` and `\"a\\u005Cb\"` are not equal.\n\n    This implementation is interoperable as it does compare strings code unit\n    by code unit.\n\n    #### Storage\n\n    String values are stored as pointers in a @ref basic_json type. That is,\n    for any access to string values, a pointer of type `string_t*` must be\n    dereferenced.\n\n    @since version 1.0.0\n    */\n    using string_t = StringType;\n\n    /*!\n    @brief a type for a boolean\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a\n    type which differentiates the two literals `true` and `false`.\n\n    To store objects in C++, a type is defined by the template parameter @a\n    BooleanType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a BooleanType (`bool`), the default value for\n    @a boolean_t is:\n\n    @code {.cpp}\n    bool\n    @endcode\n\n    #### Storage\n\n    Boolean values are stored directly inside a @ref basic_json type.\n\n    @since version 1.0.0\n    */\n    using boolean_t = BooleanType;\n\n    /*!\n    @brief a type for a number (integer)\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store integer numbers in C++, a type is defined by the template\n    parameter @a NumberIntegerType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberIntegerType (`int64_t`), the default\n    value for @a number_integer_t is:\n\n    @code {.cpp}\n    int64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number\n    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers\n    that are out of range will yield over/underflow when used in a\n    constructor. During deserialization, too large or small integer numbers\n    will be automatically be stored as @ref number_unsigned_t or @ref\n    number_float_t.\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange of the exactly supported range [INT64_MIN,\n    INT64_MAX], this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa see @ref number_float_t -- type for number values (floating-point)\n\n    @sa see @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_integer_t = NumberIntegerType;\n\n    /*!\n    @brief a type for a number (unsigned)\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store unsigned integer numbers in C++, a type is defined by the\n    template parameter @a NumberUnsignedType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberUnsignedType (`uint64_t`), the\n    default value for @a number_unsigned_t is:\n\n    @code {.cpp}\n    uint64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer\n    number that can be stored is `0`. Integer numbers that are out of range\n    will yield over/underflow when used in a constructor. During\n    deserialization, too large or small integer numbers will be automatically\n    be stored as @ref number_integer_t or @ref number_float_t.\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange (when considered in conjunction with the\n    number_integer_t type) of the exactly supported range [0, UINT64_MAX],\n    this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa see @ref number_float_t -- type for number values (floating-point)\n    @sa see @ref number_integer_t -- type for number values (integer)\n\n    @since version 2.0.0\n    */\n    using number_unsigned_t = NumberUnsignedType;\n\n    /*!\n    @brief a type for a number (floating-point)\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store floating-point numbers in C++, a type is defined by the template\n    parameter @a NumberFloatType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberFloatType (`double`), the default\n    value for @a number_float_t is:\n\n    @code {.cpp}\n    double\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in floating-point literals will be ignored. Internally,\n      the value will be stored as decimal number. For instance, the C++\n      floating-point literal `01.2` will be serialized to `1.2`. During\n      deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 8259](https://tools.ietf.org/html/rfc8259) states:\n    > This specification allows implementations to set limits on the range and\n    > precision of numbers accepted. Since software that implements IEEE\n    > 754-2008 binary64 (double precision) numbers is generally available and\n    > widely used, good interoperability can be achieved by implementations\n    > that expect no more precision or range than these provide, in the sense\n    > that implementations will approximate JSON numbers within the expected\n    > precision.\n\n    This implementation does exactly follow this approach, as it uses double\n    precision floating-point numbers. Note values smaller than\n    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`\n    will be stored as NaN internally and be serialized to `null`.\n\n    #### Storage\n\n    Floating-point number values are stored directly inside a @ref basic_json\n    type.\n\n    @sa see @ref number_integer_t -- type for number values (integer)\n\n    @sa see @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_float_t = NumberFloatType;\n\n    /*!\n    @brief a type for a packed binary type\n\n    This type is a type designed to carry binary data that appears in various\n    serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and\n    BSON's generic binary subtype. This type is NOT a part of standard JSON and\n    exists solely for compatibility with these binary types. As such, it is\n    simply defined as an ordered sequence of zero or more byte values.\n\n    Additionally, as an implementation detail, the subtype of the binary data is\n    carried around as a `std::uint8_t`, which is compatible with both of the\n    binary data formats that use binary subtyping, (though the specific\n    numbering is incompatible with each other, and it is up to the user to\n    translate between them).\n\n    [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type\n    as:\n    > Major type 2: a byte string. The string's length in bytes is represented\n    > following the rules for positive integers (major type 0).\n\n    [MessagePack's documentation on the bin type\n    family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family)\n    describes this type as:\n    > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes\n    > in addition to the size of the byte array.\n\n    [BSON's specifications](http://bsonspec.org/spec.html) describe several\n    binary types; however, this type is intended to represent the generic binary\n    type which has the description:\n    > Generic binary subtype - This is the most commonly used binary subtype and\n    > should be the 'default' for drivers and tools.\n\n    None of these impose any limitations on the internal representation other\n    than the basic unit of storage be some type of array whose parts are\n    decomposable into bytes.\n\n    The default representation of this binary format is a\n    `std::vector<std::uint8_t>`, which is a very common way to represent a byte\n    array in modern C++.\n\n    #### Default type\n\n    The default values for @a BinaryType is `std::vector<std::uint8_t>`\n\n    #### Storage\n\n    Binary Arrays are stored as pointers in a @ref basic_json type. That is,\n    for any access to array values, a pointer of the type `binary_t*` must be\n    dereferenced.\n\n    #### Notes on subtypes\n\n    - CBOR\n       - Binary values are represented as byte strings. Subtypes are serialized\n         as tagged values.\n    - MessagePack\n       - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,\n         or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)\n         is used. For other sizes, the ext family (ext8, ext16, ext32) is used.\n         The subtype is then added as singed 8-bit integer.\n       - If no subtype is given, the bin family (bin8, bin16, bin32) is used.\n    - BSON\n       - If a subtype is given, it is used and added as unsigned 8-bit integer.\n       - If no subtype is given, the generic binary subtype 0x00 is used.\n\n    @sa see @ref binary -- create a binary array\n\n    @since version 3.8.0\n    */\n    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * obj)\n        {\n            AllocatorTraits::deallocate(alloc, obj, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);\n        JSON_ASSERT(obj != nullptr);\n        return obj.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    binary    | binary          | pointer to @ref binary_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// binary (stored with pointer to save storage)\n        binary_t* binary;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    binary = create<binary_t>();\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = boolean_t(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = number_integer_t(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = number_unsigned_t(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = number_float_t(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                case value_t::discarded:\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4\", basic_json())); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value) : string(create<string_t>(value)) {}\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}\n\n        /// constructor for objects\n        json_value(const object_t& value) : object(create<object_t>(value)) {}\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}\n\n        /// constructor for arrays\n        json_value(const array_t& value) : array(create<array_t>(value)) {}\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}\n\n        /// constructor for binary arrays\n        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays\n        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        /// constructor for binary arrays (internal type)\n        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays (internal type)\n        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        void destroy(value_t t)\n        {\n            if (t == value_t::array || t == value_t::object)\n            {\n                // flatten the current json_value to a heap-allocated stack\n                std::vector<basic_json> stack;\n\n                // move the top-level items to stack\n                if (t == value_t::array)\n                {\n                    stack.reserve(array->size());\n                    std::move(array->begin(), array->end(), std::back_inserter(stack));\n                }\n                else\n                {\n                    stack.reserve(object->size());\n                    for (auto&& it : *object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n                }\n\n                while (!stack.empty())\n                {\n                    // move the last item to local variable to be processed\n                    basic_json current_item(std::move(stack.back()));\n                    stack.pop_back();\n\n                    // if current_item is array/object, move\n                    // its children to the stack to be processed later\n                    if (current_item.is_array())\n                    {\n                        std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));\n\n                        current_item.m_value.array->clear();\n                    }\n                    else if (current_item.is_object())\n                    {\n                        for (auto&& it : *current_item.m_value.object)\n                        {\n                            stack.push_back(std::move(it.second));\n                        }\n\n                        current_item.m_value.object->clear();\n                    }\n\n                    // it's now safe that current_item get destructed\n                    // since it doesn't have any children\n                }\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);\n                    break;\n                }\n\n                case value_t::null:\n                case value_t::boolean:\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                case value_t::discarded:\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n  private:\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n\n    Furthermore, the parent relation is checked for arrays and objects: If\n    @a check_parents true and the value is an array or object, then the\n    container's elements must have the current value as parent.\n\n    @param[in] check_parents  whether the parent relation should be checked.\n               The value is true by default and should only be set to false\n               during destruction of objects when the invariant does not\n               need to hold.\n    */\n    void assert_invariant(bool check_parents = true) const noexcept\n    {\n        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);\n        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);\n        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);\n        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);\n\n#if JSON_DIAGNOSTICS\n        JSON_TRY\n        {\n            // cppcheck-suppress assertWithSideEffect\n            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)\n            {\n                return j.m_parent == this;\n            }));\n        }\n        JSON_CATCH(...) {} // LCOV_EXCL_LINE\n#endif\n        static_cast<void>(check_parents);\n    }\n\n    void set_parents()\n    {\n#if JSON_DIAGNOSTICS\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                for (auto& element : *m_value.array)\n                {\n                    element.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                for (auto& element : *m_value.object)\n                {\n                    element.second.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n#endif\n    }\n\n    iterator set_parents(iterator it, typename iterator::difference_type count)\n    {\n#if JSON_DIAGNOSTICS\n        for (typename iterator::difference_type i = 0; i < count; ++i)\n        {\n            (it + i)->m_parent = this;\n        }\n#else\n        static_cast<void>(count);\n#endif\n        return it;\n    }\n\n    reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1))\n    {\n#if JSON_DIAGNOSTICS\n        if (old_capacity != std::size_t(-1))\n        {\n            // see https://github.com/nlohmann/json/issues/2838\n            JSON_ASSERT(type() == value_t::array);\n            if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))\n            {\n                // capacity has changed: update all parents\n                set_parents();\n                return j;\n            }\n        }\n\n        // ordered_json uses a vector internally, so pointers could have\n        // been invalidated; see https://github.com/nlohmann/json/issues/2962\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning(push )\n#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr\n#endif\n        if (detail::is_ordered_map<object_t>::value)\n        {\n            set_parents();\n            return j;\n        }\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning( pop )\n#endif\n\n        j.m_parent = this;\n#else\n        static_cast<void>(j);\n        static_cast<void>(old_capacity);\n#endif\n        return j;\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /*!\n    @brief parser event types\n\n    The parser callback distinguishes the following events:\n    - `object_start`: the parser read `{` and started to process a JSON object\n    - `key`: the parser read a key of a value in an object\n    - `object_end`: the parser read `}` and finished processing a JSON object\n    - `array_start`: the parser read `[` and started to process a JSON array\n    - `array_end`: the parser read `]` and finished processing a JSON array\n    - `value`: the parser finished reading a JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    @sa see @ref parser_callback_t for more information and examples\n    */\n    using parse_event_t = detail::parse_event_t;\n\n    /*!\n    @brief per-element parser callback type\n\n    With a parser callback function, the result of parsing a JSON text can be\n    influenced. When passed to @ref parse, it is called on certain events\n    (passed as @ref parse_event_t via parameter @a event) with a set recursion\n    depth @a depth and context JSON value @a parsed. The return value of the\n    callback function is a boolean indicating whether the element that emitted\n    the callback shall be kept or not.\n\n    We distinguish six scenarios (determined by the event type) in which the\n    callback function can be called. The following table describes the values\n    of the parameters @a depth, @a event, and @a parsed.\n\n    parameter @a event | description | parameter @a depth | parameter @a parsed\n    ------------------ | ----------- | ------------------ | -------------------\n    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded\n    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key\n    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object\n    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded\n    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array\n    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    Discarding a value (i.e., returning `false`) has different effects\n    depending on the context in which function was called:\n\n    - Discarded values in structured types are skipped. That is, the parser\n      will behave as if the discarded value was never read.\n    - In case a value outside a structured type is skipped, it is replaced\n      with `null`. This case happens if the top-level element is skipped.\n\n    @param[in] depth  the depth of the recursion during parsing\n\n    @param[in] event  an event of type parse_event_t indicating the context in\n    the callback function has been called\n\n    @param[in,out] parsed  the current intermediate parse result; note that\n    writing to this value has no effect for parse_event_t::key events\n\n    @return Whether the JSON value which called the function during parsing\n    should be kept (`true`) or not (`false`). In the latter case, it is either\n    skipped completely or replaced by an empty discarded object.\n\n    @sa see @ref parse for examples\n\n    @since version 1.0.0\n    */\n    using parser_callback_t = detail::parser_callback_t<basic_json>;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /*!\n    @brief create an empty value with a given type\n\n    Create an empty JSON value with a given type. The value will be default\n    initialized with an empty value which depends on the type:\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n    binary      | empty array\n\n    @param[in] v  the type of the value to create\n\n    @complexity Constant.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows the constructor for different @ref\n    value_t values,basic_json__value_t}\n\n    @sa see @ref clear() -- restores the postcondition of this constructor\n\n    @since version 1.0.0\n    */\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a null object\n\n    Create a `null` JSON value. It either takes a null pointer as parameter\n    (explicitly creating `null`) or no parameter (implicitly creating `null`).\n    The passed null pointer itself is not read -- it is only used to choose\n    the right constructor.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @liveexample{The following code shows the constructor with and without a\n    null pointer parameter.,basic_json__nullptr_t}\n\n    @since version 1.0.0\n    */\n    basic_json(std::nullptr_t = nullptr) noexcept\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value\n\n    This is a \"catch all\" constructor for all compatible JSON types; that is,\n    types for which a `to_json()` method exists. The constructor forwards the\n    parameter @a val to that method (to `json_serializer<U>::to_json` method\n    with `U = uncvref_t<CompatibleType>`, to be exact).\n\n    Template type @a CompatibleType includes, but is not limited to, the\n    following types:\n    - **arrays**: @ref array_t and all kinds of compatible containers such as\n      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,\n      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,\n      `std::multiset`, and `std::unordered_multiset` with a `value_type` from\n      which a @ref basic_json value can be constructed.\n    - **objects**: @ref object_t and all kinds of compatible associative\n      containers such as `std::map`, `std::unordered_map`, `std::multimap`,\n      and `std::unordered_multimap` with a `key_type` compatible to\n      @ref string_t and a `value_type` from which a @ref basic_json value can\n      be constructed.\n    - **strings**: @ref string_t, string literals, and all compatible string\n      containers can be used.\n    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,\n      @ref number_float_t, and all convertible number types such as `int`,\n      `size_t`, `int64_t`, `float` or `double` can be used.\n    - **boolean**: @ref boolean_t / `bool` can be used.\n    - **binary**: @ref binary_t / `std::vector<std::uint8_t>` may be used,\n      unfortunately because string literals cannot be distinguished from binary\n      character arrays by the C++ type system, all types compatible with `const\n      char*` will be directed to the string constructor instead.  This is both\n      for backwards compatibility, and due to the fact that a binary type is not\n      a standard JSON type.\n\n    See the examples below.\n\n    @tparam CompatibleType a type such that:\n    - @a CompatibleType is not derived from `std::istream`,\n    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move\n         constructors),\n    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)\n    - @a CompatibleType is not a @ref basic_json nested type (e.g.,\n         @ref json_pointer, @ref iterator, etc ...)\n    - `json_serializer<U>` has a `to_json(basic_json_t&, CompatibleType&&)` method\n\n    @tparam U = `uncvref_t<CompatibleType>`\n\n    @param[in] val the value to be forwarded to the respective constructor\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @liveexample{The following code shows the constructor with several\n    compatible types.,basic_json__CompatibleType}\n\n    @since version 2.1.0\n    */\n    template < typename CompatibleType,\n               typename U = detail::uncvref_t<CompatibleType>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >\n    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value from an existing one\n\n    This is a constructor for existing @ref basic_json types.\n    It does not hijack copy/move constructors, since the parameter has different\n    template arguments than the current ones.\n\n    The constructor tries to convert the internal @ref m_value of the parameter.\n\n    @tparam BasicJsonType a type such that:\n    - @a BasicJsonType is a @ref basic_json type.\n    - @a BasicJsonType has different template arguments than @ref basic_json_t.\n\n    @param[in] val the @ref basic_json value to be converted.\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n        using other_binary_t = typename BasicJsonType::binary_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::binary:\n                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a container (array or object) from an initializer list\n\n    Creates a JSON value of type array or object from the passed initializer\n    list @a init. In case @a type_deduction is `true` (default), the type of\n    the JSON value to be created is deducted from the initializer list @a init\n    according to the following rules:\n\n    1. If the list is empty, an empty JSON object value `{}` is created.\n    2. If the list consists of pairs whose first element is a string, a JSON\n       object value is created where the first elements of the pairs are\n       treated as keys and the second elements are as values.\n    3. In all other cases, an array is created.\n\n    The rules aim to create the best fit between a C++ initializer list and\n    JSON values. The rationale is as follows:\n\n    1. The empty initializer list is written as `{}` which is exactly an empty\n       JSON object.\n    2. C++ has no way of describing mapped types other than to list a list of\n       pairs. As JSON requires that keys must be of type string, rule 2 is the\n       weakest constraint one can pose on initializer lists to interpret them\n       as an object.\n    3. In all other cases, the initializer list could not be interpreted as\n       JSON object type, so interpreting it as JSON array type is safe.\n\n    With the rules described above, the following JSON values cannot be\n    expressed by an initializer list:\n\n    - the empty array (`[]`): use @ref array(initializer_list_t)\n      with an empty initializer list in this case\n    - arrays whose elements satisfy rule 2: use @ref\n      array(initializer_list_t) with the same initializer list\n      in this case\n\n    @note When used without parentheses around an empty initializer list, @ref\n    basic_json() is called instead of this function, yielding the JSON null\n    value.\n\n    @param[in] init  initializer list with JSON values\n\n    @param[in] type_deduction internal parameter; when set to `true`, the type\n    of the JSON value is deducted from the initializer list @a init; when set\n    to `false`, the type provided via @a manual_type is forced. This mode is\n    used by the functions @ref array(initializer_list_t) and\n    @ref object(initializer_list_t).\n\n    @param[in] manual_type internal parameter; when @a type_deduction is set\n    to `false`, the created JSON value will use the provided type (only @ref\n    value_t::array and @ref value_t::object are valid); when @a type_deduction\n    is set to `true`, this parameter has no effect\n\n    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is\n    `value_t::object`, but @a init contains an element which is not a pair\n    whose first element is a string. In this case, the constructor could not\n    create an object. If @a type_deduction would have be `true`, an array\n    would have been created. See @ref object(initializer_list_t)\n    for an example.\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows how JSON values are created from\n    initializer lists.,basic_json__list_init_t}\n\n    @sa see @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n    @sa see @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (!type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\", basic_json()));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            for (auto& element_ref : init)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            }\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief explicitly create a binary array (without subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = init;\n        return res;\n    }\n\n    /*!\n    @brief explicitly create a binary array (with subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n    @param[in] subtype subtype to use in MessagePack and BSON\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(init, subtype);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = std::move(init);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&, typename binary_t::subtype_type)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(std::move(init), subtype);\n        return res;\n    }\n\n    /*!\n    @brief explicitly create an array from an initializer list\n\n    Creates a JSON array value from a given initializer list. That is, given a\n    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the\n    initializer list is empty, the empty array `[]` is created.\n\n    @note This function is only needed to express two edge cases that cannot\n    be realized with the initializer list constructor (@ref\n    basic_json(initializer_list_t, bool, value_t)). These cases\n    are:\n    1. creating an array whose elements are all pairs whose first element is a\n    string -- in this case, the initializer list constructor would create an\n    object, taking the first elements as keys\n    2. creating an empty array -- passing the empty initializer list to the\n    initializer list constructor yields an empty object\n\n    @param[in] init  initializer list with JSON values to create an array from\n    (optional)\n\n    @return JSON array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `array`\n    function.,array}\n\n    @sa see @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa see @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /*!\n    @brief explicitly create an object from an initializer list\n\n    Creates a JSON object value from a given initializer list. The initializer\n    lists elements must be pairs, and their first elements must be strings. If\n    the initializer list is empty, the empty object `{}` is created.\n\n    @note This function is only added for symmetry reasons. In contrast to the\n    related function @ref array(initializer_list_t), there are\n    no cases which can only be expressed by this function. That is, any\n    initializer list @a init can also be passed to the initializer list\n    constructor @ref basic_json(initializer_list_t, bool, value_t).\n\n    @param[in] init  initializer list to create an object from (optional)\n\n    @return JSON object value\n\n    @throw type_error.301 if @a init is not a list of pairs whose first\n    elements are strings. In this case, no object can be created. When such a\n    value is passed to @ref basic_json(initializer_list_t, bool, value_t),\n    an array would have been created from the passed initializer list @a init.\n    See example below.\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `object`\n    function.,object}\n\n    @sa see @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa see @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /*!\n    @brief construct an array with count copies of given value\n\n    Constructs a JSON array value by creating @a cnt copies of a passed value.\n    In case @a cnt is `0`, an empty array is created.\n\n    @param[in] cnt  the number of JSON copies of @a val to create\n    @param[in] val  the JSON value to copy\n\n    @post `std::distance(begin(),end()) == cnt` holds.\n\n    @complexity Linear in @a cnt.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows examples for the @ref\n    basic_json(size_type\\, const basic_json&)\n    constructor.,basic_json__size_type_basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief construct a JSON container given an iterator range\n\n    Constructs the JSON value with the contents of the range `[first, last)`.\n    The semantics depends on the different types a JSON value can have:\n    - In case of a null type, invalid_iterator.206 is thrown.\n    - In case of other primitive types (number, boolean, or string), @a first\n      must be `begin()` and @a last must be `end()`. In this case, the value is\n      copied. Otherwise, invalid_iterator.204 is thrown.\n    - In case of structured types (array, object), the constructor behaves as\n      similar versions for `std::vector` or `std::map`; that is, a JSON array\n      or object is constructed from the values in the range.\n\n    @tparam InputIT an input iterator type (@ref iterator or @ref\n    const_iterator)\n\n    @param[in] first begin of the range to copy from (included)\n    @param[in] last end of the range to copy from (excluded)\n\n    @pre Iterators @a first and @a last must be initialized. **This\n         precondition is enforced with an assertion (see warning).** If\n         assertions are switched off, a violation of this precondition yields\n         undefined behavior.\n\n    @pre Range `[first, last)` is valid. Usually, this precondition cannot be\n         checked efficiently. Only certain edge cases are detected; see the\n         description of the exceptions below. A violation of this precondition\n         yields undefined behavior.\n\n    @warning A precondition is enforced with a runtime assertion that will\n             result in calling `std::abort` if this precondition is not met.\n             Assertions can be disabled by defining `NDEBUG` at compile time.\n             See https://en.cppreference.com/w/cpp/error/assert for more\n             information.\n\n    @throw invalid_iterator.201 if iterators @a first and @a last are not\n    compatible (i.e., do not belong to the same JSON value). In this case,\n    the range `[first, last)` is undefined.\n    @throw invalid_iterator.204 if iterators @a first and @a last belong to a\n    primitive type (number, boolean, or string), but @a first does not point\n    to the first element any more. In this case, the range `[first, last)` is\n    undefined. See example code below.\n    @throw invalid_iterator.206 if iterators @a first and @a last belong to a\n    null value. In this case, the range `[first, last)` is undefined.\n\n    @complexity Linear in distance between @a first and @a last.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows several ways to create JSON values by\n    specifying a subrange with iterators.,basic_json__InputIt_InputIt}\n\n    @since version 1.0.0\n    */\n    template < class InputIT, typename std::enable_if <\n                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||\n                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >\n    basic_json(InputIT first, InputIT last)\n    {\n        JSON_ASSERT(first.m_object != nullptr);\n        JSON_ASSERT(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\", basic_json()));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()\n                                         || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", *first.m_object));\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::object:\n            case value_t::array:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *first.m_object->m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(invalid_iterator::create(206, \"cannot construct with iterators from \" + std::string(first.m_object->type_name()), *first.m_object));\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    template<typename JsonRef,\n             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,\n                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >\n    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}\n\n    /*!\n    @brief copy constructor\n\n    Creates a copy of a given JSON value.\n\n    @param[in] other  the JSON value to copy\n\n    @post `*this == other`\n\n    @complexity Linear in the size of @a other.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - As postcondition, it holds: `other == basic_json(other)`.\n\n    @liveexample{The following code shows an example for the copy\n    constructor.,basic_json__basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *other.m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief move constructor\n\n    Move constructor. Constructs a JSON value with the contents of the given\n    value @a other using move semantics. It \"steals\" the resources from @a\n    other and leaves it as JSON null value.\n\n    @param[in,out] other  value to move to this object\n\n    @post `*this` has the same value as @a other before the call.\n    @post @a other is a JSON null value.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)\n    requirements.\n\n    @liveexample{The code below shows the move constructor explicitly called\n    via std::move.,basic_json__moveconstructor}\n\n    @since version 1.0.0\n    */\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant(false);\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief copy assignment\n\n    Copy assignment operator. Copies a JSON value via the \"copy and swap\"\n    strategy: It is expressed in terms of the copy constructor, destructor,\n    and the `swap()` member function.\n\n    @param[in] other  value to copy from\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n\n    @liveexample{The code below shows and example for the copy assignment. It\n    creates a copy of value `a` which is then swapped with `b`. Finally\\, the\n    copy of `a` (which is the null value after the swap) is\n    destroyed.,basic_json__copyassignment}\n\n    @since version 1.0.0\n    */\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        set_parents();\n        assert_invariant();\n        return *this;\n    }\n\n    /*!\n    @brief destructor\n\n    Destroys the JSON value and frees all allocated memory.\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - All stored elements are destroyed and all memory is freed.\n\n    @since version 1.0.0\n    */\n    ~basic_json() noexcept\n    {\n        assert_invariant(false);\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /*!\n    @brief serialization\n\n    Serialization function for JSON values. The function tries to mimic\n    Python's `json.dumps()` function, and currently supports its @a indent\n    and @a ensure_ascii parameters.\n\n    @param[in] indent If indent is nonnegative, then array elements and object\n    members will be pretty-printed with that indent level. An indent level of\n    `0` will only insert newlines. `-1` (the default) selects the most compact\n    representation.\n    @param[in] indent_char The character to use for indentation if @a indent is\n    greater than `0`. The default is ` ` (space).\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] error_handler  how to react on decoding errors; there are three\n    possible values: `strict` (throws and exception in case a decoding error\n    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),\n    and `ignore` (ignore invalid UTF-8 sequences during serialization; all\n    bytes are copied to the output unchanged).\n\n    @return string containing the serialization of the JSON value\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded and @a error_handler is set to strict\n\n    @note Binary values are serialized as object containing two keys:\n      - \"bytes\": an array of bytes as integers\n      - \"subtype\": the subtype as integer or \"null\" if the binary has no subtype\n\n    @complexity Linear.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @liveexample{The following example shows the effect of different @a indent\\,\n    @a indent_char\\, and @a ensure_ascii parameters to the result of the\n    serialization.,dump}\n\n    @see https://docs.python.org/2/library/json.html#json.dump\n\n    @since version 1.0.0; indentation character @a indent_char, option\n           @a ensure_ascii and exceptions added in version 3.0.0; error\n           handlers added in version 3.4.0; serialization of binary values added\n           in version 3.8.0.\n    */\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief return the type of the JSON value (explicit)\n\n    Return the type of the JSON value as a value from the @ref value_t\n    enumeration.\n\n    @return the type of the JSON value\n            Value type                | return value\n            ------------------------- | -------------------------\n            null                      | value_t::null\n            boolean                   | value_t::boolean\n            string                    | value_t::string\n            number (integer)          | value_t::number_integer\n            number (unsigned integer) | value_t::number_unsigned\n            number (floating-point)   | value_t::number_float\n            object                    | value_t::object\n            array                     | value_t::array\n            binary                    | value_t::binary\n            discarded                 | value_t::discarded\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `type()` for all JSON\n    types.,type}\n\n    @sa see @ref operator value_t() -- return the type of the JSON value (implicit)\n    @sa see @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /*!\n    @brief return whether type is primitive\n\n    This function returns true if and only if the JSON type is primitive\n    (string, number, boolean, or null).\n\n    @return `true` if type is primitive (string, number, boolean, or null),\n    `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_primitive()` for all JSON\n    types.,is_primitive}\n\n    @sa see @ref is_structured() -- returns whether JSON value is structured\n    @sa see @ref is_null() -- returns whether JSON value is `null`\n    @sa see @ref is_string() -- returns whether JSON value is a string\n    @sa see @ref is_boolean() -- returns whether JSON value is a boolean\n    @sa see @ref is_number() -- returns whether JSON value is a number\n    @sa see @ref is_binary() -- returns whether JSON value is a binary array\n\n    @since version 1.0.0\n    */\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() || is_string() || is_boolean() || is_number() || is_binary();\n    }\n\n    /*!\n    @brief return whether type is structured\n\n    This function returns true if and only if the JSON type is structured\n    (array or object).\n\n    @return `true` if type is structured (array or object), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_structured()` for all JSON\n    types.,is_structured}\n\n    @sa see @ref is_primitive() -- returns whether value is primitive\n    @sa see @ref is_array() -- returns whether value is an array\n    @sa see @ref is_object() -- returns whether value is an object\n\n    @since version 1.0.0\n    */\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() || is_object();\n    }\n\n    /*!\n    @brief return whether value is null\n\n    This function returns true if and only if the JSON value is null.\n\n    @return `true` if type is null, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_null()` for all JSON\n    types.,is_null}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_null() const noexcept\n    {\n        return m_type == value_t::null;\n    }\n\n    /*!\n    @brief return whether value is a boolean\n\n    This function returns true if and only if the JSON value is a boolean.\n\n    @return `true` if type is boolean, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_boolean()` for all JSON\n    types.,is_boolean}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_type == value_t::boolean;\n    }\n\n    /*!\n    @brief return whether value is a number\n\n    This function returns true if and only if the JSON value is a number. This\n    includes both integer (signed and unsigned) and floating-point values.\n\n    @return `true` if type is number (regardless whether integer, unsigned\n    integer or floating-type), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number()` for all JSON\n    types.,is_number}\n\n    @sa see @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa see @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa see @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() || is_number_float();\n    }\n\n    /*!\n    @brief return whether value is an integer number\n\n    This function returns true if and only if the JSON value is a signed or\n    unsigned integer number. This excludes floating-point values.\n\n    @return `true` if type is an integer or unsigned integer number, `false`\n    otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_integer()` for all\n    JSON types.,is_number_integer}\n\n    @sa see @ref is_number() -- check if value is a number\n    @sa see @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa see @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is an unsigned integer number\n\n    This function returns true if and only if the JSON value is an unsigned\n    integer number. This excludes floating-point and signed integer values.\n\n    @return `true` if type is an unsigned integer number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_unsigned()` for all\n    JSON types.,is_number_unsigned}\n\n    @sa see @ref is_number() -- check if value is a number\n    @sa see @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa see @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 2.0.0\n    */\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is a floating-point number\n\n    This function returns true if and only if the JSON value is a\n    floating-point number. This excludes signed and unsigned integer values.\n\n    @return `true` if type is a floating-point number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_float()` for all\n    JSON types.,is_number_float}\n\n    @sa see @ref is_number() -- check if value is number\n    @sa see @ref is_number_integer() -- check if value is an integer number\n    @sa see @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_type == value_t::number_float;\n    }\n\n    /*!\n    @brief return whether value is an object\n\n    This function returns true if and only if the JSON value is an object.\n\n    @return `true` if type is object, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_object()` for all JSON\n    types.,is_object}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_object() const noexcept\n    {\n        return m_type == value_t::object;\n    }\n\n    /*!\n    @brief return whether value is an array\n\n    This function returns true if and only if the JSON value is an array.\n\n    @return `true` if type is array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_array()` for all JSON\n    types.,is_array}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_array() const noexcept\n    {\n        return m_type == value_t::array;\n    }\n\n    /*!\n    @brief return whether value is a string\n\n    This function returns true if and only if the JSON value is a string.\n\n    @return `true` if type is string, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_string()` for all JSON\n    types.,is_string}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_string() const noexcept\n    {\n        return m_type == value_t::string;\n    }\n\n    /*!\n    @brief return whether value is a binary array\n\n    This function returns true if and only if the JSON value is a binary array.\n\n    @return `true` if type is binary array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_binary()` for all JSON\n    types.,is_binary}\n\n    @since version 3.8.0\n    */\n    constexpr bool is_binary() const noexcept\n    {\n        return m_type == value_t::binary;\n    }\n\n    /*!\n    @brief return whether value is discarded\n\n    This function returns true if and only if the JSON value was discarded\n    during parsing with a callback function (see @ref parser_callback_t).\n\n    @note This function will always be `false` for JSON values after parsing.\n    That is, discarded values can only occur during parsing, but will be\n    removed when inside a structured value or replaced by null in other cases.\n\n    @return `true` if type is discarded, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_discarded()` for all JSON\n    types.,is_discarded}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_type == value_t::discarded;\n    }\n\n    /*!\n    @brief return the type of the JSON value (implicit)\n\n    Implicitly return the type of the JSON value as a value from the @ref\n    value_t enumeration.\n\n    @return the type of the JSON value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies the @ref value_t operator for\n    all JSON types.,operator__value_t}\n\n    @sa see @ref type() -- return the type of the JSON value (explicit)\n    @sa see @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(type_name()), *this));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, \"incompatible ReferenceType for get_ref, actual type is \" + std::string(obj.type_name()), obj));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /*!\n    @brief get a pointer value (implicit)\n\n    Implicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning Writing data to the pointee of the result yields an undefined\n    state.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static\n    assertion.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get_ptr}\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (implicit)\n    @copydoc get_ptr()\n    */\n    template < typename PointerType, typename std::enable_if <\n                   std::is_pointer<PointerType>::value&&\n                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n  private:\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::is_default_constructible<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        auto ret = ValueType();\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueType>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @a BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value,\n                   int > = 0 >\n    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType,\n             detail::enable_if_t<\n                 std::is_same<BasicJsonType, basic_json_t>::value,\n                 int> = 0>\n    basic_json get_impl(detail::priority_tag<3> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType,\n             detail::enable_if_t<\n                 std::is_pointer<PointerType>::value,\n                 int> = 0>\n    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept\n    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n  public:\n    /*!\n    @brief get a (pointer) value (explicit)\n\n    Performs explicit type conversion between the JSON value and a compatible value if required.\n\n    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.\n    No copies are made.\n\n    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible\n    from the current @ref basic_json.\n\n    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`\n    method.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @tparam ValueType if necessary\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>\n#if defined(JSON_HAS_CPP_14)\n    constexpr\n#endif\n    auto get() const noexcept(\n    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))\n    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return get_impl<ValueType>(detail::priority_tag<4> {});\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa see @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value.\n    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType v;\n    JSONSerializer<ValueType>::from_json(*this, v);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n\n    @tparam ValueType the input parameter type.\n\n    @return the input parameter, allowing chaining calls.\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get_to}\n\n    @since version 3.3.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    // specialization to allow to call get_to with a basic_json value\n    // see https://github.com/nlohmann/json/issues/2175\n    template<typename ValueType,\n             detail::enable_if_t <\n                 detail::is_basic_json<ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const\n    {\n        v = *this;\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n\n    Implicit reference access to the internally stored JSON value. No copies\n    are made.\n\n    @warning Writing data to the referee of the result yields an undefined\n    state.\n\n    @tparam ReferenceType reference type; must be a reference to @ref array_t,\n    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or\n    @ref number_float_t. Enforced by static assertion.\n\n    @return reference to the internally stored JSON value if the requested\n    reference type @a ReferenceType fits to the JSON value; throws\n    type_error.303 otherwise\n\n    @throw type_error.303 in case passed type @a ReferenceType is incompatible\n    with the stored JSON value; see example below\n\n    @complexity Constant.\n\n    @liveexample{The example shows several calls to `get_ref()`.,get_ref}\n\n    @since version 1.1.0\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n    @copydoc get_ref()\n    */\n    template < typename ReferenceType, typename std::enable_if <\n                   std::is_reference<ReferenceType>::value&&\n                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   detail::conjunction <\n                       detail::negation<std::is_pointer<ValueType>>,\n                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,\n                                        detail::negation<std::is_same<ValueType, typename string_t::value_type>>,\n                                        detail::negation<detail::is_basic_json<ValueType>>,\n                                        detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,\n\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))\n                                                detail::negation<std::is_same<ValueType, std::string_view>>,\n#endif\n                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>\n                                                >::value, int >::type = 0 >\n                                        JSON_EXPLICIT operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /*!\n    @return reference to the binary value\n\n    @throw type_error.302 if the value is not binary\n\n    @sa see @ref is_binary() to check if the value is binary\n\n    @since version 3.8.0\n    */\n    binary_t& get_binary()\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name()), *this));\n        }\n\n        return *get_ptr<binary_t*>();\n    }\n\n    /// @copydoc get_binary()\n    const binary_t& get_binary() const\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name()), *this));\n        }\n\n        return *get_ptr<const binary_t*>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a reference to the element at specified location @a idx, with\n    bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__size_type}\n    */\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return set_parent(m_value.array->at(idx));\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a const reference to the element at specified location @a idx,\n    with bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__size_type_const}\n    */\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a reference to the element at with specified key @a key, with\n    bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa see @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__object_t_key_type}\n    */\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return set_parent(m_value.object->at(key));\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a const reference to the element at with specified key @a key,\n    with bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa see @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__object_t_key_type_const}\n    */\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\", *this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a reference to the element at specified location @a idx.\n\n    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),\n    then the array is silently filled up with `null` values to make `idx` a\n    valid reference to the last stored element.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array or null; in that\n    cases, using the [] operator with an index makes no sense.\n\n    @complexity Constant if @a idx is in the range of the array. Otherwise\n    linear in `idx - size()`.\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `[]` operator. Note the addition of `null`\n    values.,operatorarray__size_type}\n\n    @since version 1.0.0\n    */\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n#if JSON_DIAGNOSTICS\n                // remember array size & capacity before resizing\n                const auto old_size = m_value.array->size();\n                const auto old_capacity = m_value.array->capacity();\n#endif\n                m_value.array->resize(idx + 1);\n\n#if JSON_DIAGNOSTICS\n                if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))\n                {\n                    // capacity has changed: update all parents\n                    set_parents();\n                }\n                else\n                {\n                    // set parent for values added above\n                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));\n                }\n#endif\n                assert_invariant();\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a const reference to the element at specified location @a idx.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array; in that case,\n    using the [] operator with an index makes no sense.\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how array elements can be read using\n    the `[]` operator.,operatorarray__size_type_const}\n\n    @since version 1.0.0\n    */\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    reference operator[](const typename object_t::key_type& key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return set_parent(m_value.object->operator[](key));\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    reference operator[](T* key)\n    {\n        // implicitly convert null to object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return set_parent(m_value.object->operator[](key));\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    const_reference operator[](T* key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief access specified object element with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(key);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const typename object_t::key_type&), this function\n    does not throw if the given key @a key was not found.\n\n    @note Unlike @ref operator[](const typename object_t::key_type& key), this\n    function does not implicitly add an element to the position defined by @a\n    key. This function is furthermore also applicable to const objects.\n\n    @param[in] key  key of the element to access\n    @param[in] default_value  the value to return if @a key is not found\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a key\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value}\n\n    @sa see @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa see @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n\n    @since version 1.0.0\n    */\n    // using std::is_convertible in a std::enable_if will fail when using explicit conversions\n    template < class ValueType, typename std::enable_if <\n                   detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, ValueType>::value, int >::type = 0 >\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const\n    */\n    string_t value(const typename object_t::key_type& key, const char* default_value) const\n    {\n        return value(key, string_t(default_value));\n    }\n\n    /*!\n    @brief access specified object element via JSON Pointer with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(ptr);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const json_pointer&), this function does not throw\n    if the given key @a key was not found.\n\n    @param[in] ptr  a JSON pointer to the element to access\n    @param[in] default_value  the value to return if @a ptr found no value\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a ptr\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value_ptr}\n\n    @sa see @ref operator[](const json_pointer&) for unchecked access by reference\n\n    @since version 2.0.2\n    */\n    template<class ValueType, typename std::enable_if<\n                 detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ValueType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const json_pointer&, ValueType) const\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    string_t value(const json_pointer& ptr, const char* default_value) const\n    {\n        return value(ptr, string_t(default_value));\n    }\n\n    /*!\n    @brief access the first element\n\n    Returns a reference to the first element in the container. For a JSON\n    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.\n\n    @return In case of a structured type (array or object), a reference to the\n    first element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on `null` value\n\n    @liveexample{The following code shows an example for `front()`.,front}\n\n    @sa see @ref back() -- access the last element\n\n    @since version 1.0.0\n    */\n    reference front()\n    {\n        return *begin();\n    }\n\n    /*!\n    @copydoc basic_json::front()\n    */\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /*!\n    @brief access the last element\n\n    Returns a reference to the last element in the container. For a JSON\n    container `c`, the expression `c.back()` is equivalent to\n    @code {.cpp}\n    auto tmp = c.end();\n    --tmp;\n    return *tmp;\n    @endcode\n\n    @return In case of a structured type (array or object), a reference to the\n    last element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on a `null` value. See example\n    below.\n\n    @liveexample{The following code shows an example for `back()`.,back}\n\n    @sa see @ref front() -- access the first element\n\n    @since version 1.0.0\n    */\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @copydoc basic_json::back()\n    */\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @brief remove element given an iterator\n\n    Removes the element specified by iterator @a pos. The iterator @a pos must\n    be valid and dereferenceable. Thus the `end()` iterator (which is valid,\n    but is not dereferenceable) cannot be used as a value for @a pos.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] pos iterator to the element to remove\n    @return Iterator following the last removed element. If the iterator @a\n    pos refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.202 if called on an iterator which does not belong\n    to the current JSON value; example: `\"iterator does not fit current\n    value\"`\n    @throw invalid_iterator.205 if called on a primitive type with invalid\n    iterator (i.e., any iterator which is not `begin()`); example: `\"iterator\n    out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: amortized constant\n    - arrays: linear in distance between @a pos and the end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType}\n\n    @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa see @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa see @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\", *this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove elements given an iterator range\n\n    Removes the element specified by the range `[first; last)`. The iterator\n    @a first does not need to be dereferenceable if `first == last`: erasing\n    an empty range is a no-op.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] first iterator to the beginning of the range to remove\n    @param[in] last iterator past the end of the range to remove\n    @return Iterator following the last removed element. If the iterator @a\n    second refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.203 if called on iterators which does not belong\n    to the current JSON value; example: `\"iterators do not fit current value\"`\n    @throw invalid_iterator.204 if called on a primitive type with invalid\n    iterators (i.e., if `first != begin()` and `last != end()`); example:\n    `\"iterators out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: `log(size()) + std::distance(first, last)`\n    - arrays: linear in the distance between @a first and @a last, plus linear\n      in the distance between @a last and end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType_IteratorType}\n\n    @sa see @ref erase(IteratorType) -- removes the element at a given position\n    @sa see @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa see @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\", *this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()\n                                       || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", *this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove element from a JSON object given a key\n\n    Removes elements from a JSON object with the key value @a key.\n\n    @param[in] key value of the elements to remove\n\n    @return Number of elements removed. If @a ObjectType is the default\n    `std::map` type, the return value will always be `0` (@a key was not\n    found) or `1` (@a key was found).\n\n    @post References and iterators to the erased elements are invalidated.\n    Other references and iterators are not affected.\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n\n    @complexity `log(size()) + count(key)`\n\n    @liveexample{The example shows the effect of `erase()`.,erase__key_type}\n\n    @sa see @ref erase(IteratorType) -- removes the element at a given position\n    @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa see @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->erase(key);\n        }\n\n        JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief remove element from a JSON array given an index\n\n    Removes element from a JSON array at the index @a idx.\n\n    @param[in] idx index of the element to remove\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n    @throw out_of_range.401 when `idx >= size()`; example: `\"array index 17\n    is out of range\"`\n\n    @complexity Linear in distance between @a idx and the end of the container.\n\n    @liveexample{The example shows the effect of `erase()`.,erase__size_type}\n\n    @sa see @ref erase(IteratorType) -- removes the element at a given position\n    @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa see @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n\n    @since version 1.0.0\n    */\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", *this));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /*!\n    @brief find an element in a JSON object\n\n    Finds an element in a JSON object with key equivalent to @a key. If the\n    element is not found or the JSON value is not an object, end() is\n    returned.\n\n    @note This method always returns @ref end() when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value of the element to search for.\n\n    @return Iterator to an element with key equivalent to @a key. If no such\n    element is found or the JSON value is not an object, past-the-end (see\n    @ref end()) iterator is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `find()` is used.,find__key_type}\n\n    @sa see @ref contains(KeyT&&) const -- checks whether a key exists\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    iterator find(KeyT&& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief find an element in a JSON object\n    @copydoc find(KeyT&&)\n    */\n    template<typename KeyT>\n    const_iterator find(KeyT&& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief returns the number of occurrences of a key in a JSON object\n\n    Returns the number of elements with key @a key. If ObjectType is the\n    default `std::map` type, the return value will always be `0` (@a key was\n    not found) or `1` (@a key was found).\n\n    @note This method always returns `0` when executed on a JSON type that is\n          not an object.\n\n    @param[in] key key value of the element to count\n\n    @return Number of elements with key @a key. If the JSON value is not an\n    object, the return value will be `0`.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `count()` is used.,count}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    size_type count(KeyT&& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object\n\n    Check whether an element exists in a JSON object with key equivalent to\n    @a key. If the element is not found or the JSON value is not an object,\n    false is returned.\n\n    @note This method always returns false when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value to check its existence.\n\n    @return true if an element with specified @a key exists. If no such\n    element with such key is found or the JSON value is not an object,\n    false is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains}\n\n    @sa see @ref find(KeyT&&) -- returns an iterator to an object element\n    @sa see @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer\n\n    @since version 3.6.0\n    */\n    template < typename KeyT, typename std::enable_if <\n                   !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 >\n    bool contains(KeyT && key) const\n    {\n        return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object given a JSON pointer\n\n    Check whether the given JSON pointer @a ptr can be resolved in the current\n    JSON value.\n\n    @note This method can be executed on any JSON value type.\n\n    @param[in] ptr JSON pointer to check its existence.\n\n    @return true if the JSON pointer can be resolved to a stored value, false\n    otherwise.\n\n    @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains_json_pointer}\n\n    @sa see @ref contains(KeyT &&) const -- checks the existence of a key\n\n    @since version 3.7.0\n    */\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /*!\n    @brief returns an iterator to the first element\n\n    Returns an iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `begin()`.,begin}\n\n    @sa see @ref cbegin() -- returns a const iterator to the beginning\n    @sa see @ref end() -- returns an iterator to the end\n    @sa see @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cbegin()\n    */\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /*!\n    @brief returns a const iterator to the first element\n\n    Returns a const iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.\n\n    @liveexample{The following code shows an example for `cbegin()`.,cbegin}\n\n    @sa see @ref begin() -- returns an iterator to the beginning\n    @sa see @ref end() -- returns an iterator to the end\n    @sa see @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to one past the last element\n\n    Returns an iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `end()`.,end}\n\n    @sa see @ref cend() -- returns a const iterator to the end\n    @sa see @ref begin() -- returns an iterator to the beginning\n    @sa see @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cend()\n    */\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /*!\n    @brief returns a const iterator to one past the last element\n\n    Returns a const iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.\n\n    @liveexample{The following code shows an example for `cend()`.,cend}\n\n    @sa see @ref end() -- returns an iterator to the end\n    @sa see @ref begin() -- returns an iterator to the beginning\n    @sa see @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-beginning\n\n    Returns an iterator to the reverse-beginning; that is, the last element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(end())`.\n\n    @liveexample{The following code shows an example for `rbegin()`.,rbegin}\n\n    @sa see @ref crbegin() -- returns a const reverse iterator to the beginning\n    @sa see @ref rend() -- returns a reverse iterator to the end\n    @sa see @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /*!\n    @copydoc basic_json::crbegin()\n    */\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-end\n\n    Returns an iterator to the reverse-end; that is, one before the first\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(begin())`.\n\n    @liveexample{The following code shows an example for `rend()`.,rend}\n\n    @sa see @ref crend() -- returns a const reverse iterator to the end\n    @sa see @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa see @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /*!\n    @copydoc basic_json::crend()\n    */\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /*!\n    @brief returns a const reverse iterator to the last element\n\n    Returns a const iterator to the reverse-beginning; that is, the last\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.\n\n    @liveexample{The following code shows an example for `crbegin()`.,crbegin}\n\n    @sa see @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa see @ref rend() -- returns a reverse iterator to the end\n    @sa see @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /*!\n    @brief returns a const reverse iterator to one before the first\n\n    Returns a const reverse iterator to the reverse-end; that is, one before\n    the first element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.\n\n    @liveexample{The following code shows an example for `crend()`.,crend}\n\n    @sa see @ref rend() -- returns a reverse iterator to the end\n    @sa see @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa see @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /*!\n    @brief wrapper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without iterator_wrapper:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without iterator proxy:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with iterator proxy:\n\n    @code{cpp}\n    for (auto it : json::iterator_wrapper(j_object))\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example).\n\n    @param[in] ref  reference to a JSON value\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @note The name of this function is not yet final and may change in the\n    future.\n\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use @ref items() instead;\n                that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @copydoc iterator_wrapper(reference)\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @brief helper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without `items()` function:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with `items()` function:\n\n    @code{cpp}\n    for (auto& el : j_object.items())\n    {\n        std::cout << \"key: \" << el.key() << \", value:\" << el.value() << '\\n';\n    }\n    @endcode\n\n    The `items()` function also allows to use\n    [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding)\n    (C++17):\n\n    @code{cpp}\n    for (auto& [key, val] : j_object.items())\n    {\n        std::cout << \"key: \" << key << \", value:\" << val << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example). For primitive types (e.g., numbers),\n          `key()` returns an empty string.\n\n    @warning Using `items()` on temporary objects is dangerous. Make sure the\n             object's lifetime exeeds the iteration. See\n             <https://github.com/nlohmann/json/issues/2040> for more\n             information.\n\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the function is used.,items}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 3.1.0, structured bindings support since 3.5.0.\n    */\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /*!\n    @copydoc items()\n    */\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /*!\n    @brief checks whether the container is empty.\n\n    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `true`\n            boolean     | `false`\n            string      | `false`\n            number      | `false`\n            binary      | `false`\n            object      | result of function `object_t::empty()`\n            array       | result of function `array_t::empty()`\n\n    @liveexample{The following code uses `empty()` to check if a JSON\n    object contains any elements.,empty}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `empty()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return whether a string stored as JSON value\n    is empty - it returns whether the JSON container itself is empty which is\n    false in the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `begin() == end()`.\n\n    @sa see @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the number of elements\n\n    Returns the number of elements in a JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0`\n            boolean     | `1`\n            string      | `1`\n            number      | `1`\n            binary      | `1`\n            object      | result of function object_t::size()\n            array       | result of function array_t::size()\n\n    @liveexample{The following code calls `size()` on the different value\n    types.,size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their size() functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return the length of a string stored as JSON\n    value - it returns the number of elements in the JSON value which is 1 in\n    the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `std::distance(begin(), end())`.\n\n    @sa see @ref empty() -- checks whether the container is empty\n    @sa see @ref max_size() -- returns the maximal number of elements\n\n    @since version 1.0.0\n    */\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the maximum possible number of elements\n\n    Returns the maximum number of elements a JSON value is able to hold due to\n    system or library implementation limitations, i.e. `std::distance(begin(),\n    end())` for the JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0` (same as `size()`)\n            boolean     | `1` (same as `size()`)\n            string      | `1` (same as `size()`)\n            number      | `1` (same as `size()`)\n            binary      | `1` (same as `size()`)\n            object      | result of function `object_t::max_size()`\n            array       | result of function `array_t::max_size()`\n\n    @liveexample{The following code calls `max_size()` on the different value\n    types. Note the output is implementation specific.,max_size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `max_size()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of returning `b.size()` where `b` is the largest\n      possible JSON value.\n\n    @sa see @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /*!\n    @brief clears the contents\n\n    Clears the content of a JSON value and resets it to the default value as\n    if @ref basic_json(value_t) would have been called with the current value\n    type from @ref type():\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    binary      | An empty byte vector\n    object      | `{}`\n    array       | `[]`\n\n    @post Has the same effect as calling\n    @code {.cpp}\n    *this = basic_json(type());\n    @endcode\n\n    @liveexample{The example below shows the effect of `clear()` to different\n    JSON types.,clear}\n\n    @complexity Linear in the size of the JSON value.\n\n    @iterators All iterators, pointers and references related to this container\n               are invalidated.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @sa see @ref basic_json(value_t) -- constructor that creates an object with the\n        same value than calling `clear()`\n\n    @since version 1.0.0\n    */\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value.binary->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Appends the given element @a val to the end of the JSON value. If the\n    function is called on a JSON null value, an empty array is created before\n    appending @a val.\n\n    @param[in] val the value to add to the JSON array\n\n    @throw type_error.308 when called on a type other than JSON array or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON array. Note how the `null` value was silently\n    converted to a JSON array.,push_back}\n\n    @since version 1.0.0\n    */\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->push_back(std::move(val));\n        set_parent(m_value.array->back(), old_capacity);\n        // if val is moved from, basic_json move constructor marks it null so we do not call the destructor\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->push_back(val);\n        set_parent(m_value.array->back(), old_capacity);\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    Inserts the given element @a val to the JSON object. If the function is\n    called on a JSON null value, an empty object is created before inserting\n    @a val.\n\n    @param[in] val the value to add to the JSON object\n\n    @throw type_error.308 when called on a type other than JSON object or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON object. Note how the `null` value was silently\n    converted to a JSON object.,push_back__object_t__value}\n\n    @since version 1.0.0\n    */\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to object\n        auto res = m_value.object->insert(val);\n        set_parent(res.first->second);\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(const typename object_t::value_type&)\n    */\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    This function allows to use `push_back` with an initializer list. In case\n\n    1. the current value is an object,\n    2. the initializer list @a init contains only two elements, and\n    3. the first element of @a init is a string,\n\n    @a init is converted into an object element and added using\n    @ref push_back(const typename object_t::value_type&). Otherwise, @a init\n    is converted to a JSON value and added using @ref push_back(basic_json&&).\n\n    @param[in] init  an initializer list\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @note This function is required to resolve an ambiguous overload error,\n          because pairs like `{\"key\", \"value\"}` can be both interpreted as\n          `object_t::value_type` or `std::initializer_list<basic_json>`, see\n          https://github.com/nlohmann/json/issues/235 for more information.\n\n    @liveexample{The example shows how initializer lists are treated as\n    objects when possible.,push_back__initializer_list}\n    */\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() && init.size() == 2 && (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(initializer_list_t)\n    */\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Creates a JSON value from the passed parameters @a args to the end of the\n    JSON value. If the function is called on a JSON null value, an empty array\n    is created before appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return reference to the inserted element\n\n    @throw type_error.311 when called on a type other than JSON array or\n    null; example: `\"cannot use emplace_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` can be used to add\n    elements to a JSON array. Note how the `null` value was silently converted\n    to a JSON array.,emplace_back}\n\n    @since version 2.0.8, returns reference since 3.7.0\n    */\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace_back() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n        return set_parent(m_value.array->back(), old_capacity);\n    }\n\n    /*!\n    @brief add an object to an object if key does not exist\n\n    Inserts a new element into a JSON object constructed in-place with the\n    given @a args if there is no element with the key in the container. If the\n    function is called on a JSON null value, an empty object is created before\n    appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return a pair consisting of an iterator to the inserted element, or the\n            already-existing element if no insertion happened, and a bool\n            denoting whether the insertion took place.\n\n    @throw type_error.311 when called on a type other than JSON object or\n    null; example: `\"cannot use emplace() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `emplace()` can be used to add elements\n    to a JSON object. Note how the `null` value was silently converted to a\n    JSON object. Further note how no value is added if there was already one\n    value stored with the same key.,emplace}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace() with \" + std::string(type_name()), *this));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        set_parent(res.first->second);\n\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        JSON_ASSERT(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        set_parents();\n        return result;\n    }\n\n    /*!\n    @brief inserts element\n\n    Inserts element @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] val element to insert\n    @return iterator pointing to the inserted @a val.\n\n    @throw type_error.309 if called on JSON values other than arrays;\n    example: `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Constant plus linear in the distance between @a pos and end of\n    the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief inserts element\n    @copydoc insert(const_iterator, const basic_json&)\n    */\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts @a cnt copies of @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] cnt number of copies of @a val to insert\n    @param[in] val element to insert\n    @return iterator pointing to the first element inserted, or @a pos if\n    `cnt==0`\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Linear in @a cnt plus linear in the distance between @a pos\n    and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__count}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)` before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n    @throw invalid_iterator.211 if @a first or @a last are iterators into\n    container for which insert is called; example: `\"passed iterators may not\n    belong to container\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `first==last`\n\n    @complexity Linear in `std::distance(first, last)` plus linear in the\n    distance between @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", *this));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\", *this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from initializer list @a ilist before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] ilist initializer list to insert the values from\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `ilist` is empty\n\n    @complexity Linear in `ilist.size()` plus linear in the distance between\n    @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__ilist}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", *this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)`.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than objects; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number\n    of elements to insert.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range_object}\n\n    @since version 3.0.0\n    */\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name()), *this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", *this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\", *this));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from JSON object @a j and overwrites existing keys.\n\n    @param[in] j  JSON object to read values from\n    @param[in] merge_objects  when true, existing keys are not overwritten, but\n                              contents of objects are merged recursively\n                              (default: false)\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0, `merge_objects` parameter added in 3.10.4.\n    */\n    void update(const_reference j, bool merge_objects = false)\n    {\n        update(j.begin(), j.end(), merge_objects);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from from range `[first, last)` and overwrites existing\n    keys.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n    @param[in] merge_objects  when true, existing keys are not overwritten, but\n                              contents of objects are merged recursively\n                              (default: false)\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n    @throw type_error.312 if iterator @a first or @a last does does not\n    point to an object; example: `\"cannot use update() with string\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used__range.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0, `merge_objects` parameter added in 3.10.4.\n    */\n    void update(const_iterator first, const_iterator last, bool merge_objects = false)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name()), *this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", *this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(first.m_object->type_name()), *first.m_object));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            if (merge_objects && it.value().is_object())\n            {\n                auto it2 = m_value.object->find(it.key());\n                if (it2 != m_value.object->end())\n                {\n                    it2->second.update(it.value(), true);\n                    continue;\n                }\n            }\n            m_value.object->operator[](it.key()) = it.value();\n#if JSON_DIAGNOSTICS\n            m_value.object->operator[](it.key()).m_parent = this;\n#endif\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n\n        set_parents();\n        other.set_parents();\n        assert_invariant();\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value from @a left with those of @a right. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated. implemented as a friend function callable via ADL.\n\n    @param[in,out] left JSON value to exchange the contents with\n    @param[in,out] right JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    friend void swap(reference left, reference right) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        left.swap(right);\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON array with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other array to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an array; example: `\"cannot\n    use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how arrays can be swapped with\n    `swap()`.,swap__array_t}\n\n    @since version 1.0.0\n    */\n    void swap(array_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            std::swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON object with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other object to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an object; example:\n    `\"cannot use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how objects can be swapped with\n    `swap()`.,swap__object_t}\n\n    @since version 1.0.0\n    */\n    void swap(object_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            std::swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other string to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__string_t}\n\n    @since version 1.0.0\n    */\n    void swap(string_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            std::swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other binary to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__binary_t}\n\n    @since version 3.8.0\n    */\n    void swap(binary_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /// @copydoc swap(binary_t&)\n    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name()), *this));\n        }\n    }\n\n    /// @}\n\n  public:\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    /*!\n    @brief comparison: equal\n\n    Compares two JSON values for equality according to the following rules:\n    - Two JSON values are equal if (1) they are from the same type and (2)\n      their stored values are the same according to their respective\n      `operator==`.\n    - Integer and floating-point numbers are automatically converted before\n      comparison. Note that two NaN values are always treated as unequal.\n    - Two JSON null values are equal.\n\n    @note Floating-point inside JSON values numbers are compared with\n    `json::number_float_t::operator==` which is `double::operator==` by\n    default. To compare floating-point while respecting an epsilon, an alternative\n    [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39)\n    could be used, for instance\n    @code {.cpp}\n    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>\n    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept\n    {\n        return std::abs(a - b) <= epsilon;\n    }\n    @endcode\n    Or you can self-defined operator equal function like this:\n    @code {.cpp}\n    bool my_equal(const_reference lhs, const_reference rhs) {\n    const auto lhs_type lhs.type();\n    const auto rhs_type rhs.type();\n    if (lhs_type == rhs_type) {\n        switch(lhs_type)\n            // self_defined case\n            case value_t::number_float:\n                return std::abs(lhs - rhs) <= std::numeric_limits<float>::epsilon();\n            // other cases remain the same with the original\n            ...\n    }\n    ...\n    }\n    @endcode\n\n    @note NaN values never compare equal to themselves or to other NaN values.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are equal\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Linear.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__equal}\n\n    @since version 1.0.0\n    */\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return *lhs.m_value.array == *rhs.m_value.array;\n\n                case value_t::object:\n                    return *lhs.m_value.object == *rhs.m_value.object;\n\n                case value_t::null:\n                    return true;\n\n                case value_t::string:\n                    return *lhs.m_value.string == *rhs.m_value.string;\n\n                case value_t::boolean:\n                    return lhs.m_value.boolean == rhs.m_value.boolean;\n\n                case value_t::number_integer:\n                    return lhs.m_value.number_integer == rhs.m_value.number_integer;\n\n                case value_t::number_unsigned:\n                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;\n\n                case value_t::number_float:\n                    return lhs.m_value.number_float == rhs.m_value.number_float;\n\n                case value_t::binary:\n                    return *lhs.m_value.binary == *rhs.m_value.binary;\n\n                case value_t::discarded:\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n\n        return false;\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /*!\n    @brief comparison: not equal\n\n    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are not equal\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__notequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /*!\n    @brief comparison: less than\n\n    Compares whether one JSON value @a lhs is less than another JSON value @a\n    rhs according to the following rules:\n    - If @a lhs and @a rhs have the same type, the values are compared using\n      the default `<` operator.\n    - Integer and floating-point numbers are automatically converted before\n      comparison\n    - In case @a lhs and @a rhs have different types, the values are ignored\n      and the order of the types is considered, see\n      @ref operator<(const value_t, const value_t).\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__less}\n\n    @since version 1.0.0\n    */\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    // note parentheses are necessary, see\n                    // https://github.com/nlohmann/json/issues/1530\n                    return (*lhs.m_value.array) < (*rhs.m_value.array);\n\n                case value_t::object:\n                    return (*lhs.m_value.object) < (*rhs.m_value.object);\n\n                case value_t::null:\n                    return false;\n\n                case value_t::string:\n                    return (*lhs.m_value.string) < (*rhs.m_value.string);\n\n                case value_t::boolean:\n                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);\n\n                case value_t::number_integer:\n                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);\n\n                case value_t::number_unsigned:\n                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);\n\n                case value_t::number_float:\n                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);\n\n                case value_t::binary:\n                    return (*lhs.m_value.binary) < (*rhs.m_value.binary);\n\n                case value_t::discarded:\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;\n        }\n\n        // We only reach this line if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        return operator<(lhs_type, rhs_type);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /*!\n    @brief comparison: less than or equal\n\n    Compares whether one JSON value @a lhs is less than or equal to another\n    JSON value by calculating `not (rhs < lhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greater}\n\n    @since version 1.0.0\n    */\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(rhs < lhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /*!\n    @brief comparison: greater than\n\n    Compares whether one JSON value @a lhs is greater than another\n    JSON value by calculating `not (lhs <= rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__lessequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n\n    Compares whether one JSON value @a lhs is greater than or equal to another\n    JSON value by calculating `not (lhs < rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greaterequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs < rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n#ifndef JSON_NO_IO\n    /*!\n    @brief serialize to stream\n\n    Serialize the given JSON value @a j to the output stream @a o. The JSON\n    value will be serialized using the @ref dump member function.\n\n    - The indentation of the output can be controlled with the member variable\n      `width` of the output stream @a o. For instance, using the manipulator\n      `std::setw(4)` on @a o sets the indentation level to `4` and the\n      serialization result is the same as calling `dump(4)`.\n\n    - The indentation character can be controlled with the member variable\n      `fill` of the output stream @a o. For instance, the manipulator\n      `std::setfill('\\\\t')` sets indentation to use a tab character rather than\n      the default space character.\n\n    @param[in,out] o  stream to serialize to\n    @param[in] j  JSON value to serialize\n\n    @return the stream @a o\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @liveexample{The example below shows the serialization with different\n    parameters to `width` to adjust the indentation level.,operator_serialize}\n\n    @since version 1.0.0; indentation character added in version 3.0.0\n    */\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /*!\n    @brief serialize to stream\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use\n                @ref operator<<(std::ostream&, const basic_json&)\n                instead; that is, replace calls like `j >> o;` with `o << j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /*!\n    @brief deserialize from a compatible input\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb or reading from the input @a i has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an array.,parse__array__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__string__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__istream__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}\n\n    @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to\n    ignore comments.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(InputType&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief deserialize from a pair of character iterators\n\n    The value_type of the iterator must be a integral type with size of 1, 2 or\n    4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32.\n\n    @param[in] first iterator to start of character range\n    @param[in] last  iterator to end of character range\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(IteratorType first,\n                            IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))\n    static basic_json parse(detail::span_input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief check if the input is valid JSON\n\n    Unlike the @ref parse(InputType&&, const parser_callback_t,const bool)\n    function, this function neither throws an exception in case of invalid JSON\n    input (i.e., a parse error) nor creates diagnostic information.\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i input to read from\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return Whether the input read from @a i is valid JSON.\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `accept()` function reading\n    from a string.,accept__string}\n    */\n    template<typename InputType>\n    static bool accept(InputType&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    template<typename IteratorType>\n    static bool accept(IteratorType first, IteratorType last,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))\n    static bool accept(detail::span_input_adapter&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(i.get(), nullptr, false, ignore_comments).accept(true);\n    }\n\n    /*!\n    @brief generate SAX events\n\n    The SAX event lister must follow the interface of @ref json_sax.\n\n    This function reads from a compatible input. Examples are:\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in,out] sax  SAX event listener\n    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)\n    @param[in] strict  whether the input has to be consumed completely\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default); only applies to the JSON file format.\n\n    @return return value of the last processed SAX event\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has\n    a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `sax_parse()` function\n    reading from string and processing the events with a user-defined SAX\n    event consumer.,sax_parse}\n\n    @since version 3.2.0\n    */\n    template <typename InputType, typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(InputType&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template<class IteratorType, class SAX>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template <typename SAX>\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = i.get();\n        return format == input_format_t::json\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n#ifndef JSON_NO_IO\n    /*!\n    @brief deserialize from stream\n    @deprecated This stream operator is deprecated and will be removed in\n                version 4.0.0 of the library. Please use\n                @ref operator>>(std::istream&, basic_json&)\n                instead; that is, replace calls like `j << i;` with `i >> j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /*!\n    @brief deserialize from stream\n\n    Deserializes an input stream to a JSON value.\n\n    @param[in,out] i  input stream to read a serialized JSON value from\n    @param[in,out] j  JSON value to write the deserialized input to\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below shows how a JSON value is constructed by\n    reading a serialization from a stream.,operator_deserialize}\n\n    @sa parse(std::istream&, const parser_callback_t) for a variant with a\n    parser callback function to filter values while parsing\n\n    @since version 1.0.0\n    */\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /*!\n    @brief return the type as string\n\n    Returns the type name as string to be used in error messages - usually to\n    indicate that a function was called on a wrong JSON type.\n\n    @return a string representation of a the @a m_type member:\n            Value type  | return value\n            ----------- | -------------\n            null        | `\"null\"`\n            boolean     | `\"boolean\"`\n            string      | `\"string\"`\n            number      | `\"number\"` (for all number types)\n            object      | `\"object\"`\n            array       | `\"array\"`\n            binary      | `\"binary\"`\n            discarded   | `\"discarded\"`\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Constant.\n\n    @liveexample{The following code exemplifies `type_name()` for all JSON\n    types.,type_name}\n\n    @sa see @ref type() -- return the type of the JSON value\n    @sa see @ref operator value_t() -- return the type of the JSON value (implicit)\n\n    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`\n    since 3.0.0\n    */\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        {\n            switch (m_type)\n            {\n                case value_t::null:\n                    return \"null\";\n                case value_t::object:\n                    return \"object\";\n                case value_t::array:\n                    return \"array\";\n                case value_t::string:\n                    return \"string\";\n                case value_t::boolean:\n                    return \"boolean\";\n                case value_t::binary:\n                    return \"binary\";\n                case value_t::discarded:\n                    return \"discarded\";\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                default:\n                    return \"number\";\n            }\n        }\n    }\n\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n#if JSON_DIAGNOSTICS\n    /// a pointer to a parent value (for debugging purposes)\n    basic_json* m_parent = nullptr;\n#endif\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /*!\n    @brief create a CBOR serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise\n    Binary Object Representation) serialization format. CBOR is a binary\n    serialization format which aims to be more compact than JSON itself, yet\n    more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    CBOR types according to the CBOR specification (RFC 7049):\n\n    JSON value type | value/range                                | CBOR type                          | first byte\n    --------------- | ------------------------------------------ | ---------------------------------- | ---------------\n    null            | `null`                                     | Null                               | 0xF6\n    boolean         | `true`                                     | True                               | 0xF5\n    boolean         | `false`                                    | False                              | 0xF4\n    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B\n    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A\n    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39\n    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38\n    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37\n    number_integer  | 0..23                                      | Integer                            | 0x00..0x17\n    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17\n    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_float    | *any value representable by a float*       | Single-Precision Float             | 0xFA\n    number_float    | *any value NOT representable by a float*   | Double-Precision Float             | 0xFB\n    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77\n    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78\n    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79\n    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A\n    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B\n    array           | *size*: 0..23                              | array                              | 0x80..0x97\n    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98\n    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99\n    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A\n    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B\n    object          | *size*: 0..23                              | map                                | 0xA0..0xB7\n    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8\n    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9\n    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA\n    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB\n    binary          | *size*: 0..23                              | byte string                        | 0x40..0x57\n    binary          | *size*: 23..255                            | byte string (1 byte follow)        | 0x58\n    binary          | *size*: 256..65535                         | byte string (2 bytes follow)       | 0x59\n    binary          | *size*: 65536..4294967295                  | byte string (4 bytes follow)       | 0x5A\n    binary          | *size*: 4294967296..18446744073709551615   | byte string (8 bytes follow)       | 0x5B\n\n    Binary values with subtype are mapped to tagged values (0xD8..0xDB)\n    depending on the subtype, followed by a byte string, see \"binary\" cells\n    in the table above.\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a CBOR value.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The following CBOR types are not used in the conversion:\n          - UTF-8 strings terminated by \"break\" (0x7F)\n          - arrays terminated by \"break\" (0x9F)\n          - maps terminated by \"break\" (0xBF)\n          - byte strings terminated by \"break\" (0x5F)\n          - date/time (0xC0..0xC1)\n          - bignum (0xC2..0xC3)\n          - decimal fraction (0xC4)\n          - bigfloat (0xC5)\n          - expected conversions (0xD5..0xD7)\n          - simple values (0xE0..0xF3, 0xF8)\n          - undefined (0xF7)\n          - half-precision floats (0xF9)\n          - break (0xFF)\n\n    @param[in] j  JSON value to serialize\n    @return CBOR serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in CBOR format.,to_cbor}\n\n    @sa http://cbor.io\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        analogous deserialization\n    @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9; compact representation of floating-point numbers\n           since version 3.8.0\n    */\n    static std::vector<std::uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_cbor(j);\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /*!\n    @brief create a MessagePack serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the MessagePack\n    serialization format. MessagePack is a binary serialization format which\n    aims to be more compact than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    MessagePack types according to the MessagePack specification:\n\n    JSON value type | value/range                       | MessagePack type | first byte\n    --------------- | --------------------------------- | ---------------- | ----------\n    null            | `null`                            | nil              | 0xC0\n    boolean         | `true`                            | true             | 0xC3\n    boolean         | `false`                           | false            | 0xC2\n    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3\n    number_integer  | -2147483648..-32769               | int32            | 0xD2\n    number_integer  | -32768..-129                      | int16            | 0xD1\n    number_integer  | -128..-33                         | int8             | 0xD0\n    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF\n    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F\n    number_integer  | 128..255                          | uint 8           | 0xCC\n    number_integer  | 256..65535                        | uint 16          | 0xCD\n    number_integer  | 65536..4294967295                 | uint 32          | 0xCE\n    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F\n    number_unsigned | 128..255                          | uint 8           | 0xCC\n    number_unsigned | 256..65535                        | uint 16          | 0xCD\n    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE\n    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_float    | *any value representable by a float*     | float 32 | 0xCA\n    number_float    | *any value NOT representable by a float* | float 64 | 0xCB\n    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF\n    string          | *length*: 32..255                 | str 8            | 0xD9\n    string          | *length*: 256..65535              | str 16           | 0xDA\n    string          | *length*: 65536..4294967295       | str 32           | 0xDB\n    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F\n    array           | *size*: 16..65535                 | array 16         | 0xDC\n    array           | *size*: 65536..4294967295         | array 32         | 0xDD\n    object          | *size*: 0..15                     | fix map          | 0x80..0x8F\n    object          | *size*: 16..65535                 | map 16           | 0xDE\n    object          | *size*: 65536..4294967295         | map 32           | 0xDF\n    binary          | *size*: 0..255                    | bin 8            | 0xC4\n    binary          | *size*: 256..65535                | bin 16           | 0xC5\n    binary          | *size*: 65536..4294967295         | bin 32           | 0xC6\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a MessagePack value.\n\n    @note The following values can **not** be converted to a MessagePack value:\n          - strings with more than 4294967295 bytes\n          - byte strings with more than 4294967295 bytes\n          - arrays with more than 4294967295 elements\n          - objects with more than 4294967295 elements\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in MessagePack format.,to_msgpack}\n\n    @sa http://msgpack.org\n    @sa see @ref from_msgpack for the analogous deserialization\n    @sa see @ref to_cbor(const basic_json& for the related CBOR format\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_msgpack(j);\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /*!\n    @brief create a UBJSON serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the UBJSON\n    (Universal Binary JSON) serialization format. UBJSON aims to be more compact\n    than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    UBJSON types according to the UBJSON specification:\n\n    JSON value type | value/range                       | UBJSON type | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | `Z`\n    boolean         | `true`                            | true        | `T`\n    boolean         | `false`                           | false       | `F`\n    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`\n    number_integer  | -2147483648..-32769               | int32       | `l`\n    number_integer  | -32768..-129                      | int16       | `I`\n    number_integer  | -128..127                         | int8        | `i`\n    number_integer  | 128..255                          | uint8       | `U`\n    number_integer  | 256..32767                        | int16       | `I`\n    number_integer  | 32768..2147483647                 | int32       | `l`\n    number_integer  | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 0..127                            | int8        | `i`\n    number_unsigned | 128..255                          | uint8       | `U`\n    number_unsigned | 256..32767                        | int16       | `I`\n    number_unsigned | 32768..2147483647                 | int32       | `l`\n    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 2147483649..18446744073709551615  | high-precision | `H`\n    number_float    | *any value*                       | float64     | `D`\n    string          | *with shortest length indicator*  | string      | `S`\n    array           | *see notes on optimized format*   | array       | `[`\n    object          | *see notes on optimized format*   | map         | `{`\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a UBJSON value.\n\n    @note The following values can **not** be converted to a UBJSON value:\n          - strings with more than 9223372036854775807 bytes (theoretical)\n\n    @note The following markers are not used in the conversion:\n          - `Z`: no-op values are not created.\n          - `C`: single-byte strings are serialized with `S` markers.\n\n    @note Any UBJSON output created @ref to_ubjson can be successfully parsed\n          by @ref from_ubjson.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The optimized formats for containers are supported: Parameter\n          @a use_size adds size information to the beginning of a container and\n          removes the closing marker. Parameter @a use_type further checks\n          whether all elements of a container have the same type and adds the\n          type marker to the beginning of the container. The @a use_type\n          parameter must only be used together with @a use_size = true. Note\n          that @a use_size = true alone may result in larger representations -\n          the benefit of this parameter is that the receiving side is\n          immediately informed on the number of elements of the container.\n\n    @note If the JSON data contains the binary type, the value stored is a list\n          of integers, as suggested by the UBJSON documentation.  In particular,\n          this means that serialization and the deserialization of a JSON\n          containing binary values into UBJSON and back will result in a\n          different JSON object.\n\n    @param[in] j  JSON value to serialize\n    @param[in] use_size  whether to add size annotations to container types\n    @param[in] use_type  whether to add type annotations to container types\n                         (must be combined with @a use_size = true)\n    @return UBJSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in UBJSON format.,to_ubjson}\n\n    @sa http://ubjson.org\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the\n        analogous deserialization\n    @sa see @ref to_cbor(const basic_json& for the related CBOR format\n    @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format\n\n    @since version 3.1.0\n    */\n    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,\n            const bool use_size = false,\n            const bool use_type = false)\n    {\n        std::vector<std::uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and returns a vector\n           containing the corresponding BSON-representation.\n\n    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are\n    stored as a single entity (a so-called document).\n\n    The library uses the following mapping from JSON values types to BSON types:\n\n    JSON value type | value/range                       | BSON type   | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | 0x0A\n    boolean         | `true`, `false`                   | boolean     | 0x08\n    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12\n    number_integer  | -2147483648..2147483647           | int32       | 0x10\n    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 0..2147483647                     | int32       | 0x10\n    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 9223372036854775808..18446744073709551615| --   | --\n    number_float    | *any value*                       | double      | 0x01\n    string          | *any value*                       | string      | 0x02\n    array           | *any value*                       | document    | 0x04\n    object          | *any value*                       | document    | 0x03\n    binary          | *any value*                       | binary      | 0x05\n\n    @warning The mapping is **incomplete**, since only JSON-objects (and things\n    contained therein) can be serialized to BSON.\n    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,\n    and the keys may not contain U+0000, since they are serialized a\n    zero-terminated c-strings.\n\n    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`\n    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)\n    @throw type_error.317    if `!j.is_object()`\n\n    @pre The input `j` is required to be an object: `j.is_object() == true`.\n\n    @note Any BSON output created via @ref to_bson can be successfully parsed\n          by @ref from_bson.\n\n    @param[in] j  JSON value to serialize\n    @return BSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in BSON format.,to_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa see @ref from_bson(detail::input_adapter&&, const bool strict) for the\n        analogous deserialization\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n    @sa see @ref to_cbor(const basic_json&) for the related CBOR format\n    @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format\n    */\n    static std::vector<std::uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and forwards the\n           corresponding BSON-representation to the given output_adapter `o`.\n    @param j The JSON object to convert to BSON.\n    @param o The output adapter that receives the binary BSON representation.\n    @pre The input `j` shall be an object: `j.is_object() == true`\n    @sa see @ref to_bson(const basic_json&)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_bson(j);\n    }\n\n    /*!\n    @copydoc to_bson(const basic_json&, detail::output_adapter<std::uint8_t>)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in CBOR format\n\n    Deserializes a given input @a i to a JSON value using the CBOR (Concise\n    Binary Object Representation) serialization format.\n\n    The library maps CBOR types to JSON value types as follows:\n\n    CBOR type              | JSON value type | first byte\n    ---------------------- | --------------- | ----------\n    Integer                | number_unsigned | 0x00..0x17\n    Unsigned integer       | number_unsigned | 0x18\n    Unsigned integer       | number_unsigned | 0x19\n    Unsigned integer       | number_unsigned | 0x1A\n    Unsigned integer       | number_unsigned | 0x1B\n    Negative integer       | number_integer  | 0x20..0x37\n    Negative integer       | number_integer  | 0x38\n    Negative integer       | number_integer  | 0x39\n    Negative integer       | number_integer  | 0x3A\n    Negative integer       | number_integer  | 0x3B\n    Byte string            | binary          | 0x40..0x57\n    Byte string            | binary          | 0x58\n    Byte string            | binary          | 0x59\n    Byte string            | binary          | 0x5A\n    Byte string            | binary          | 0x5B\n    UTF-8 string           | string          | 0x60..0x77\n    UTF-8 string           | string          | 0x78\n    UTF-8 string           | string          | 0x79\n    UTF-8 string           | string          | 0x7A\n    UTF-8 string           | string          | 0x7B\n    UTF-8 string           | string          | 0x7F\n    array                  | array           | 0x80..0x97\n    array                  | array           | 0x98\n    array                  | array           | 0x99\n    array                  | array           | 0x9A\n    array                  | array           | 0x9B\n    array                  | array           | 0x9F\n    map                    | object          | 0xA0..0xB7\n    map                    | object          | 0xB8\n    map                    | object          | 0xB9\n    map                    | object          | 0xBA\n    map                    | object          | 0xBB\n    map                    | object          | 0xBF\n    False                  | `false`         | 0xF4\n    True                   | `true`          | 0xF5\n    Null                   | `null`          | 0xF6\n    Half-Precision Float   | number_float    | 0xF9\n    Single-Precision Float | number_float    | 0xFA\n    Double-Precision Float | number_float    | 0xFB\n\n    @warning The mapping is **incomplete** in the sense that not all CBOR\n             types can be converted to a JSON value. The following CBOR types\n             are not supported and will yield parse errors (parse_error.112):\n             - date/time (0xC0..0xC1)\n             - bignum (0xC2..0xC3)\n             - decimal fraction (0xC4)\n             - bigfloat (0xC5)\n             - expected conversions (0xD5..0xD7)\n             - simple values (0xE0..0xF3, 0xF8)\n             - undefined (0xF7)\n\n    @warning CBOR allows map keys of any type, whereas JSON only allows\n             strings as keys in object values. Therefore, CBOR maps with keys\n             other than UTF-8 strings are rejected (parse_error.113).\n\n    @note Any CBOR output created @ref to_cbor can be successfully parsed by\n          @ref from_cbor.\n\n    @param[in] i  an input in CBOR format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] tag_handler how to treat CBOR tags (optional, error by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from CBOR were\n    used in the given input @a v or if the input is not valid CBOR\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in CBOR\n    format to a JSON value.,from_cbor}\n\n    @sa http://cbor.io\n    @sa see @ref to_cbor(const basic_json&) for the analogous serialization\n    @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the\n        related MessagePack format\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the\n        related UBJSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0; added @a tag_handler parameter since 3.9.0.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);\n    }\n\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in MessagePack format\n\n    Deserializes a given input @a i to a JSON value using the MessagePack\n    serialization format.\n\n    The library maps MessagePack types to JSON value types as follows:\n\n    MessagePack type | JSON value type | first byte\n    ---------------- | --------------- | ----------\n    positive fixint  | number_unsigned | 0x00..0x7F\n    fixmap           | object          | 0x80..0x8F\n    fixarray         | array           | 0x90..0x9F\n    fixstr           | string          | 0xA0..0xBF\n    nil              | `null`          | 0xC0\n    false            | `false`         | 0xC2\n    true             | `true`          | 0xC3\n    float 32         | number_float    | 0xCA\n    float 64         | number_float    | 0xCB\n    uint 8           | number_unsigned | 0xCC\n    uint 16          | number_unsigned | 0xCD\n    uint 32          | number_unsigned | 0xCE\n    uint 64          | number_unsigned | 0xCF\n    int 8            | number_integer  | 0xD0\n    int 16           | number_integer  | 0xD1\n    int 32           | number_integer  | 0xD2\n    int 64           | number_integer  | 0xD3\n    str 8            | string          | 0xD9\n    str 16           | string          | 0xDA\n    str 32           | string          | 0xDB\n    array 16         | array           | 0xDC\n    array 32         | array           | 0xDD\n    map 16           | object          | 0xDE\n    map 32           | object          | 0xDF\n    bin 8            | binary          | 0xC4\n    bin 16           | binary          | 0xC5\n    bin 32           | binary          | 0xC6\n    ext 8            | binary          | 0xC7\n    ext 16           | binary          | 0xC8\n    ext 32           | binary          | 0xC9\n    fixext 1         | binary          | 0xD4\n    fixext 2         | binary          | 0xD5\n    fixext 4         | binary          | 0xD6\n    fixext 8         | binary          | 0xD7\n    fixext 16        | binary          | 0xD8\n    negative fixint  | number_integer  | 0xE0-0xFF\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @param[in] i  an input in MessagePack format convertible to an input\n                  adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from MessagePack were\n    used in the given input @a i or if the input is not valid MessagePack\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    MessagePack format to a JSON value.,from_msgpack}\n\n    @sa http://msgpack.org\n    @sa see @ref to_msgpack(const basic_json&) for the analogous serialization\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for\n        the related UBJSON format\n    @sa see @ref from_bson(InputType&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(InputType&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_msgpack(InputType&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(IteratorType first, IteratorType last,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(const T* ptr, std::size_t len,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(detail::span_input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in UBJSON format\n\n    Deserializes a given input @a i to a JSON value using the UBJSON (Universal\n    Binary JSON) serialization format.\n\n    The library maps UBJSON types to JSON value types as follows:\n\n    UBJSON type | JSON value type                         | marker\n    ----------- | --------------------------------------- | ------\n    no-op       | *no value, next value is read*          | `N`\n    null        | `null`                                  | `Z`\n    false       | `false`                                 | `F`\n    true        | `true`                                  | `T`\n    float32     | number_float                            | `d`\n    float64     | number_float                            | `D`\n    uint8       | number_unsigned                         | `U`\n    int8        | number_integer                          | `i`\n    int16       | number_integer                          | `I`\n    int32       | number_integer                          | `l`\n    int64       | number_integer                          | `L`\n    high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H'\n    string      | string                                  | `S`\n    char        | string                                  | `C`\n    array       | array (optimized values are supported)  | `[`\n    object      | object (optimized values are supported) | `{`\n\n    @note The mapping is **complete** in the sense that any UBJSON value can\n          be converted to a JSON value.\n\n    @param[in] i  an input in UBJSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if a parse error occurs\n    @throw parse_error.113 if a string could not be parsed successfully\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    UBJSON format to a JSON value.,from_ubjson}\n\n    @sa http://ubjson.org\n    @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             analogous serialization\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa see @ref from_msgpack(InputType&&, const bool, const bool) for\n        the related MessagePack format\n    @sa see @ref from_bson(InputType&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_ubjson(InputType&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(const T* ptr, std::size_t len,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(detail::span_input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief Create a JSON value from an input in BSON format\n\n    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)\n    serialization format.\n\n    The library maps BSON record types to JSON value types as follows:\n\n    BSON type       | BSON marker byte | JSON value type\n    --------------- | ---------------- | ---------------------------\n    double          | 0x01             | number_float\n    string          | 0x02             | string\n    document        | 0x03             | object\n    array           | 0x04             | array\n    binary          | 0x05             | binary\n    undefined       | 0x06             | still unsupported\n    ObjectId        | 0x07             | still unsupported\n    boolean         | 0x08             | boolean\n    UTC Date-Time   | 0x09             | still unsupported\n    null            | 0x0A             | null\n    Regular Expr.   | 0x0B             | still unsupported\n    DB Pointer      | 0x0C             | still unsupported\n    JavaScript Code | 0x0D             | still unsupported\n    Symbol          | 0x0E             | still unsupported\n    JavaScript Code | 0x0F             | still unsupported\n    int32           | 0x10             | number_integer\n    Timestamp       | 0x11             | still unsupported\n    128-bit decimal float | 0x13       | still unsupported\n    Max Key         | 0x7F             | still unsupported\n    Min Key         | 0xFF             | still unsupported\n\n    @warning The mapping is **incomplete**. The unsupported mappings\n             are indicated in the table above.\n\n    @param[in] i  an input in BSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.114 if an unsupported BSON record type is encountered\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    BSON format to a JSON value.,from_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa see @ref to_bson(const basic_json&) for the analogous serialization\n    @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa see @ref from_msgpack(InputType&&, const bool, const bool) for\n        the related MessagePack format\n    @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the\n        related UBJSON format\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_bson(InputType&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        return from_bson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. Similar to @ref operator[](const typename\n    object_t::key_type&), `null` values are created in arrays and objects if\n    necessary.\n\n    In particular:\n    - If the JSON pointer points to an object key that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned.\n    - If the JSON pointer points to an array index that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned. All indices between the current maximum and the given\n      index are also filled with `null`.\n    - The special value `-` is treated as a synonym for the index past the\n      end.\n\n    @param[in] ptr  a JSON pointer\n\n    @return reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer}\n\n    @since version 2.0.0\n    */\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. The function does not change the JSON\n    value; no `null` values are created. In particular, the special value\n    `-` yields an exception.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return const reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}\n\n    @since version 2.0.0\n    */\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a reference to the element at with specified JSON pointer @a ptr,\n    with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer}\n    */\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a const reference to the element at with specified JSON pointer @a\n    ptr, with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer_const}\n    */\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief return flattened JSON value\n\n    The function creates a JSON object whose keys are JSON pointers (see [RFC\n    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all\n    primitive. The original JSON value can be restored using the @ref\n    unflatten() function.\n\n    @return an object that maps JSON pointers to primitive values\n\n    @note Empty objects and arrays are flattened to `null` and will not be\n          reconstructed correctly by the @ref unflatten() function.\n\n    @complexity Linear in the size the JSON value.\n\n    @liveexample{The following code shows how a JSON object is flattened to an\n    object whose keys consist of JSON pointers.,flatten}\n\n    @sa see @ref unflatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /*!\n    @brief unflatten a previously flattened JSON value\n\n    The function restores the arbitrary nesting of a JSON value that has been\n    flattened before using the @ref flatten() function. The JSON value must\n    meet certain constraints:\n    1. The value must be an object.\n    2. The keys must be JSON pointers (see\n       [RFC 6901](https://tools.ietf.org/html/rfc6901))\n    3. The mapped values must be primitive JSON types.\n\n    @return the original JSON from a flattened version\n\n    @note Empty objects and arrays are flattened by @ref flatten() to `null`\n          values and can not unflattened to their original type. Apart from\n          this example, for a JSON value `j`, the following is always true:\n          `j == j.flatten().unflatten()`.\n\n    @complexity Linear in the size the JSON value.\n\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n\n    @liveexample{The following code shows how a flattened JSON object is\n    unflattened into the original nested JSON object.,unflatten}\n\n    @sa see @ref flatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON patch\n\n    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for\n    expressing a sequence of operations to apply to a JSON) document. With\n    this function, a JSON Patch is applied to the current JSON value by\n    executing all operations from the patch.\n\n    @param[in] json_patch  JSON patch document\n    @return patched document\n\n    @note The application of a patch is atomic: Either all operations succeed\n          and the patched document is returned or an exception is thrown. In\n          any case, the original value is not changed: the patch is applied\n          to a copy of the value.\n\n    @throw parse_error.104 if the JSON patch does not consist of an array of\n    objects\n\n    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory\n    attributes are missing); example: `\"operation add must have member path\"`\n\n    @throw out_of_range.401 if an array index is out of range.\n\n    @throw out_of_range.403 if a JSON pointer inside the patch could not be\n    resolved successfully in the current JSON value; example: `\"key baz not\n    found\"`\n\n    @throw out_of_range.405 if JSON pointer has no parent (\"add\", \"remove\",\n    \"move\")\n\n    @throw other_error.501 if \"test\" operation was unsuccessful\n\n    @complexity Linear in the size of the JSON value and the length of the\n    JSON patch. As usually only a fraction of the JSON value is affected by\n    the patch, the complexity can usually be neglected.\n\n    @liveexample{The following code shows how a JSON patch is applied to a\n    value.,patch}\n\n    @sa see @ref diff -- create a JSON patch by comparing two JSON values\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)\n\n    @since version 2.0.0\n    */\n    basic_json patch(const basic_json& json_patch) const\n    {\n        // make a working copy to apply the patch to\n        basic_json result = *this;\n\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result[ptr];\n\n            switch (parent.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::array_index(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\", parent));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent it cannot be primitive\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [this, &result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, \"key '\" + last_path + \"' not found\", *this));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(json_pointer::array_index(last_path));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", json_patch));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : \"operation '\" + op + \"'\";\n\n                // check if desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have member '\" + member + \"'\", val));\n                }\n\n                // check if result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have string member '\" + member + \"'\", val));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", val));\n            }\n\n            // collect mandatory members\n            const auto op = get_value(\"op\", \"op\", true).template get<std::string>();\n            const auto path = get_value(op, \"path\", true).template get<std::string>();\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const auto from_path = get_value(\"move\", \"from\", true).template get<std::string>();\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const auto from_path = get_value(\"copy\", \"from\", true).template get<std::string>();\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_HEDLEY_UNLIKELY(!success))\n                    {\n                        JSON_THROW(other_error::create(501, \"unsuccessful: \" + val.dump(), val));\n                    }\n\n                    break;\n                }\n\n                case patch_operations::invalid:\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, \"operation value '\" + op + \"' is invalid\", val));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief creates a diff as a JSON patch\n\n    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can\n    be changed into the value @a target by calling @ref patch function.\n\n    @invariant For two JSON values @a source and @a target, the following code\n    yields always `true`:\n    @code {.cpp}\n    source.patch(diff(source, target)) == target;\n    @endcode\n\n    @note Currently, only `remove`, `add`, and `replace` operations are\n          generated.\n\n    @param[in] source  JSON value to compare from\n    @param[in] target  JSON value to compare against\n    @param[in] path    helper value to create JSON pointers\n\n    @return a JSON patch to convert the @a source to @a target\n\n    @complexity Linear in the lengths of @a source and @a target.\n\n    @liveexample{The following code shows how a JSON patch is created as a\n    diff for two JSON values.,diff}\n\n    @sa see @ref patch -- apply a JSON patch\n    @sa see @ref merge_patch -- apply a JSON Merge Patch\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n\n    @since version 2.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() && i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], path + \"/\" + std::to_string(i));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // i now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", path + \"/\" + std::to_string(i)}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", path + \"/-\"},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto path_key = path + \"/\" + detail::escape(it.key());\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path_key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path_key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto path_key = path + \"/\" + detail::escape(it.key());\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path_key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // both primitive type: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON Merge Patch\n\n    The merge patch format is primarily intended for use with the HTTP PATCH\n    method as a means of describing a set of modifications to a target\n    resource's content. This function applies a merge patch to the current\n    JSON value.\n\n    The function implements the following algorithm from Section 2 of\n    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):\n\n    ```\n    define MergePatch(Target, Patch):\n      if Patch is an Object:\n        if Target is not an Object:\n          Target = {} // Ignore the contents and set it to an empty Object\n        for each Name/Value pair in Patch:\n          if Value is null:\n            if Name exists in Target:\n              remove the Name/Value pair from Target\n          else:\n            Target[Name] = MergePatch(Target[Name], Value)\n        return Target\n      else:\n        return Patch\n    ```\n\n    Thereby, `Target` is the current object; that is, the patch is applied to\n    the current value.\n\n    @param[in] apply_patch  the patch to apply\n\n    @complexity Linear in the lengths of @a patch.\n\n    @liveexample{The following code shows how a JSON Merge Patch is applied to\n    a JSON document.,merge_patch}\n\n    @sa see @ref patch -- apply a JSON patch\n    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)\n\n    @since version 3.0.0\n    */\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (!is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/*!\n@brief user-defined to_string function for JSON values\n\nThis function implements a user-defined to_string  for JSON objects.\n\n@param[in] j  a JSON object\n@return a std::string object\n*/\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n} // namespace nlohmann\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\n// specialization of std::swap, and std::hash\nnamespace std\n{\n\n/// hash value for JSON objects\ntemplate<>\nstruct hash<nlohmann::json>\n{\n    /*!\n    @brief return a hash value for a JSON object\n\n    @since version 1.0.0\n    */\n    std::size_t operator()(const nlohmann::json& j) const\n    {\n        return nlohmann::detail::hash(j);\n    }\n};\n\n/// specialization for std::less<value_t>\n/// @note: do not remove the space after '<',\n///        see https://github.com/nlohmann/json/pull/679\ntemplate<>\nstruct less<::nlohmann::detail::value_t>\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(nlohmann::detail::value_t lhs,\n                    nlohmann::detail::value_t rhs) const noexcept\n    {\n        return nlohmann::detail::operator<(lhs, rhs);\n    }\n};\n\n// C++20 prohibit function specialization in the std namespace.\n#ifndef JSON_HAS_CPP_20\n\n/*!\n@brief exchanges the values of two JSON objects\n\n@since version 1.0.0\n*/\ntemplate<>\ninline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name)\n    is_nothrow_move_constructible<nlohmann::json>::value&&  // NOLINT(misc-redundant-expression)\n    is_nothrow_move_assignable<nlohmann::json>::value\n                              )\n{\n    j1.swap(j2);\n}\n\n#endif\n\n} // namespace std\n\n/*!\n@brief user-defined string literal for JSON values\n\nThis operator implements a user-defined string literal for JSON objects. It\ncan be used by adding `\"_json\"` to a string literal and returns a JSON object\nif no parse error occurred.\n\n@param[in] s  a string representation of a JSON object\n@param[in] n  the length of string @a s\n@return a JSON object\n\n@since version 1.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/*!\n@brief user-defined string literal for JSON pointer\n\nThis operator implements a user-defined string literal for JSON Pointers. It\ncan be used by adding `\"_json_pointer\"` to a string literal and returns a JSON pointer\nobject if no parse error occurred.\n\n@param[in] s  a string representation of a JSON Pointer\n@param[in] n  the length of string @a s\n@return a JSON pointer object\n\n@since version 2.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n// #include <nlohmann/detail/macro_unscope.hpp>\n\n\n// restore clang diagnostic settings\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n\n// clean up\n#undef JSON_ASSERT\n#undef JSON_INTERNAL_CATCH\n#undef JSON_CATCH\n#undef JSON_THROW\n#undef JSON_TRY\n#undef JSON_PRIVATE_UNLESS_TESTED\n#undef JSON_HAS_CPP_11\n#undef JSON_HAS_CPP_14\n#undef JSON_HAS_CPP_17\n#undef JSON_HAS_CPP_20\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n#undef JSON_EXPLICIT\n#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL\n\n// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n\n\n#undef JSON_HEDLEY_ALWAYS_INLINE\n#undef JSON_HEDLEY_ARM_VERSION\n#undef JSON_HEDLEY_ARM_VERSION_CHECK\n#undef JSON_HEDLEY_ARRAY_PARAM\n#undef JSON_HEDLEY_ASSUME\n#undef JSON_HEDLEY_BEGIN_C_DECLS\n#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#undef JSON_HEDLEY_CLANG_HAS_WARNING\n#undef JSON_HEDLEY_COMPCERT_VERSION\n#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#undef JSON_HEDLEY_CONCAT\n#undef JSON_HEDLEY_CONCAT3\n#undef JSON_HEDLEY_CONCAT3_EX\n#undef JSON_HEDLEY_CONCAT_EX\n#undef JSON_HEDLEY_CONST\n#undef JSON_HEDLEY_CONSTEXPR\n#undef JSON_HEDLEY_CONST_CAST\n#undef JSON_HEDLEY_CPP_CAST\n#undef JSON_HEDLEY_CRAY_VERSION\n#undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#undef JSON_HEDLEY_C_DECL\n#undef JSON_HEDLEY_DEPRECATED\n#undef JSON_HEDLEY_DEPRECATED_FOR\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#undef JSON_HEDLEY_DIAGNOSTIC_POP\n#undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#undef JSON_HEDLEY_DMC_VERSION\n#undef JSON_HEDLEY_DMC_VERSION_CHECK\n#undef JSON_HEDLEY_EMPTY_BASES\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#undef JSON_HEDLEY_END_C_DECLS\n#undef JSON_HEDLEY_FLAGS\n#undef JSON_HEDLEY_FLAGS_CAST\n#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#undef JSON_HEDLEY_GCC_HAS_FEATURE\n#undef JSON_HEDLEY_GCC_HAS_WARNING\n#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#undef JSON_HEDLEY_GCC_VERSION\n#undef JSON_HEDLEY_GCC_VERSION_CHECK\n#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#undef JSON_HEDLEY_GNUC_HAS_WARNING\n#undef JSON_HEDLEY_GNUC_VERSION\n#undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#undef JSON_HEDLEY_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_BUILTIN\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_EXTENSION\n#undef JSON_HEDLEY_HAS_FEATURE\n#undef JSON_HEDLEY_HAS_WARNING\n#undef JSON_HEDLEY_IAR_VERSION\n#undef JSON_HEDLEY_IAR_VERSION_CHECK\n#undef JSON_HEDLEY_IBM_VERSION\n#undef JSON_HEDLEY_IBM_VERSION_CHECK\n#undef JSON_HEDLEY_IMPORT\n#undef JSON_HEDLEY_INLINE\n#undef JSON_HEDLEY_INTEL_CL_VERSION\n#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#undef JSON_HEDLEY_INTEL_VERSION\n#undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#undef JSON_HEDLEY_IS_CONSTANT\n#undef JSON_HEDLEY_IS_CONSTEXPR_\n#undef JSON_HEDLEY_LIKELY\n#undef JSON_HEDLEY_MALLOC\n#undef JSON_HEDLEY_MCST_LCC_VERSION\n#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#undef JSON_HEDLEY_MESSAGE\n#undef JSON_HEDLEY_MSVC_VERSION\n#undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#undef JSON_HEDLEY_NEVER_INLINE\n#undef JSON_HEDLEY_NON_NULL\n#undef JSON_HEDLEY_NO_ESCAPE\n#undef JSON_HEDLEY_NO_RETURN\n#undef JSON_HEDLEY_NO_THROW\n#undef JSON_HEDLEY_NULL\n#undef JSON_HEDLEY_PELLES_VERSION\n#undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#undef JSON_HEDLEY_PGI_VERSION\n#undef JSON_HEDLEY_PGI_VERSION_CHECK\n#undef JSON_HEDLEY_PREDICT\n#undef JSON_HEDLEY_PRINTF_FORMAT\n#undef JSON_HEDLEY_PRIVATE\n#undef JSON_HEDLEY_PUBLIC\n#undef JSON_HEDLEY_PURE\n#undef JSON_HEDLEY_REINTERPRET_CAST\n#undef JSON_HEDLEY_REQUIRE\n#undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#undef JSON_HEDLEY_REQUIRE_MSG\n#undef JSON_HEDLEY_RESTRICT\n#undef JSON_HEDLEY_RETURNS_NON_NULL\n#undef JSON_HEDLEY_SENTINEL\n#undef JSON_HEDLEY_STATIC_ASSERT\n#undef JSON_HEDLEY_STATIC_CAST\n#undef JSON_HEDLEY_STRINGIFY\n#undef JSON_HEDLEY_STRINGIFY_EX\n#undef JSON_HEDLEY_SUNPRO_VERSION\n#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#undef JSON_HEDLEY_TINYC_VERSION\n#undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#undef JSON_HEDLEY_TI_ARMCL_VERSION\n#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL2000_VERSION\n#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL430_VERSION\n#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL6X_VERSION\n#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL7X_VERSION\n#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CLPRU_VERSION\n#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#undef JSON_HEDLEY_TI_VERSION\n#undef JSON_HEDLEY_TI_VERSION_CHECK\n#undef JSON_HEDLEY_UNAVAILABLE\n#undef JSON_HEDLEY_UNLIKELY\n#undef JSON_HEDLEY_UNPREDICTABLE\n#undef JSON_HEDLEY_UNREACHABLE\n#undef JSON_HEDLEY_UNREACHABLE_RETURN\n#undef JSON_HEDLEY_VERSION\n#undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#undef JSON_HEDLEY_VERSION_ENCODE\n#undef JSON_HEDLEY_WARNING\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#undef JSON_HEDLEY_FALL_THROUGH\n\n\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/renderer/CMakeLists.txt",
    "content": "# Copyright (C) 2023, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nset(SIBR_PROJECT \"remote\")\nproject(sibr_${SIBR_PROJECT})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\n\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS} .)\nif(WIN32)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n)\nelse()\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n\tsibr_basic\n)\nendif()\n\n\nadd_definitions( -DSIBR_EXP_ULR_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/renderer\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS ${SHADERS}\n\tRSC_FOLDER ${SIBR_PROJECT}\n\n    #STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/renderer/Config.hpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n# include <core/system/CommandLineArgs.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_EXP_ULR_EXPORT\n#      ifdef SIBR_EXP_ULR_EXPORTS\n/* We are building this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllexport)\n#      else\n/* We are using this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_EXP_ULR_EXPORT\n# endif\n\nnamespace sibr {\n\n\t/// Arguments for all ULR applications.\n\tstruct RemoteAppArgs :\n\t\tvirtual BasicIBRAppArgs {\n\t\tRequiredArg<std::string> pathShort = {\"s\", \"path to the dataset root\"};\n\t\tArg<bool> loadImages = { \"load_images\", \"Whether or not to load images for scene overview\" };\n\t\tArg<std::string> ip = { \"ip\", \"127.0.0.1\", \"Target IP to connect to (default localhost)\"};\n\t\tArg<uint> port = { \"port\", 6009, \"Port to use for connection\" };\n\t};\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/renderer/RemotePointView.cpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n#include <projects/remote/renderer/RemotePointView.hpp>\n#include <core/graphics/GUI.hpp>\n#include <thread>\n#include <boost/asio.hpp>\n\nconstexpr char* jResX = \"resolution_x\";\nconstexpr char* jResY = \"resolution_y\";\nconstexpr char* jFovY = \"fov_y\";\nconstexpr char* jFovX = \"fov_x\";\nconstexpr char* jZFar = \"z_far\";\nconstexpr char* jZNear = \"z_near\";\nconstexpr char* jTrain = \"train\";\nconstexpr char* jViewMat = \"view_matrix\";\nconstexpr char* jViewProjMat = \"view_projection_matrix\";\nconstexpr char* jScalingModifier = \"scaling_modifier\";\nconstexpr char* jSHsPython = \"shs_python\";\nconstexpr char* jRotScalePython = \"rot_scale_python\";\nconstexpr char* jKeepAlive = \"keep_alive\";\n\nvoid sibr::RemotePointView::send_receive()\n{\n\twhile (keep_running)\n\t{\n\t\tSIBR_LOG << \"Trying to connect...\" << std::endl;\n\t\ttry\n\t\t{\n\t\t\tboost::asio::io_service ioservice;\n\t\t\tboost::asio::ip::tcp::socket sock(ioservice);\n\t\t\tboost::asio::ip::address addr = boost::asio::ip::address::from_string(_ip);\n\t\t\tboost::asio::ip::tcp::endpoint contact(addr, _port);\n\n\t\t\tboost::system::error_code ec;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tsock.connect(contact, ec);\n\t\t\t\tstd::this_thread::sleep_for(std::chrono::milliseconds(100));\n\t\t\t} while (keep_running && ec.failed());\n\n\t\t\tSIBR_LOG << \"Connected!\" << std::endl;\n\t\t\twhile (keep_running)\n\t\t\t{\n\t\t\t\t{\n\t\t\t\t\tstd::lock_guard<std::mutex> lg(_renderDataMutex);\n\n\t\t\t\t\t// Serialize our arbitrary data to something simple, yet convenient for both sides\n\t\t\t\t\tjson sendData;\n\t\t\t\t\tsendData[jTrain] = _doTrainingBool ? 1 : 0;\n\t\t\t\t\tsendData[jSHsPython] = _doSHsPython ? 1 : 0;\n\t\t\t\t\tsendData[jRotScalePython] = _doRotScalePython ? 1 : 0;\n\t\t\t\t\tsendData[jScalingModifier] = _scalingModifier;\n\t\t\t\t\tsendData[jResX] = _remoteInfo.imgResolution.x();\n\t\t\t\t\tsendData[jResY] = _remoteInfo.imgResolution.y();\n\t\t\t\t\tsendData[jFovY] = _remoteInfo.fovy;\n\t\t\t\t\tsendData[jFovX] = _remoteInfo.fovx;\n\t\t\t\t\tsendData[jZFar] = _remoteInfo.zfar;\n\t\t\t\t\tsendData[jZNear] = _remoteInfo.znear;\n\t\t\t\t\tsendData[jKeepAlive] = _keepAlive ? 1 : 0;\n\t\t\t\t\tsendData[jViewMat] = std::vector<float>((float*)&_remoteInfo.view, ((float*)&_remoteInfo.view) + 16);\n\t\t\t\t\tsendData[jViewProjMat] = std::vector<float>((float*)&_remoteInfo.viewProj, ((float*)&_remoteInfo.viewProj) + 16);\n\n\t\t\t\t\tstd::string message = sendData.dump();\n\t\t\t\t\tuint32_t messageLength = message.size();\n\t\t\t\t\tboost::asio::write(sock, boost::asio::buffer(&messageLength, sizeof(uint32_t)));\n\t\t\t\t\tboost::asio::write(sock, boost::asio::buffer(message.c_str(), messageLength));\n\t\t\t\t}\n\n\t\t\t\tuint32_t bytes_to_receive = _remoteInfo.imgResolution.x() * _remoteInfo.imgResolution.y() * 3;\n\t\t\t\tif (bytes_to_receive > 0)\n\t\t\t\t{\n\t\t\t\t\tstd::lock_guard<std::mutex> ilg(_imageDataMutex);\n\t\t\t\t\t_imageData.resize(bytes_to_receive);\n\t\t\t\t\tboost::asio::read(sock, boost::asio::buffer(_imageData.data(), _imageData.size()));\n\t\t\t\t\t{\n\t\t\t\t\t\tstd::lock_guard<std::mutex> lg(_renderDataMutex);\n\t\t\t\t\t\t_timestampReceived = _timestampRequested;\n\t\t\t\t\t}\n\t\t\t\t\t_imageDirty = true;\n\t\t\t\t}\n\t\t\t\tuint32_t sceneLength;\n\t\t\t\tboost::asio::read(sock, boost::asio::buffer(&sceneLength, sizeof(uint32_t)));\n\t\t\t\tstd::vector<char> sceneName(sceneLength);\n\t\t\t\tboost::asio::read(sock, boost::asio::buffer(sceneName.data(), sceneLength));\n\t\t\t\tsceneName.push_back(0);\n\t\t\t\tcurrent_scene = std::string(sceneName.data());\n\t\t\t}\n\t\t}\n\t\tcatch (...)\n\t\t{\n\t\t\tSIBR_LOG << \"Connection dropped\" << std::endl;\n\t\t}\n\t}\n}\n\nsibr::RemotePointView::RemotePointView(std::string ip, uint port) : sibr::ViewBase(0, 0),\n_ip(ip), _port(port)\n{\n\t_pointbasedrenderer.reset(new PointBasedRenderer());\n\t_copyRenderer.reset(new CopyRenderer());\n\t_copyRenderer->flip() = true;\n\n\tglCreateTextures(GL_TEXTURE_2D, 1, &_imageTexture);\n\tglTextureParameteri(_imageTexture, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);\n\tglTextureParameteri(_imageTexture, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);\n\tglTextureParameteri(_imageTexture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n\tglTextureParameteri(_imageTexture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n\n\t_networkThread = std::make_unique<std::thread>(&RemotePointView::send_receive, this);\n}\n\nvoid sibr::RemotePointView::setScene(const sibr::BasicIBRScene::Ptr & newScene) {\n\t_scene = newScene;\n\n\t// Tell the scene we are a priori using all active cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto & cams = newScene->cameras()->inputCameras();\n\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif (cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n}\n\nvoid sibr::RemotePointView::onRenderIBR(sibr::IRenderTarget & dst, const sibr::Camera & eye)\n{\n\tif (!_scene)\n\t\treturn;\n\n\tbool preview = false;\n\t{\n\t\tstd::lock_guard<std::mutex> lg(_renderDataMutex);\n\t\tif (eye.view() != _remoteInfo.view || eye.viewproj() != _remoteInfo.viewProj)\n\t\t{\n\t\t\t_remoteInfo.view = eye.view();\n\t\t\t_remoteInfo.viewProj = eye.viewproj();\n\t\t\t_remoteInfo.fovy = eye.fovy();\n\t\t\t_remoteInfo.fovx = 2 * atan(tan(eye.fovy() * 0.5) * eye.aspect());\n\t\t\t_remoteInfo.znear = eye.znear();\n\t\t\t_remoteInfo.zfar = eye.zfar();\n\t\t\t_timestampRequested++;\n\t\t}\n\t\tif (_resolution != _remoteInfo.imgResolution)\n\t\t{\n\t\t\t_remoteInfo.imgResolution = _resolution;\n\t\t\t_imageResize = true;\n\t\t\t_timestampRequested++;\n\t\t}\n\t\tpreview = _timestampReceived != _timestampRequested;\n\t}\n\n\tif (_showSfM || _timestampReceived == 0 || (preview && _renderSfMInMotion))\n\t{\n\t\t_pointbasedrenderer->process(_scene->proxies()->proxy(), eye, dst);\n\t}\n\telse\n\t{\n\t\t{\n\t\t\tstd::lock_guard<std::mutex> ilg(_imageDataMutex);\n\t\t\tif (_imageResize)\n\t\t\t{\n\t\t\t\tglBindTexture(GL_TEXTURE_2D, _imageTexture);\n\t\t\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _resolution.x(), _resolution.y(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);\n\t\t\t\tglBindTexture(GL_TEXTURE_2D, 0);\n\t\t\t\t_imageResize = false;\n\t\t\t}\n\t\t\tif (_imageDirty && _imageData.size() == 3 * _resolution.x() * _resolution.y())\n\t\t\t{\n\t\t\t\tglTextureSubImage2D(_imageTexture, 0, 0, 0, _resolution.x(), _resolution.y(), GL_RGB, GL_UNSIGNED_BYTE, _imageData.data());\n\t\t\t\t_imageDirty = false;\n\t\t\t}\n\t\t}\n\t\t_copyRenderer->process(_imageTexture, dst);\n\t}\n}\n\nvoid sibr::RemotePointView::onGUI()\n{\n\tconst std::string guiName = \"Remote Viewer Settings (\" + name() + \")\";\n\tif (ImGui::Begin(guiName.c_str())) \n\t{\n\t\tImGui::Checkbox(\"Show Input Points\", &_showSfM);\n\t\tImGui::Checkbox(\"Show Input Points during Motion\", &_renderSfMInMotion);\n\t\tImGui::Checkbox(\"Train\", &_doTrainingBool);\n\t\tImGui::Checkbox(\"SHs Python\", &_doSHsPython);\n\t\tImGui::Checkbox(\"Rot-Scale Python\", &_doRotScalePython);\n\t\tImGui::Checkbox(\"Keep model alive (after training)\", &_keepAlive);\n\t\tImGui::SliderFloat(\"Scaling Modifier\", &_scalingModifier, 0.001f, 1.0f);\n\t}\n\tImGui::End();\n}\n\nsibr::RemotePointView::~RemotePointView()\n{\n\tkeep_running = false;\n\t_networkThread->join();\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/remote/renderer/RemotePointView.hpp",
    "content": "/*\n * Copyright (C) 2023, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/renderer/RenderMaskHolder.hpp>\n# include <core/scene/BasicIBRScene.hpp>\n# include <core/system/SimpleTimer.hpp>\n# include <core/system/Config.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/view/ViewBase.hpp>\n# include <core/renderer/CopyRenderer.hpp>\n# include <core/renderer/PointBasedRenderer.hpp>\n# include <atomic>\n# include <mutex>\n# include <memory>\n# include <core/graphics/Texture.hpp>\n#include <projects/remote/json.hpp>\n#include <thread>\nusing json = nlohmann::json;\n\nnamespace sibr { \n\n\t/**\n\t * \\class RemotePointView\n\t * \\brief Wrap a ULR renderer with additional parameters and information.\n\t */\n\tclass SIBR_EXP_ULR_EXPORT RemotePointView : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(RemotePointView);\n\n\tpublic:\n\n\t\tRemotePointView(std::string ip, uint port);\n\n\t\t/** Replace the current scene.\n\t\t *\\param newScene the new scene to render */\n\t\tvoid setScene(const sibr::BasicIBRScene::Ptr & newScene);\n\n\t\t/**\n\t\t * Perform rendering. Called by the view manager or rendering mode.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param eye The novel viewpoint.\n\t\t */\n\t\tvoid onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye) override;\n\n\t\t/**\n\t\t * Update the GUI.\n\t\t */\n\t\tvoid onGUI() override;\n\n\t\t/** \\return a reference to the scene */\n\t\tconst std::shared_ptr<sibr::BasicIBRScene> & getScene() const { return _scene; }\n\n\t\tvirtual ~RemotePointView() override;\n\n\t\tstd::string sceneName()\n\t\t{\n\t\t\treturn current_scene;\n\t\t}\n\n\tprotected:\n\n\t\tstd::string current_scene;\n\n\t\tstruct RemoteRenderInfo\n\t\t{\n\t\t\tVector2i imgResolution;\n\t\t\tfloat fovy;\n\t\t\tfloat fovx;\n\t\t\tfloat znear;\n\t\t\tfloat zfar;\n\t\t\tMatrix4f view;\n\t\t\tMatrix4f viewProj;\n\t\t};\n\n\t\tRemoteRenderInfo _remoteInfo;\n\n\t\tbool _doTrainingBool = true;\n\t\tbool _doSHsPython = false;\n\t\tbool _doRotScalePython = false;\n\t\tbool _keepAlive = true;\n\t\tbool _showSfM = false;\n\n\t\tfloat _scalingModifier = 1.0f;\n\n\t\tstd::atomic<bool> keep_running = true;\n\n\t\tstd::string _ip;\n\t\tint _port;\n\n\t\tvoid send_receive();\n\n\t\tGLuint _imageTexture;\n\n\t\tbool _renderSfMInMotion = false;\n\n\t\tbool _imageResize = true;\n\t\tbool _imageDirty = true;\n\t\tuint32_t _timestampRequested = 1;\n\t\tuint32_t _timestampReceived = 0;\n\n\t\tstd::mutex _renderDataMutex;\n\t\tstd::mutex _imageDataMutex;\n\n\t\tstd::unique_ptr <std::thread> _networkThread;\n\t\tstd::vector<unsigned char> _imageData;\n\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene; ///< The current scene.\n\t\tPointBasedRenderer::Ptr _pointbasedrenderer;\n\t\tCopyRenderer::Ptr _copyRenderer;\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(sibr_ulr_all)\n\nadd_subdirectory(apps)\nadd_subdirectory(renderer)\n\ninclude(install_runtime)\nsubdirectory_target(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR} \"projects/ulr\")\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_ulr_apps)\n\nadd_subdirectory(ulr/)\nadd_subdirectory(ulrv2/)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/ulr/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_ulr_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tsibr_view\n\tsibr_assets\n\tsibr_ulr\n\tsibr_graphics\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/ulr/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"ulr\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/ulr/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <projects/ulr/renderer/ULRView.hpp>\n#include <core/scene/BasicIBRScene.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n\n#define PROGRAM_NAME \"sibr_ulr_app\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\t\"Usage: \" PROGRAM_NAME \" -path <dataset-path>\"    \t                                \"\\n\"\n\t;\n\n\n\nint main( int ac, char** av )\n{\n\t{\n\n\t\t// Parse Commad-line Args\n\t\tCommandLineArgs::parseMainArgs(ac, av);\n\t\tBasicIBRAppArgs myArgs;\n\t\t\n\t\tconst bool doVSync = !myArgs.vsync;\n\t\t// rendering size\n\t\tuint rendering_width = myArgs.rendering_size.get()[0];\n\t\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\t// window size\n\t\tuint win_width = myArgs.win_width;\n\t\tuint win_height = myArgs.win_height;\n\n\t\t// Window setup\n\t\tsibr::Window        window(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/ulr/\" + PROGRAM_NAME + \".ini\");\n\n\t\t// Setup IBR\n\t\tBasicIBRScene::Ptr\t\tscene(new BasicIBRScene(myArgs));\n\n\t\t// check rendering size\n\t\trendering_width = (rendering_width <= 0) ? scene->cameras()->inputCameras()[0]->w() : rendering_width;\n\t\trendering_height = (rendering_height <= 0) ? scene->cameras()->inputCameras()[0]->h() : rendering_height;\n\t\tVector2u usedResolution(rendering_width, rendering_height);\n\n\t\tconst unsigned int sceneResWidth = usedResolution.x();\n\t\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\t\tULRView::Ptr\tulrView(new ULRView(scene, sceneResWidth, sceneResHeight));\n\t\tulrView->setNumBlend(50, 50);\n\n\t\t// Raycaster.\n\t\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\t\traycaster->init();\n\t\traycaster->addMesh(scene->proxies()->proxy());\n\n\t\t// Camera handler for main view.\n\t\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\t\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster);\n\n\n\t\t// Add views to mvm.\n\t\tMultiViewManager        multiViewManager(window, false);\n\t\tmultiViewManager.addIBRSubView(\"ULR view\", ulrView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\t\tmultiViewManager.addCameraForView(\"ULR view\", generalCamera);\n\n\t\t// Top view\n\t\tconst std::shared_ptr<sibr::SceneDebugView>    topView(new sibr::SceneDebugView(scene, generalCamera, myArgs));\n\t\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\n\t\tif (myArgs.pathFile.get() !=  \"\" ) {\n\t\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"ULR view\"), \"ulr\");\n\t\t\tif( !myArgs.noExit )\n\t\t\t\texit(0);\n\t\t}\n\n\t\twhile (window.isOpened())\n\t\t{\n\t\t\tsibr::Input::poll();\n\t\t\twindow.makeContextCurrent();\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape))\n\t\t\t\twindow.close();\n\n\t\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\t\tmultiViewManager.onRender(window);\n\t\t\twindow.swapBuffer();\n\t\t\tCHECK_GL_ERROR\n\t\t}\n\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/ulr/resources/sibr_ulr_app.ini",
    "content": "\n[Window][Camera ULR view]\nPos=50,50\nSize=900,300\nCollapsed=0\n\n[Window][Top view settings]\nPos=950,50\nSize=450,300\nCollapsed=0\n\n[Window][Metrics##0]\nPos=1400,50\nSize=450,300\nCollapsed=0\n\n[Window][ULR view]\nPos=50,350\nSize=900,600\nCollapsed=0\n\n[Window][Top view]\nPos=950,350\nSize=900,600\nCollapsed=0\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/ulrv2/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\n\nproject(SIBR_ulrv2_app)\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB RESOURCES \"resources/*.ini\")\nsource_group(\"Resources Files\" FILES ${RESOURCES})\n\nadd_executable(${PROJECT_NAME} ${SOURCES})\ntarget_link_libraries(${PROJECT_NAME}\n\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tOpenMP::OpenMP_CXX\n\tsibr_view\n\tsibr_assets\n\tsibr_ulr\n\tsibr_renderer\n\tsibr_graphics\n)\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/ulr/apps\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tRESOURCES  \t${RESOURCES}\n\tRSC_FOLDER \t\"ulr\"\n    STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/ulrv2/main.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <fstream>\n\n#include <core/graphics/Window.hpp>\n#include <core/view/MultiViewManager.hpp>\n#include <core/system/String.hpp>\n\n#include \"projects/ulr/renderer/ULRView.hpp\"\n#include <projects/ulr/renderer/ULRV2View.hpp>\n#include <projects/ulr/renderer/ULRV3View.hpp>\n\n#include <core/renderer/DepthRenderer.hpp>\n#include <core/raycaster/Raycaster.hpp>\n#include <core/view/SceneDebugView.hpp>\n\n#define PROGRAM_NAME \"sibr_ulrv2_app\"\nusing namespace sibr;\n\nconst char* usage = \"\"\n\"Usage: \" PROGRAM_NAME \" -path <dataset-path>\"    \t                                \"\\n\"\n;\n\n\nint legacyV1main(ULRAppArgs & myArgs);\nint legacyV2main(ULRAppArgs & myArgs);\n\n\nint main(int ac, char** av) {\n\n\t// Parse Command-line Args\n\tCommandLineArgs::parseMainArgs(ac, av);\n\tULRAppArgs myArgs;\n\tmyArgs.displayHelpIfRequired();\n\n\tif (myArgs.version == 2) {\n\t\treturn legacyV2main(myArgs);\n\t}\n\tif (myArgs.version == 1) {\n\t\treturn legacyV1main(myArgs);\n\t}\n\n\tconst bool doVSync = !myArgs.vsync;\n\t// rendering size\n\tuint rendering_width = myArgs.rendering_size.get()[0];\n\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\n\t// window size\n\tuint win_width = myArgs.win_width;\n\tuint win_height = myArgs.win_height;\n\n\n\t// Window setup\n\tsibr::Window\t\twindow(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs, getResourcesDirectory() + \"/ulr/\" + PROGRAM_NAME + \".ini\");\n\n\tBasicIBRScene::Ptr\t\tscene(new BasicIBRScene(myArgs, true));\n\n\t// Setup the scene: load the proxy, create the texture arrays.\n\tconst uint flags = SIBR_GPU_LINEAR_SAMPLING | SIBR_FLIP_TEXTURE;\n\n\t// Fix rendering aspect ratio if user provided rendering size\n\tuint scene_width = scene->cameras()->inputCameras()[0]->w();\n\tuint scene_height = scene->cameras()->inputCameras()[0]->h();\n\tfloat scene_aspect_ratio = scene_width * 1.0f / scene_height;\n\tfloat rendering_aspect_ratio = rendering_width * 1.0f / rendering_height;\n\n\tif ((rendering_width > 0) && !myArgs.force_aspect_ratio ) {\n\t\tif (abs(scene_aspect_ratio - rendering_aspect_ratio) > 0.001f) {\n\t\t\tif (scene_width > scene_height) {\n\t\t\t\trendering_height = rendering_width / scene_aspect_ratio;\n\t\t\t}\n\t\t\telse {\n\t\t\t\trendering_width = rendering_height * scene_aspect_ratio;\n\t\t\t}\n\t\t}\n\t}\n\n\t\n\t// check rendering size\n\trendering_width = (rendering_width <= 0) ? scene->cameras()->inputCameras()[0]->w() : rendering_width;\n\trendering_height = (rendering_height <= 0) ? scene->cameras()->inputCameras()[0]->h() : rendering_height;\n\tVector2u usedResolution(rendering_width, rendering_height);\n\tstd::cerr << \" USED RES \" << usedResolution << \" scene w h \" << scene_width << \" : \" << scene_height <<  \n\t\t \" NAME \" << scene->cameras()->inputCameras()[0]->name() << std::endl;\n\n\tconst unsigned int sceneResWidth = usedResolution.x();\n\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\t\n\tscene->renderTargets()->initRGBandDepthTextureArrays(scene->cameras(), scene->images(), scene->proxies(), flags, true, myArgs.force_aspect_ratio);\n\n\t// Create the ULR view.\n\tULRV3View::Ptr\tulrView(new ULRV3View(scene, sceneResWidth, sceneResHeight));\n\n\t// Check if masks are provided and enabled.\n\tif (myArgs.masks) {\n\t\tif (!myArgs.maskParams.get().empty()) {\n\t\t\tif (!myArgs.maskParamsExtra.get().empty()) {\n\t\t\t\tulrView->getULRrenderer()->loadMasks(scene, myArgs.maskParams.get(), \"\", myArgs.maskParamsExtra.get());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tulrView->getULRrenderer()->loadMasks(scene, myArgs.maskParams.get(), \"\", \".png\");\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tulrView->getULRrenderer()->loadMasks(scene);\n\t\t}\n\t\tulrView->getULRrenderer()->useMasks() = true;\n\t}\n\n\t// Raycaster.\n\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\traycaster->init();\n\traycaster->addMesh(scene->proxies()->proxy());\n\n\t// Camera handler for main view.\n\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster);\n\n\t// Add views to mvm.\n\tMultiViewManager        multiViewManager(window, false);\n\tmultiViewManager.addIBRSubView(\"ULR view\", ulrView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\tmultiViewManager.addCameraForView(\"ULR view\", generalCamera);\n\n\tCHECK_GL_ERROR;\n\n\tif (myArgs.offscreen || myArgs.pathFile.get() !=  \"\" ) {\n\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"ULR view\"), \"\");\n\t\tif( !myArgs.noExit )\n\t\t\texit(0);\n\t}\n\n\t// Top view\n\tconst std::shared_ptr<sibr::SceneDebugView> topView(new sibr::SceneDebugView(scene, generalCamera, myArgs));\n\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\n\tCHECK_GL_ERROR;\n\n\t// Main looooooop.\n\twhile (window.isOpened()) {\n\n\t\tsibr::Input::poll();\n\t\twindow.makeContextCurrent();\n\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape)) {\n\t\t\twindow.close();\n\t\t}\n\n\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\tmultiViewManager.onRender(window);\n\n\t\twindow.swapBuffer();\n\t\tCHECK_GL_ERROR;\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n\n/// Use ULRV2 view and renderer.\nint legacyV2main(ULRAppArgs & myArgs)\n{\n\n\t{\n\n\t\tconst bool doVSync = !myArgs.vsync;\n\t\t// rendering size\n\t\tuint rendering_width = myArgs.rendering_size.get()[0];\n\t\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\t// window size\n\t\tuint win_width = myArgs.win_width;\n\t\tuint win_height = myArgs.win_height;\n\n\t\t// Window setup\n\t\tsibr::Window\t\twindow(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs);\n\n\t\tBasicIBRScene::Ptr\t\tscene(new BasicIBRScene(myArgs));\n\t\t\n\n\t\t// check rendering size\n\t\trendering_width = (rendering_width <= 0) ? scene->cameras()->inputCameras()[0]->w() : rendering_width;\n\t\trendering_height = (rendering_height <= 0) ? scene->cameras()->inputCameras()[0]->h() : rendering_height;\n\t\tVector2u usedResolution(rendering_width, rendering_height);\n\n\t\tconst unsigned int sceneResWidth = usedResolution.x();\n\t\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\t\tULRV2View::Ptr\tulrView(new ULRV2View(scene, sceneResWidth, sceneResHeight));\n\t\tulrView->setNumBlend(40, 40);\n\n\t\t// Raycaster.\n\t\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\t\traycaster->init();\n\t\traycaster->addMesh(scene->proxies()->proxy());\n\n\t\t// Camera handler for main view.\n\t\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\t\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster);\n\n\n\t\t// Add views to mvm.\n\t\tMultiViewManager        multiViewManager(window, false);\n\t\tmultiViewManager.addIBRSubView(\"ULR view\", ulrView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\t\tmultiViewManager.addCameraForView(\"ULR view\", generalCamera);\n\n\t\t// Top view\n\t\tconst std::shared_ptr<sibr::SceneDebugView>    topView(new sibr::SceneDebugView(scene, generalCamera, myArgs));\n\t\tmultiViewManager.addSubView(\"Top view\", topView, usedResolution);\n\n\t\t// Soft Visibility masks\n\t\tstd::vector<sibr::ImageL32F>\tdepths3D(scene->cameras()->inputCameras().size());\n\t\tif (myArgs.softVisibility) {\n\n\t\t\tint numImages = (int)scene->cameras()->inputCameras().size();\n\n\t\t\tfor (int imId = 0; imId < numImages; ++imId) {\n\n\t\t\t\tsibr::InputCamera cam = *scene->cameras()->inputCameras()[imId];\n\t\t\t\tsibr::Vector3f camPos = cam.position();\n\t\t\t\tint w = cam.w();\n\t\t\t\tint h = cam.h();\n\n\t\t\t\tsibr::DepthRenderer rendererDepth(cam.w(), cam.h());\n\n\t\t\t\tsibr::ImageL32F depthMapSIBR(w, h);\n\n\t\t\t\trendererDepth.render(cam, scene->proxies()->proxy());\n\t\t\t\trendererDepth._depth_RT->readBack(depthMapSIBR);\n\n\t\t\t\tdepths3D[imId] = sibr::ImageL32F(w, h, 0);\n\n\t\t\t\tfor (int i = 0; i < w; i++) {\n\t\t\t\t\tfor (int j = 0; j < h; j++) {\n\t\t\t\t\t\tsibr::Vector2i pixelPos(i, j);\n\t\t\t\t\t\tsibr::Vector3f pos3dMesh(cam.unprojectImgSpaceInvertY(pixelPos, depthMapSIBR(i, j).x()));\n\t\t\t\t\t\tdepths3D[imId](i, j).x() = (camPos - pos3dMesh).norm();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//showFloat(depthMapSIBR);\n\t\t\t\t//showFloat(depths3D[imId]);\n\t\t\t}\n\t\t}\n\n\n\t\tif (myArgs.masks) {\n\t\t\tif (!myArgs.maskParams.get().empty()) {\n\t\t\t\tulrView->loadMasks(scene, usedResolution.x(), usedResolution.y(), myArgs.maskParams.get(), \"\", \".png\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tulrView->loadMasks(scene, usedResolution.x(), usedResolution.y());\n\t\t\t}\n\n\t\t}\n\t\tif (myArgs.invert) {\n\t\t\tulrView->_ulr->setDoInvertMasks(true);\n\t\t}\n\t\tif (myArgs.alphas) {\n\t\t\tulrView->_ulr->setAreMasksBinary(false);\n\t\t}\n\t\tif (myArgs.poisson) {\n\t\t\tulrView->noPoissonBlend(true);\n\t\t}\n\n\t\tTexture2DArrayLum32F\tsoft_visibility_textures;\n\t\tif (myArgs.softVisibility) {\n\t\t\tint numImages = (int)scene->cameras()->inputCameras().size();\n\t\t\tstd::vector<sibr::ImageL32F>\tsoftVisibilities(numImages);\n\n\t\t\tint wSoft = depths3D[0].w();\n\t\t\tint hSoft = depths3D[0].h();\n#pragma omp parallel for\n\t\t\tfor (int imId = 0; imId < numImages; ++imId) {\n\n\t\t\t\tsibr::ImageRGBA tempVisibility;\n\t\t\t\tulrView->computeVisibilityMap(depths3D[imId], tempVisibility);\n\t\t\t\tsoftVisibilities[imId] = std::move(sibr::convertRGBAtoL32F(tempVisibility));\n\t\t\t\tcv::Mat temp;\n\t\t\t\tcv::resize(softVisibilities[imId].toOpenCV(), temp, cv::Size(wSoft, hSoft), 0, 0, cv::INTER_NEAREST);\n\t\t\t\tsoftVisibilities[imId].fromOpenCV(temp);\n\t\t\t}\n\n\t\t\tsoft_visibility_textures.createFromImages(softVisibilities, SIBR_GPU_LINEAR_SAMPLING | SIBR_FLIP_TEXTURE);\n\n\t\t\tulrView->_ulr->getSoftVisibilityMaps() = &soft_visibility_textures;\n\t\t\tulrView->_ulr->getSoftVisibilityThreshold() = 20.0f;\n\t\t\t//ulrView->noPoissonBlend(true);\n\n\t\t}\n\n\t\tif (myArgs.pathFile.get() !=  \"\" ) {\n\t\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"ULR view\"), \"ulr\");\n\t\t\tif( !myArgs.noExit )\n\t\t\t\texit(0);\n\t\t}\n\n\t\tCHECK_GL_ERROR;\n\t\twhile (window.isOpened())\n\t\t{\n\n\n\t\t\tsibr::Input::poll();\n\t\t\twindow.makeContextCurrent();\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape))\n\t\t\t\twindow.close();\n\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Z)) {\n\t\t\t\tif (ulrView->_ulr->getSoftVisibilityMaps()) {\n\t\t\t\t\tstd::cout << \" disabling soft visibility\" << std::endl;\n\t\t\t\t\tulrView->_ulr->getSoftVisibilityMaps() = nullptr;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tstd::cout << \" enabling soft visibility\" << std::endl;\n\t\t\t\t\tulrView->_ulr->getSoftVisibilityMaps() = &soft_visibility_textures;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\t\tmultiViewManager.onRender(window);\n\t\t\twindow.swapBuffer();\n\t\t\tCHECK_GL_ERROR;\n\t\t}\n\t}\n\n\n\treturn EXIT_SUCCESS;\n}\n\n/// Use ULRV1 view and renderer.\nint legacyV1main(ULRAppArgs & myArgs)\n{\n\n\t{\n\n\t\tconst bool doVSync = !myArgs.vsync;\n\t\t// rendering size\n\t\tuint rendering_width = myArgs.rendering_size.get()[0];\n\t\tuint rendering_height = myArgs.rendering_size.get()[1];\n\t\t// window size\n\t\tuint win_width = myArgs.win_width;\n\t\tuint win_height = myArgs.win_height;\n\n\t\t// Window setup\n\t\tsibr::Window        window(PROGRAM_NAME, sibr::Vector2i(50, 50), myArgs);\n\n\t\t// Setup IBR\n\t\tBasicIBRScene::Ptr\t\tscene(new BasicIBRScene(myArgs));\n\t\t\n\t\t// check rendering size\n\t\trendering_width = (rendering_width <= 0) ? scene->cameras()->inputCameras()[0]->w() : rendering_width;\n\t\trendering_height = (rendering_height <= 0) ? scene->cameras()->inputCameras()[0]->h() : rendering_height;\n\t\tVector2u usedResolution(rendering_width, rendering_height);\n\n\t\tconst unsigned int sceneResWidth = usedResolution.x();\n\t\tconst unsigned int sceneResHeight = usedResolution.y();\n\n\t\tULRView::Ptr\tulrView(new ULRView(scene, sceneResWidth, sceneResHeight));\n\t\tulrView->setNumBlend(50, 50);\n\n\t\t// Raycaster.\n\t\tstd::shared_ptr<sibr::Raycaster> raycaster = std::make_shared<sibr::Raycaster>();\n\t\traycaster->init();\n\t\traycaster->addMesh(scene->proxies()->proxy());\n\n\t\t// Camera handler for main view.\n\t\tsibr::InteractiveCameraHandler::Ptr generalCamera(new InteractiveCameraHandler());\n\t\tgeneralCamera->setup(scene->cameras()->inputCameras(), Viewport(0, 0, (float)usedResolution.x(), (float)usedResolution.y()), raycaster);\n\n\n\t\t// Add views to mvm.\n\t\tMultiViewManager        multiViewManager(window, false);\n\t\tmultiViewManager.addIBRSubView(\"ULR view\", ulrView, usedResolution, ImGuiWindowFlags_ResizeFromAnySide);\n\t\tmultiViewManager.addCameraForView(\"ULR view\", generalCamera);\n\n\t\t// Top view\n\t\tconst std::shared_ptr<sibr::SceneDebugView>    topView(new sibr::SceneDebugView(scene, generalCamera, myArgs));\n\t\tmultiViewManager.addSubView(\"Top view\", topView);\n\n\t\tif (myArgs.pathFile.get() !=  \"\" ) {\n\t\t\tgeneralCamera->getCameraRecorder().loadPath(myArgs.pathFile.get(), usedResolution.x(), usedResolution.y());\n\t\t\tgeneralCamera->getCameraRecorder().recordOfflinePath(myArgs.outPath, multiViewManager.getIBRSubView(\"ULR view\"), \"ulr\");\n\t\t\tif( !myArgs.noExit )\n\t\t\t\texit(0);\n\t\t}\n\n\n\t\twhile (window.isOpened())\n\t\t{\n\t\t\tsibr::Input::poll();\n\t\t\twindow.makeContextCurrent();\n\t\t\tif (sibr::Input::global().key().isPressed(sibr::Key::Escape))\n\t\t\t\twindow.close();\n\n\t\t\tmultiViewManager.onUpdate(sibr::Input::global());\n\t\t\tmultiViewManager.onRender(window);\n\t\t\twindow.swapBuffer();\n\t\t\tCHECK_GL_ERROR\n\t\t}\n\n\t}\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/apps/ulrv2/resources/sibr_ulrv2_app.ini",
    "content": "\n[Window][ULRV2 Settings (ULR view)]\nPos=50,50\nSize=350,300\nCollapsed=0\n\n[Window][ULRV3 Settings (ULR view)]\nPos=50,50\nSize=350,300\nCollapsed=0\n\n[Window][Camera ULR view]\nPos=400,50\nSize=550,300\nCollapsed=0\n\n[Window][Top view settings]\nPos=950,50\nSize=450,300\nCollapsed=0\n\n[Window][Metrics##0]\nPos=1400,50\nSize=450,300\nCollapsed=0\n\n[Window][ULR view]\nPos=50,350\nSize=900,600\nCollapsed=0\n\n[Window][Top view]\nPos=950,350\nSize=900,600\nCollapsed=0\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/documentation/IBR_ULR.dox",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/*!\n@page ulrPage Unstructured Lumigraph Rendering (ULR)\n\n\\section ulr_intro Introduction\n\nThis *Project* contains three specialized implementations of Unstructured Lumigraph Rendering \\[Buelher 2001\\]; we refer the reader to that paper for technical details. In contrast to the original paper, our method blends input images on a *per-pixel* basis. In a nutshell the rendering apps in this *Project* first render a depth buffer for the novel (current) view, then for a subset *S* of input images reproject each visible 3D point into each image in *S*, and then blend the result according to the blending weights as defined in the original paper.\n\nThis *Project* contains two renderings apps: `SIBR_ulr_app` which is the original (slow) version, `SIBR_ulrv2_app` that contains the second and third versions of the ULR.\n\nThe original version of ULR first selects a subset of the original images using angle and position criteria on the CPU, then uses a multi-pass ping-pong shader to rank the 4 best-cost images for each pixel, by storing these four color values and their weights in rendertargets and iteratively updating them while keeping them sorted. \n\nVersion 2 removes the need for the ping-pong best-cost image update, by iterating on the selected cameras in one shader. The corresponding images and depth maps are passed as a list of textures. The maximum supported number of cameras is determined by the maximum texture slot count in fragment shaders.\n\nVersion 3 stores images and depth maps as texture 2D arrays, and pack all cameras in a uniform buffer object. This allows all cameras to be considered and do the selection entirely on the GPU. The maximum supported number of cameras is determined by the maximum texture array layer count and uniform buffer object size.\n\n\\subsection ulr_authors Authors\n\nThis *Project* was written by: Gaurav Chaurasia, Sebastien Bonopera, Theo Thonat, Simon Rodriguez, Sebastien Bonopera, Jerome Esnault, Siddhant Prakashand George Drettakis, who also supervised the entire *Project*.\n\n<hr>\n\n\\section ulr_howToUse How to use\n\n\\subsection ulr_binary Use the binary distribution\n\nThe easiest way to use *SIBR* to run ULR is to download the binary distribution. All steps described below, including all preprocessing for your datasets will work using this code.\nDownload the distribution from the page: https://sibr.gitlabpages.inria.fr/download.html (Core, 57Mb); unzip the file and rename the directory \"install\".\n\n\n\\subsection ulr_howToUse_checkout Checkout the code\n\nULR is already available as part of the SIBR Core code. You will need to checkout SIBR Core as mentioned in \\ref sibr_checkout .\n\n\\subsection ulr_howToUse_configure Configuration\n\nAs for most of the projects, ULR can be configured through SIBR Core CMake configuration by selecting `SIBR_IBR_ULR` variable before running the configuration (see \\ref sibr_configure_cmake).\n\n\\subsection ulr_howToUse_build Build & Install\n\nYou can build and install ULR via running ALL_BUILD and/or INSTALL in sibr_projects.sln solution (as mentioned in \\ref sibr_compile Compiling\n) or through `sibr_ulr*` specific targets in sibr_projects.sln solution.\nDont forget to build INSTALL if you use ALL_BUILD.\n\n\\subsection ulr_howToUse_run Run\n\nAfter installing ULR, several apps should be available in `install\\bin`, notably :\n\n- sibr_ulr_app.exe (or sibr_ulr_app_d.exe / sibr_ulr_app_rwdi.exe depending on the configuration of the target) : this is the legacy version of ULR\n- sibr_ulrv2_app.exe (or sibr_ulrv2_app_d.exe / sibr_ulrv2_app_rwdi.exe depending on the configuration of the target) : this updated version gives you the choice between three different implementations of ULR (with GPU optimization and other tweaks)\n\nBoth can be run by running the executable with a path to a working dataset:\n\n\tsibr_ulrv2_app.exe --path PATH_TO_DATASET\n\nOur interactive viewer has a main view running the algorithm and a top view to visualize the position of the calibrated cameras. By default you are in WASD mode, and can toggle to trackball using the \"y\" key. Please see the page [Interface](https://sibr.gitlabpages.inria.fr/docs/develop/howto_sibr_useful_objects.html) for more details on the interface.\n\nFor example datasets see below \\ref ulr_howToUse_example_datasets.\n\n\\subsection Playing paths from the command line\n\nPaths can be played by the ulr renderers by running the renderer in offscreen mode:\n```\nSIBR_ulrv2_app.exe --path PATH_TO_DATASET --offscreen --pathFile path.(out|lookat|tst|path) [--outPath optionalOutputPath --noExit]\n```\nBy default, the application exits when this operation is performed. This is the easiest way to compare algorithms, although interactive options exist for some *Projects*.\n\n<hr>\n\n\\subsection ulr_howToUse_dataset Datasets\n\n\\subsubsection ulr_howToUse_dataset_structure Dataset structure\n\nA ULR dataset only requires standard SfM/MVS data to function: to generate such a dataset from your input images see:\n\n\\ref howto_generate_dataset\n\nA standard SIBR dataset contains *cameras* and the *mesh* required for the algorithm to run; no additional preprocessing is required.\n\n\\subsubsection ulr_howToUse_example_datasets Example Datasets\n\nSome example datasets can be found here:\n\thttps://repo-sam.inria.fr/fungraph/sibr-datasets/datasets.html\n\nYou dan download the ULR only package for each dataset. \nFeel free to download and experiment, for example with (now famous) Museum Front 27 dataset. Goto the install\\bin directory:\n\n```\n\twget https://repo-sam.inria.fr/fungraph/sibr-datasets/museum_front27_ulr.zip\n\tsibr_ulrv2_app.exe --path museum_front27\\sibr_cm\n```\n\n<hr>\n\n\\subsubsection ulr_howToUse_run_cliOptions CLI options\n\n | name                 | type      | Required  | default value             | description                                   |\n | -------------------- | --------- | --------- | ------------------------- | --------------------------------------------- |\n | **Basic app options**            |||||\n | appPath              | string    | false     |   \"./\"                    | define a custom app path                      |\n | help                 | bool      | false     |   false                   | display this help message                     |\n | **Basic window options**         |||||\n | width                | int       | false     |   720                     | initial window width                          |\n | height               | int       | false     |   480                     | initial window height                         |\n | vsync                | int       | false     |   1                       | enable vertical sync                          |\n | fullscreen           | bool      | false     |   false                   | set the window to fullscreen                  |\n | hd                   | bool      | false     |   false                   | rescale UI elements for high-density screens  |\n | nogui                | bool      | false     |   false                   | do not use ImGui                              |\n | gldebug              | bool      | false     |   false                   | enable OpenGL error callback                  |\n | **Basic rendering options**      |||||\n | scene                | string    | false     |   \"scene_metadata.txt\"    | scene metadata file                           |\n | rendering-size       | Vector2i  | false     |   { 0, 0 }                | size at which rendering is performed          |\n | texture-width        | int       | false     |   0                       | size of the input data in memory              |\n | texture-ratio        | float     | false     |   1.0f                    |                                               |\n | rendering-mode       | int       | false     |   RENDERMODE_MONO         | select mono (0) or stereo (1) rendering mode  |\n | focal-pt             | Vector3f  | false     |   { 0.0f, 0.0f, 0.0f }    |                                               |\n | colmap_fovXfovY_flag | Switch    | false     |   false                   |                                               |\n | **Basic dataset options**        |||||\n | path                 | string    | true      |                           | path to the dataset root                      |\n | dataset_type         | string    | false     |   \"\"                      | type of dataset                               |\n | **ULR specific options**         |||||\n | v                    | int       | false     |   3                       | ULR implementation version                    |\n | soft-visibility      | bool      | false     |   false                   | generate and use soft visibility masks        |\n | masks                | bool      | false     |   false                   | use binary masks                              |\n | masks-param          | string    | false     |   \"\"                      |                                               |\n | masks-param-extra    | string    | false     |   \"\"                      |                                               |\n | invert               | bool      | false     |   false                   | invert the masks                              |\n | alphas               | bool      | false     |   false                   |                                               |\n | poisson-blend        | bool      | false     |   false                   | apply Poisson-filling to the ULR result       |\n\n\n\\subsection ulr_references References\n\\[Buehler 2001\\] C. Buehler, M. Bosse, L. McMillan, S. Gortler, and M. Cohen. \"Unstructured lumigraph\nrendering.\" In Proceedings SIGGRAPH 2001, pp. 425-432. ACM, 2001. https://www.ics.uci.edu/~gopi/ICS280Win02/UnstructuredLumigraph.pdf\n*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/documentation/ulr_doc.cmake",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nset(PROJECT_PAGE \"ulrPage\")\nset(PROJECT_LINK \"https://gitlab.inria.fr/sibr/sibr_core\")\nset(PROJECT_TYPE \"SAMPLES\")"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/CMakeLists.txt",
    "content": "# Copyright (C) 2020, Inria\n# GRAPHDECO research group, https://team.inria.fr/graphdeco\n# All rights reserved.\n# \n# This software is free for non-commercial, research and evaluation use \n# under the terms of the LICENSE.md file.\n# \n# For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n\n\nset(SIBR_PROJECT \"ulr\")\nproject(sibr_${SIBR_PROJECT})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\")\nsource_group(\"Source Files\" FILES ${SOURCES})\n\nfile(GLOB SHADERS \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\nsource_group(\"Source Files\\\\shaders\" FILES ${SHADERS})\n\nfile(GLOB SOURCES \"*.cpp\" \"*.h\" \"*.hpp\" \"shaders/*.frag\" \"shaders/*.vert\" \"shaders/*.geom\")\n\n\n## Specify target rules\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ninclude_directories(${Boost_INCLUDE_DIRS} .)\nif(WIN32)\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\tglfw3\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n)\nelse()\ntarget_link_libraries(${PROJECT_NAME}\n\t${Boost_LIBRARIES}\n\t${ASSIMP_LIBRARIES}\n\t${GLEW_LIBRARIES}\n\t${OPENGL_LIBRARIES}\n\t${OpenCV_LIBRARIES}\n\t${GLFW_LIBRARY}\n\tsibr_system\n\tsibr_view\n\tsibr_assets\n\tsibr_renderer\n)\nendif\n\nadd_definitions( -DSIBR_EXP_ULR_EXPORTS -DBOOST_ALL_DYN_LINK  )\n\nset_target_properties(${PROJECT_NAME} PROPERTIES FOLDER \"projects/${SIBR_PROJECT}/renderer\")\n\n## High level macro to install in an homogen way all our ibr targets\ninclude(install_runtime)\nibr_install_target(${PROJECT_NAME}\n    INSTALL_PDB                         ## mean install also MSVC IDE *.pdb file (DEST according to target type)\n\tSHADERS ${SHADERS}\n\tRSC_FOLDER ${SIBR_PROJECT}\n\n    #STANDALONE  ${INSTALL_STANDALONE}   ## mean call install_runtime with bundle dependencies resolution\n    COMPONENT   ${PROJECT_NAME}_install ## will create custom target to install only this project\n)\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/Config.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include <core/system/Config.hpp>\n# include <core/system/CommandLineArgs.hpp>\n\n# ifdef SIBR_OS_WINDOWS\n#  ifdef SIBR_STATIC_DEFINE\n#    define SIBR_EXPORT\n#    define SIBR_NO_EXPORT\n#  else\n#    ifndef SIBR_EXP_ULR_EXPORT\n#      ifdef SIBR_EXP_ULR_EXPORTS\n/* We are building this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllexport)\n#      else\n/* We are using this library */\n#        define SIBR_EXP_ULR_EXPORT __declspec(dllimport)\n#      endif\n#    endif\n#    ifndef SIBR_NO_EXPORT\n#      define SIBR_NO_EXPORT\n#    endif\n#  endif\n# else\n#  define SIBR_EXP_ULR_EXPORT\n# endif\n\nnamespace sibr {\n\n\t/// Arguments for all ULR applications.\n\tstruct ULRAppArgs :\n\t\tvirtual BasicIBRAppArgs {\n\t\tArg<int> version = { \"v\", 3, \"ULR implementation version\" };\n\t\tArgSwitch softVisibility = { \"soft-visibility\", false, \"generate and use soft visibility masks\" };\n\t\tArg<bool> masks = { \"masks\" , \"use binary masks\" };\n\t\tArg<std::string> maskParams = { \"masks-param\" , \"\" };\n\t\tArg<std::string> maskParamsExtra = { \"masks-param-extra\" , \"\" };\n\t\tArg<bool> invert = { \"invert\", \"invert the masks\" };\n\t\tArg<bool> alphas = { \"alphas\", \"\" };\n\t\tArg<bool> poisson = { \"poisson-blend\", \"apply Poisson-filling to the ULR result\" };\n\t};\n\n}\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRRenderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n# include \"Config.hpp\"\n# include <core/assets/Resources.hpp>\n# include <projects/ulr/renderer/ULRRenderer.hpp>\n\nnamespace sibr { \nULRRenderer::ULRRenderer(const uint w, const uint h)\n{\n\t\n    std::cerr << \"\\n[ULRenderer] initializing\" << std::endl;\n    std::cerr << \"\\n[ULRenderer] loading shaders\" << std::endl;\n    _ulrShaderPass1 .init(\"ULR1\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr1.frag\"));\n    _ulrShaderPass2 .init(\"ULR2\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr2.frag\"));\n    _depthShader.init(\"Depth\",\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr_intersect.vert\"),\n\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr_intersect.frag\"));\n\n    _ulrShaderPass1_nCamPos .init(_ulrShaderPass1, \"nCamPos\");\n    _ulrShaderPass1_iCamPos .init(_ulrShaderPass1, \"iCamPos\");\n    _ulrShaderPass1_iCamDir .init(_ulrShaderPass1, \"iCamDir\");\n    _ulrShaderPass1_iCamProj.init(_ulrShaderPass1, \"iCamProj\");\n    _ulrShaderPass1_occlTest .init(_ulrShaderPass1, \"occlTest\");\n\t_ulrShaderPass1_masking .init(_ulrShaderPass1, \"doMasking\");\n    _depthShader_proj.init(_depthShader,\"proj\");\n\n    std::cerr << \"\\n[ULRenderer] creating render targets\" << std::endl;\n\n    _ulr0_RT .reset(new sibr::RenderTargetRGBA32F(w,h,0,4));\n    _ulr1_RT .reset(new sibr::RenderTargetRGBA32F(w,h,0,4));\n    _depth_RT.reset(new sibr::RenderTargetRGBA32F(w,h));\n\n\t_doOccl = true;\n}\n\nvoid\nULRRenderer::process(std::vector<uint>& imgs_ulr, const sibr::Camera& eye,\n\t\tconst sibr::BasicIBRScene::Ptr scene,\n\t\tstd::shared_ptr<sibr::Mesh>& altMesh,\n\t\tconst std::vector<std::shared_ptr<RenderTargetRGBA32F> >& inputRTs,\n\t\tIRenderTarget& dst)\n{\n\t// Get a new camera with z_near ~ 0\n\tsibr::Camera new_cam = eye;\n\tnew_cam.znear( 0.001f );\n\n    // render geometry to depth map\n\n\tglViewport(0,0, _depth_RT->w(), _depth_RT->h());\n    _depth_RT->clear();\n    _depth_RT->bind();\n\n    _depthShader.begin();\n    _depthShader_proj.set(new_cam.viewproj());\n\n\tglClear(GL_DEPTH_BUFFER_BIT);\n\n\tif( altMesh != nullptr )\n\t\taltMesh->render( true, true); // enable depth test - disable back culling\n\telse\n\t\tscene->proxies()->proxy().render( true, true); // enable depth test - disable back culling\n\n    _depthShader.end();\n    _depth_RT->unbind();\n\n    // ULR pass 1\n    _ulr0_RT->clear(sibr::Vector4f(0,0,0,1e5));\n    _ulr1_RT->clear(sibr::Vector4f(0,0,0,1e5));\n    for (uint i=0; i<imgs_ulr.size(); i++) {\n        if (scene->cameras()->inputCameras()[imgs_ulr[i]]->isActive()) {\n\t\t\tconst sibr::InputCamera& cam = *scene->cameras()->inputCameras()[imgs_ulr[i]];\n            std::swap(_ulr0_RT, _ulr1_RT);\n            _ulrShaderPass1.begin();\n            _ulr0_RT->bind();\n            glViewport(0,0, _ulr0_RT->w(), _ulr0_RT->h());\n            glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, inputRTs[imgs_ulr[i]]->texture());\n            glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _depth_RT->texture());\n            glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, _ulr1_RT->texture(0));\n            glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, _ulr1_RT->texture(1));\n            glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_2D, _ulr1_RT->texture(2));\n            glActiveTexture(GL_TEXTURE5); glBindTexture(GL_TEXTURE_2D, _ulr1_RT->texture(3));\n\t\t\tif (useMasks()){\n\t\t\t\t\tglActiveTexture(GL_TEXTURE6);\n\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, getMasks()[imgs_ulr[i]]->texture());\n\t\t\t}\n\t\t\t_ulrShaderPass1_masking.set(useMasks());\n\t\t\t_ulrShaderPass1_nCamPos.set(eye.position());\n            _ulrShaderPass1_iCamPos.set(cam.position());\n            _ulrShaderPass1_iCamDir.set(cam.dir());\n            _ulrShaderPass1_iCamProj.set(cam.viewproj());\n            _ulrShaderPass1_occlTest.set(_doOccl);\n\t\t\tsibr::RenderUtility::renderScreenQuad();\n            _ulr0_RT->unbind();\n            _ulrShaderPass1.end();\n\n#if 0\n\t\t\t{\n\t\t\t\tsibr::ImageRGBA32F img2;\n\t\t\t\t_ulr0_RT->readBack(img2);\n\t\t\t\tshow(img2); // DEBUG\n\t\t\t}\n#endif\n        }\n    }\n\n    // ULR pass 2\n    // enable depth test to ensure depth of proxy is written to\n    // depth buffer by the shader\n//    glEnable(GL_DEPTH_TEST); /// \\todo TODO -- breaks with fences -- check\n    _ulrShaderPass2.begin();\n    dst.clear();\n    dst.bind();\n    glViewport(0,0, dst.w(), dst.h());\n    glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _depth_RT->texture());\n    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _ulr0_RT->texture(0));\n    glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, _ulr0_RT->texture(1));\n    glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, _ulr0_RT->texture(2));\n    glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_2D, _ulr0_RT->texture(3));\n\tsibr::RenderUtility::renderScreenQuad();\n    dst.unbind();\n    _ulrShaderPass2.end();\n\n#if 0\n\tsibr::ImageRGB img;\n\tdst.readBack(img);\n\tshow(img); // DEBUG\n#endif\n\n}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRRenderer.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"Config.hpp\"\r\n# include <core/system/Config.hpp>\r\n# include <core/graphics/Texture.hpp>\r\n# include <core/graphics/Shader.hpp>\r\n# include <core/graphics/Mesh.hpp>\r\n# include <core/renderer/RenderMaskHolder.hpp>\r\n# include <core/scene/BasicIBRScene.hpp>\r\n\r\nnamespace sibr { \r\n\r\n\t/** Legacy ULR renderer. Process each input image separately and accumulate them.\r\n\t **/\r\n\tclass SIBR_EXP_ULR_EXPORT ULRRenderer : public RenderMaskHolder\r\n\t{\r\n\t\tSIBR_CLASS_PTR(ULRRenderer);\r\n\r\n\t\t/** Constructor.\r\n\t\t *\\param w rendering width\r\n\t\t *\\param h rendering height\r\n\t\t */\r\n\t\tULRRenderer(const uint w, const uint h);\r\n\r\n\t\t/** Render.\r\n\t\t *\\param imgs_ulr vector of selected image IDs\r\n\t\t *\\param eye novel viewpoint\r\n\t\t *\\param scene the scene to render\r\n\t\t *\\param altMesh optional alternative mesh\r\n\t\t *\\param inputRTs the RGBD input images\r\n\t\t *\\param output destination target\r\n\t\t */\r\n\t\tvoid process(std::vector<uint>& imgs_ulr, const sibr::Camera& eye,\r\n\t\t\tconst sibr::BasicIBRScene::Ptr scene,\r\n\t\t\tstd::shared_ptr<sibr::Mesh>& altMesh,\r\n\t\t\tconst std::vector<std::shared_ptr<RenderTargetRGBA32F> >& inputRTs,\r\n\t\t\tIRenderTarget& output);\r\n\r\n\t\t/** Toggle occlusion testing.\r\n\t\t *\\param val should occlusion testing be performed\r\n\t\t */\r\n\t\tvoid doOccl(bool val) { _doOccl = val; }\r\n\r\n\tprivate:\r\n\t\tsibr::RenderTargetRGBA32F::Ptr _ulr0_RT;\r\n\t\tsibr::RenderTargetRGBA32F::Ptr _ulr1_RT;\r\n\t\tsibr::RenderTargetRGBA32F::Ptr _depth_RT;\r\n\r\n\t\tsibr::GLShader _ulrShaderPass1;\r\n\t\tsibr::GLShader _ulrShaderPass2;\r\n\t\tsibr::GLShader _depthShader;\r\n\r\n\t\tsibr::GLParameter _ulrShaderPass1_nCamPos;\r\n\t\tsibr::GLParameter _ulrShaderPass1_iCamPos;\r\n\t\tsibr::GLParameter _ulrShaderPass1_iCamDir;\r\n\t\tsibr::GLParameter _ulrShaderPass1_iCamProj;\r\n\t\tsibr::GLParameter _ulrShaderPass1_occlTest;\r\n\t\tsibr::GLParameter _ulrShaderPass1_masking;\r\n\t\tsibr::GLParameter _depthShader_proj;\r\n\r\n\t\tbool\t_doOccl;\r\n\r\n   };\r\n\r\n} /*namespace sibr*/\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV2Renderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n# include \"Config.hpp\"\n# include <core/assets/Resources.hpp>\n# include <map>\n# include \"ULRV2Renderer.hpp\"\n#include \"core/system/String.hpp\"\n\nnamespace sibr {\n\t\tULRV2Renderer::ULRV2Renderer(const std::vector<InputCamera::Ptr> & cameras, const uint w, const uint h, const unsigned int maxCams, const std::string & fShader, const std::string & vShader, const bool facecull)\n\t\t{\n\t\t\t\t\t\n\t\t\t// Count how many cameras are active in the scene.\n\t\t\tunsigned int numActiveCams = 0;\n\t\t\tfor (auto & cam : cameras) {\n\t\t\t\tif (cam->isActive()) {\n\t\t\t\t\t++numActiveCams;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_numCams = maxCams == 0 ? numActiveCams : std::min(maxCams, numActiveCams);\n\t\t\t\n\t\t\tsetupULRshader(fShader,vShader);\n\n\t\t\t_depthRT.reset(new sibr::RenderTargetRGBA32F(w, h));\n\n\t\t\t_doOccl = true;\n\t\t\t_areMasksBinary = true;\n\t\t\t_doInvertMasks = false;\n\t\t\t_discardBlackPixels = true;\n\t\t\t_shouldCull = facecull;\n\t\t\t_epsilonOcclusion = 1e-2f;\n\t\t\t_soft_visibility_threshold = 30.0f;\n\t\t\tsoft_visibility_maps = nullptr;\n\t\t}\n\n\t\tvoid ULRV2Renderer::setupULRshader(const std::string & fShader, const std::string & vShader)\n\t\t{\n\t\t\tstd::cerr << \"[ULRV2Renderer] Trying to initialize shaders for at most \" << _numCams << \" cameras.\" << std::endl;\n\t\t\t/// \\todo TODO SR: handle the case were we require more shader texture slots than we are allowed too.\n\t\t\t/// Seems to be around 90 on Quadro K4200. We can either do multiple passes (fi 40 cams per pass),\n\t\t\t/// or try to use texture arrays to avoid this problem.\n\t\t\t/// If this happens to you, lower the maximum number of cameras picked by the ulr algo.\n\n\t\t\tGLShader::Define::List defines;\n\t\t\tdefines.emplace_back(\"NUM_CAMS\", _numCams);\n\t\t\t_ulrShader.init(\"ULRV2\",\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"\") + \"/\" + vShader + \".vert\"),\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"\") + \"/\" + fShader + \".frag\", defines));\n\t\t\t_depthShader.init(\"ULRV2Depth\",\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr_intersect.vert\"),\n\t\t\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr_intersect.frag\", defines));\n\n\t\t\t_proj.init(_depthShader, \"proj\");\n\t\t\t_ncamPos.init(_ulrShader, \"ncam_pos\");\n\t\t\t_occTest.init(_ulrShader, \"occ_test\");\n\t\t\t_areMasksBinaryGL.init(_ulrShader, \"is_binary_mask\");\n\t\t\t_doInvertMasksGL.init(_ulrShader, \"invert_mask\");\n\t\t\t_discardBlackPixelsGL.init(_ulrShader, \"discard_black_pixels\");\n\t\t\t_doMask.init(_ulrShader, \"doMasking\");\n\t\t\t_camCount.init(_ulrShader, \"camsCount\");\n\t\t\t_use_soft_visibility.init(_ulrShader, \"useSoftVisibility\");\n\t\t\t_soft_visibility_threshold.init(_ulrShader, \"softVisibilityThreshold\");\n\t\t\t_epsilonOcclusion.init(_ulrShader, \"epsilonOcclusion\");\n\n\t\t\t_icamProj.resize(_numCams);\n\t\t\t_icamPos.resize(_numCams);\n\t\t\t_icamDir.resize(_numCams);\n\t\t\t_inputRGB.resize(_numCams);\n\t\t\t_masks.resize(_numCams);\n\t\t\t_selected_cams.resize(_numCams);\n\n\t\t\t_ulrShader.begin();\n\t\t\tfor (uint i = 0; i<(uint)_numCams; i++)\n\t\t\t{\n\t\t\t\t_icamProj[i].init(_ulrShader, sibr::sprint(\"icam_proj[%d]\", i));\n\t\t\t\t_icamPos[i].init(_ulrShader, sibr::sprint(\"icam_pos[%d]\", i));\n\t\t\t\t_icamDir[i].init(_ulrShader, sibr::sprint(\"icam_dir[%d]\", i));\n\t\t\t\t_selected_cams[i].init(_ulrShader, sibr::sprint(\"selected_cams[%d]\", i));\n\t\t\t\t_inputRGB[i].init(_ulrShader, sibr::sprint(\"input_rgb[%d]\", i));\n\t\t\t\t_inputRGB[i].set(i + 2);  // location 0 and 1 reserved.s\n\t\t\t\t_masks[i].init(_ulrShader, sibr::sprint(\"masks[%d]\", i));\n\t\t\t\t_masks[i].set(GLuint(_numCams + i + 2));\n\n\t\t\t}\n\t\t\t_ulrShader.end();\n\n\t\t}\n\t\t\n\t\tvoid\n\t\t\tULRV2Renderer::process(const std::vector<uint>& imgs_ulr, const sibr::Camera& eye,\n\t\t\t\tconst sibr::BasicIBRScene::Ptr& scene,\n\t\t\t\tstd::shared_ptr<sibr::Mesh>& altMesh,\n\t\t\t\tconst std::vector<std::shared_ptr<RenderTargetRGBA32F> >& inputRTs,\n\t\t\t\tIRenderTarget& dst)\n\t\t{\n\t\t\t// Get a new camera with z_near ~ 0\n\t\t\tsibr::Camera new_cam = eye;\n\t\t\t//new_cam.znear(0.001f);\n\n\n\t\t\tglViewport(0, 0, _depthRT->w(), _depthRT->h());\n\t\t\t_depthRT->bind();\n\t\t\tglClearColor(0, 0, 0, 1);\n\t\t\tglClearDepth(1.0);\n\t\t\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\t\t_depthShader.begin();\n\t\t\t_proj.set(new_cam.viewproj());\n\t\t\tif (altMesh != nullptr) {\n\t\t\t\taltMesh->render(true, _shouldCull); // enable depth test - disable back culling\n\t\t\t} else {\n\t\t\t\tscene->proxies()->proxy().render(true, _shouldCull);\n\t\t\t}\n\t\t\t_depthShader.end();\n\t\t\t_depthRT->unbind();\n\t\t\t\n\t\t\tglViewport(0, 0, dst.w(), dst.h());\n\t\t\tdst.clear();\n\t\t\tdst.bind();\n\n\t\t\t_ulrShader.begin();\n\t\t\t\n\t\t\t_ncamPos.set(eye.position());\n\t\t\t_occTest.set(_doOccl);\n\t\t\t_areMasksBinaryGL.set(_areMasksBinary);\n\t\t\t_doInvertMasksGL.set(_doInvertMasks);\n\t\t\t_discardBlackPixelsGL.set(_discardBlackPixels);\n\t\t\t_doMask.set(useMasks());\n\t\t\t_epsilonOcclusion.send();\n\n\t\t\tCHECK_GL_ERROR\n\n\t\t\t_use_soft_visibility.set(soft_visibility_maps != nullptr && soft_visibility_maps->handle());\n\n\t\t\tCHECK_GL_ERROR\n\n\t\t\tglActiveTexture(GL_TEXTURE0);\n\t\t\tglBindTexture(GL_TEXTURE_2D, _depthRT->texture());\n\n\t\t\tCHECK_GL_ERROR\n\t\t\t\n\t\t\tif (_use_soft_visibility) {\n\t\t\t\t//std::cout << \"using soft visib\" << std::endl;\n\t\t\t\t_soft_visibility_threshold.send();\n\n\t\t\t\tCHECK_GL_ERROR;\n\n\t\t\t\tglActiveTexture(GL_TEXTURE1);\n\t\t\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, soft_visibility_maps->handle());\n\n\t\t\t\tCHECK_GL_ERROR;\n\t\t\t}\n\n\t\t\tCHECK_GL_ERROR;\n\n\t\t\tint usedCamerasCount = 0;\n\t\t\t\n\t\t\tfor (int i = 0; i < std::min(imgs_ulr.size(), _numCams); ++i) {\n\t\t\t\t\n\t\t\t\tif (!scene->cameras()->inputCameras()[imgs_ulr[i]]->isActive()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tauto& cam = *scene->cameras()->inputCameras()[imgs_ulr[i]];\n\t\t\t\t_icamPos[usedCamerasCount].set(cam.position());\n\t\t\t\t_icamDir[usedCamerasCount].set(cam.dir());\n\t\t\t\t_icamProj[usedCamerasCount].set(cam.viewproj());\n\t\t\t\t_selected_cams[usedCamerasCount].set((int)imgs_ulr[i]);\n\t\t\t\tglActiveTexture(GL_TEXTURE0 + usedCamerasCount + 2);\n\t\t\t\tglBindTexture(GL_TEXTURE_2D, inputRTs[imgs_ulr[i]]->texture());\n\n\t\t\t\tif (useMasks()) {\n\t\t\t\t\tglActiveTexture(GL_TEXTURE0 + (int)_numCams + usedCamerasCount + 2);\n\t\t\t\t\tglBindTexture(GL_TEXTURE_2D, getMasks()[imgs_ulr[i]]->texture());\n\t\t\t\t}\n\t\t\t\t++usedCamerasCount;\n\t\t\t}\n\n\t\t\tCHECK_GL_ERROR;\n\n\t\t\t_camCount.set(usedCamerasCount);\n\t\t\t\n\t\t\tCHECK_GL_ERROR;\n\n\t\t\t//glDisable(GL_DEPTH_TEST);\n\t\t\tRenderUtility::renderScreenQuad();\n\n\t\t\tCHECK_GL_ERROR;\n\n\t\t\t_ulrShader.end();\n\t\t\tdst.unbind();\n\n\t\t}\n\n\n\n\t} /*namespace sibr*/\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV2Renderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/system/Config.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/renderer/RenderMaskHolder.hpp>\n\nnamespace sibr { \n\n\t/** Second version of the ULR render. use separate samplers for each input image.\n\t */\n\tclass SIBR_EXP_ULR_EXPORT ULRV2Renderer : public RenderMaskHolder\n\t{\n\t\tSIBR_CLASS_PTR(ULRV2Renderer);\n\n\t\t/** Constructor.\n\t\t *\\param cameras input cameras\n\t\t *\\param w rendering width\n\t\t *\\param h rendering height\n\t\t *\\param maxCams maximum number of cameras selcted for rendering a frame\n\t\t *\\param fShader name of the fragment shader\n\t\t *\\param vShader name of the vertex shader\n\t\t *\\param facecull should backface culling be performed during the prepass.\n\t\t **/\n\t\tULRV2Renderer(const std::vector<InputCamera::Ptr> & cameras, const uint w, const uint h, const unsigned int maxCams = 0, const std::string & fShader = \"ulr/ulr_v2\", const std::string & vShader = \"ulr/ulr_v2\", const bool facecull = true);\n\n\t\t/** Setup the ULR shaders.\n\t\t *\\param fShader name of the fragment shader\n\t\t *\\param vShader name of the vertex shader\n\t\t **/\n\t\tvoid setupULRshader(const std::string & fShader = \"ulr/ulr_v2\", const std::string & vShader = \"ulr/ulr_v2\");\n\n\t\t/** Render.\n\t\t *\\param imgs_ulr vector of selected image IDs\n\t\t *\\param eye novel viewpoint\n\t\t *\\param scene the scene to render\n\t\t *\\param altMesh optional alternative mesh\n\t\t *\\param inputRTs the RGBD input images\n\t\t *\\param dst destination target\n\t\t */\n\t\tvoid process(const std::vector<uint>& imgs_ulr, const sibr::Camera& eye,\n\t\t\tconst sibr::BasicIBRScene::Ptr& scene,\n\t\t\tstd::shared_ptr<sibr::Mesh>& altMesh,\n\t\t\tconst std::vector<std::shared_ptr<RenderTargetRGBA32F> >& inputRTs,\n\t\t\tIRenderTarget& dst);\n\n\t\t/** Should occlusion testing be performed.\n\t\t *\\param val true if testing should occur\n\t\t */\n\t\tvoid doOccl(bool val) { _doOccl = val; }\n\n\t\t/** \\return a reference to the occlusion threshold */\n\t\tfloat & epsilonOcclusion() { return _epsilonOcclusion; }\n\n\t\t/** Are the mask smooth values or binary.\n\t\t *\\param val true if they are binary\n\t\t */\n\t\tvoid setAreMasksBinary(bool val) { _areMasksBinary = val; }\n\n\t\t/** Should the masks be inverted.\n\t\t *\\param val true if they should\n\t\t */\n\t\tvoid setDoInvertMasks(bool val) { _doInvertMasks = val; }\n\n\t\t/** Should black pixels be ignored when accumulating colors.\n\t\t *\\param val true if they should be ignored\n\t\t */\n\t\tvoid setDiscardBlackPixels(bool val) { _discardBlackPixels = val; }\n\n\t\t/** Should backface culling be performed.\n\t\t *\\param val true if it should\n\t\t */\n\t\tvoid setCulling(bool val) { _shouldCull = val; }\n\n\t\t/** \\return a pointer to the soft visibility texture array if it exists */\n\t\tTexture2DArrayLum32F * & getSoftVisibilityMaps(void) { return soft_visibility_maps; }\n\n\t\t/** \\return a reference to the soft visibility threshold. */\n\t\tsibr::GLuniform<float> & getSoftVisibilityThreshold() { return _soft_visibility_threshold; }\n\n\t\t/** \\return a pointer to the ULR OpenGL program. */\n\t\tsibr::GLShader * getProgram() { return &_ulrShader; }\n\n\t\t/** \\return the number of cameras */\n\t\tsize_t getNumCams() { return _numCams; }\n\n\tpublic:\n\t\tsibr::RenderTargetRGBA32F::Ptr _depthRT; ///< the prepass render target.\n\n\tprivate:\n\t\t\n\t\tsibr::GLShader _ulrShader;\n\t\tsibr::GLShader _depthShader;\n\n\t\tstd::vector<sibr::GLParameter>\t_icamProj;\n\t\tstd::vector<sibr::GLParameter>\t_icamPos;\n\t\tstd::vector<sibr::GLParameter>\t_icamDir;\n\t\tstd::vector<sibr::GLParameter>\t_inputRGB;\n\t\tstd::vector<sibr::GLParameter>\t_masks;\n\t\tstd::vector<sibr::GLuniform<int> >\t_selected_cams;\n\n\t\tTexture2DArrayLum32F * soft_visibility_maps;\n\t\tsibr::GLuniform<float> _soft_visibility_threshold;\n\t\tsibr::GLuniform<bool> _use_soft_visibility;\n\n\t\tsibr::GLParameter _occTest;\n\t\tsibr::GLParameter _areMasksBinaryGL;\n\t\tsibr::GLParameter _doInvertMasksGL;\n\t\tsibr::GLParameter _discardBlackPixelsGL;\n\t\tsibr::GLParameter _doMask;\n\t\tsibr::GLParameter _ncamPos;\n\t\tsibr::GLParameter _camCount;\n\t\tsibr::GLParameter _proj;\n\t\tsibr::GLuniform<float> _epsilonOcclusion;\n\n\t\tbool\t_doOccl;\n\t\tbool\t_areMasksBinary;\n\t\tbool\t_doInvertMasks;\n\t\tbool\t_discardBlackPixels;\n\t\tbool _shouldCull;\n\t\tsize_t _numCams;\n\t\n   };\n\n} /*namespace sibr*/\n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV2View.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n/// \\todo TODO: make shorter\n#include \"Config.hpp\"\n#include <core/assets/Resources.hpp>\n#include <projects/ulr/renderer/ULRV2View.hpp>\n#include <core/system/Vector.hpp>\n#include <core/graphics/Texture.hpp>\n#include <core/graphics/GUI.hpp>\n#include <map>\n\nnamespace sibr { \n\tULRV2View::~ULRV2View( )\n{\n\t_altMesh.reset();\n}\n\nULRV2View::ULRV2View( const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h ) :\n\t_scene(ibrScene),\n\t\tsibr::ViewBase(render_w, render_h), \n\t_renderMode(ULRV2View::RenderMode::NORMAL), _singleCamId(0)\n{\n\t_altMesh.reset();\n\t_altMesh = nullptr;\n\t_numDistUlr = 4, _numAnglUlr = 0;\n    std::cerr << \"[ULR] setting number of images to blend \"<< _numDistUlr << \" \" << _numAnglUlr << std::endl;\n\n\t_ulr.reset(new ULRV2Renderer(ibrScene->cameras()->inputCameras(), render_w, render_h, _numDistUlr + _numAnglUlr));\n\tuint w = render_w;\n\tuint h = render_h;\n\t_poissonRT.reset(new RenderTargetRGBA(w, h, SIBR_CLAMP_UVS));\n\t_blendRT.reset(new RenderTargetRGBA(w, h, SIBR_CLAMP_UVS));\n\t_poisson.reset(new PoissonRenderer(w,h));\n\t_poisson->enableFix() = true;\n\t_inputRTs = ibrScene->renderTargets()->inputImagesRT();\n\n\ttestAltlULRShader = false;\n}\n\nvoid ULRV2View::onRenderIBR( sibr::IRenderTarget& dst, const sibr::Camera& eye ) {\n    // Select subset of input images for ULR\n\t//std::vector<uint> imgs_ulr = chosen_cameras(eye);\n\tstd::vector<uint> imgs_ulr = chosen_cameras_angdist(eye);\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n\t//std::cout << imgs_ulr.size() << \" \" << std::flush;\n\n\tif (_renderMode == RenderMode::ONLY_ONE_CAM) {\n\t\tfor (auto i : imgs_ulr) {\n\t\t\t//std::cout << i << \" \";\n\t\t}\n\t\tint id_cam = std::max(0, std::min((int)imgs_ulr.size()-1, _singleCamId));\n\t\tint cam = imgs_ulr[id_cam];\n\t\timgs_ulr = std::vector<uint>(1, cam);\n\t\t//std::cout << \" -> ulr debug single cam, id : \" << _singleCamId << \", cam : \";\n\t\t//for (auto i : imgs_ulr) {\n\t\t\t//std::cout << i << \" \";\n\t\t//}\n\t\t//std::cout << std::endl;\n\t} else if(_renderMode == RenderMode::LEAVE_ONE_OUT) {\n\t\tstd::vector<uint> new_imgs_ulr;\n\t\tfor(const auto & i : imgs_ulr) {\n\t\t\t\tif(int(i) != _singleCamId) {\n\t\t\t\t\tnew_imgs_ulr.emplace_back(i);\n\t\t\t\t}\n\t\t}\n\t\timgs_ulr = new_imgs_ulr;\n\t}\n\n\tif (_noPoissonBlend) {\n\t\t_ulr->process(\n\t\t\timgs_ulr,\n\t\t\teye,\n\t\t\t_scene,\n\t\t\t_altMesh,\n\t\t\t_inputRTs,\n\t\t\tdst);\n\n\t}  else {\n\t\t\t_ulr->process(\n\t\t\t\t/* input -- images chosen */ imgs_ulr, \n\t\t\t\t/* input -- camera position */ eye, \n\t\t\t\t/* input -- scene */ _scene, \n\t\t\t\t/* input -- alt mesh if available */ _altMesh, \n\t\t\t\t/* input -- input RTs -- can be RGB or alpha */  _inputRTs,\n\t\t\t\t/* output */ *_blendRT);\n\n\t\t\t_poisson->process(\n\t\t\t\t\t_blendRT,\n\t\t\t\t\t_poissonRT);\n\n\n\t\t\tblit(*_poissonRT, dst);\n\t}\n}\n\nvoid ULRV2View::onUpdate(Input & input)\n{\n\tif (input.key().isReleased(sibr::Key::Tab)) {\n\t\ttestAltlULRShader = !testAltlULRShader;\n\t\tif (testAltlULRShader) {\n\t\t\t_ulr->setupULRshader(\"ulr_v2_alt\");\n\t\t} else {\n\t\t\t_ulr->setupULRshader();\n\t\t}\n\t\tstd::cout << \"ULR using \" << (testAltlULRShader ? \"all cams\" : \"standard ulr\") << std::endl;\n\t}\n}\n\nvoid ULRV2View::onGUI() {\n\t\tconst std::string guiName = \"ULRV2 Settings (\" + name() + \")\";\n\t\tif(ImGui::Begin(guiName.c_str())) {\n\t\t\t\n\t\t\tImGui::PushScaledItemWidth(80);\n\t\t\tconst bool v1_changed = ImGui::InputInt(\"#Dist\", &_numDistUlr, 1, 10);\n\t\t\tImGui::SameLine();\n\t\t\tconst bool v2_changed = ImGui::InputInt(\"#Angle\", &_numAnglUlr, 1, 10);\n\t\t\tImGui::PopItemWidth();\n\n\t\t\tif (v1_changed || v2_changed) {\n\t\t\t\tsetNumBlend(_numDistUlr, _numAnglUlr);\n\t\t\t}\n\n\t\t\t\n\n\t\t\tImGui::Checkbox(\"Disable Poisson\", &_noPoissonBlend);\n\t\t\tImGui::Checkbox(\"Poisson fix\", &_poisson->enableFix());\n\n\t\t\tImGui::PushScaledItemWidth(120);\n\t\t\tImGui::InputFloat(\"Epsilon occlusion\", &_ulr->epsilonOcclusion(), 0.001f, 0.01f);\n\t\t\tImGui::Combo(\"Rendering mode\", (int*)(&_renderMode), \"Standard\\0One image\\0Leave one out\\0\\0\");\n\t\t\tif (ImGui::InputInt(\"Selected image\", &_singleCamId, 1, 10)) {\n\t\t\t\t_renderMode = RenderMode::ONLY_ONE_CAM;\n\t\t\t}\n\t\t\t_singleCamId = sibr::clamp(_singleCamId, 0, (int)_scene->cameras()->inputCameras().size() - 1);\n\t\t\t//ImGui::SliderInt(\"Selected image\", &_singleCamId, 0, scene().inputCameras().size() - 1);\n\t\t\tImGui::PopItemWidth();\n\n\t\t}\n\t\tImGui::End();\n}\n\nvoid ULRV2View::computeVisibilityMap(const sibr::ImageL32F & depthMap, sibr::ImageRGBA & out)\n{\n\tconst float threshold_3d = 2.5f;\n\tconst std::vector<sibr::Vector2i> shifts = { { 1,0 },{ 0,1 },{ -1,0 },{ 0,-1 } };\n\n\tsibr::ImageL8 edgeMap(depthMap.w(), depthMap.h(), 255);\n\tfor (uint i = 0; i < depthMap.h(); i++) {\n\t\tfor (uint j = 0; j < depthMap.w(); j++) {\n\t\t\tsibr::Vector2i pos(j, i);\n\t\t\tfloat currentDepth = depthMap(pos).x();\n\t\t\tfor (const auto & shift : shifts) {\n\t\t\t\tVector2i npos = pos + shift;\n\t\t\t\tif (!depthMap.isInRange(npos)) { continue; }\n\t\t\t\tif (std::abs(depthMap(npos).x() - currentDepth) > threshold_3d) {\n\t\t\t\t\tedgeMap(pos).x() = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcv::Mat distance(depthMap.h(), depthMap.w(), CV_32FC1);\n\tcv::distanceTransform(edgeMap.toOpenCVnonConst(), distance, cv::DIST_L2, cv::DIST_MASK_PRECISE);\n\n\tsibr::ImageL32F outF;\n\toutF.fromOpenCV(distance);\n\tout = sibr::convertL32FtoRGBA(outF);\n\n}\n\n\t// -----------------------------------------------------------------------\n\nstd::vector<uint> ULRV2View::chosen_cameras(const sibr::Camera& eye) {\n    std::vector<uint> imgs_id;\n    std::multimap<float,uint> distMap;\t\t\t\t\t\t\t\t\t// distance wise closest input cameras\n\tstd::multimap<float,uint> dang;\t\t\t\t\t\t\t\t\t// angular distance from inputs to novel camera\n    for (uint i=0; i< _scene->cameras()->inputCameras().size(); i++ ) {\n        const sibr::InputCamera& inputCam = *_scene->cameras()->inputCameras()[i];\n        if (inputCam.isActive()) {\n\t\t\t// Convert following to Eigen versions\n            float dist = sibr::distance(inputCam.position(), eye.position());\n            float angle = sibr::dot(inputCam.dir(),eye.dir());\n                distMap.insert(std::make_pair(dist,i));\t\t\t\t\t// sort distances in increasing order\n\t\t\t\tdang.insert(std::make_pair( acos(angle),i));\t\t\t\t// sort angles in increasing order\n        }\n    }\n\tfor (uint i=0; i< _scene->cameras()->inputCameras().size(); i++) {\n        const sibr::InputCamera& inputCam = *_scene->cameras()->inputCameras()[i];\n        if (inputCam.isActive() && distMap.size() <= (_numDistUlr+_numAnglUlr)/2 ) {\n            float dist = sibr::distance(inputCam.position(),eye.position());\n            distMap.insert(std::make_pair(dist,i));\t\t\t\t\t// sort distances in increasing order\n\t\t\t}\n\t}\n\n    std::multimap<float,uint>::const_iterator d_it(distMap.begin());\t// select the _numDistUlr closest cameras\n\tfor (int i=0; d_it!=distMap.end() && i<_numDistUlr; d_it++,i++) {\n        imgs_id.push_back(d_it->second);\n    }\n\n\tstd::multimap<float,uint>::const_iterator a_it(dang.begin());    // select the NUM_ANG_ULR closest cameras\n\tfor (int i=0; a_it!=dang.end() && i<_numAnglUlr; a_it++,i++) {\n        imgs_id.push_back(a_it->second);\n    }\n\n\tstd::sort( imgs_id.begin(), imgs_id.end() );\t\t\t\t// Avoid repetitions\n\timgs_id.erase( std::unique( imgs_id.begin(), imgs_id.end() ), imgs_id.end() );\n\n\tSIBR_ASSERT(imgs_id.size() <= _numDistUlr + _numAnglUlr);\n    return imgs_id;\n}\n\nstd::vector<uint> ULRV2View::chosen_cameras_angdist(const sibr::Camera & eye)\n{\n\tconst auto & cams = _scene->cameras()->inputCameras();\n\tstd::vector<uint> out;\n\n\t// sort angle / dist combined\n\tstruct camAng\n\t{\n\t\tcamAng() {}\n\t\tcamAng(float a, float d, int i) : ang(a), dist(d), id(i) {}\n\t\tfloat ang, dist;\n\t\tint id;\n\t\tstatic bool compare(const camAng & a, const camAng & b) { return a.ang / a.dist > b.ang / b.dist;  }\n\t};\n\n\tint total_size = _numAnglUlr + _numDistUlr;\n\n\tstd::vector<camAng> allAng;\n\tfor (int id = 0; id < (int)cams.size(); ++id) {\n\t\tconst auto & cam = *cams[id];\n        float angle = sibr::dot(cam.dir(),eye.dir());\n\t\t// reject back facing \n\t\tif( angle > 0.001 && cam.isActive()) {\n\t\t\tfloat dist =  (cam.position() - eye.position()).norm();\n\t\t\tallAng.push_back(camAng(angle, dist, id));\t\t\n\t\t}\n\t}\n\n\tstd::vector<bool> wasChosen(cams.size(), false);\n\n\tstd::sort(allAng.begin(), allAng.end(), camAng::compare);\n\tfor (int id = 0; id < std::min((int)allAng.size(), total_size); ++id) {\n\t\tout.push_back(allAng[id].id);\n\t\twasChosen[allAng[id].id] = true;\n\t}\n\n\tfor (int id = 0; id < (int)cams.size(); ++id) {\n\t\tif (!wasChosen[id] && out.size() < total_size && cams[id]->isActive()) {\n\t\t\tout.push_back(id);\n\t\t}\n\t}\n\n\treturn out;\n}\n\nstd::vector<uint> ULRV2View::chosen_camerasNew(const sibr::Camera & eye)\n{\n\tconst auto & cams = _scene->cameras()->inputCameras();\n\n\tstruct camDist\n\t{\n\t\tcamDist() {}\n\t\tcamDist(float d, int i) : dist(d), id(i) {}\n\t\tfloat dist;\n\t\tint id;\n\t\tstatic bool compare(const camDist & a, const camDist & b) { return a.dist < b.dist;  }\n\t};\n\n\tstd::vector<camDist> allDist;\n\tfor (int id = 0; id < (int)cams.size(); ++id) {\n\t\tconst auto & cam = *cams[id];\n\t\tallDist.push_back(camDist((cam.position() - eye.position()).norm(), id));\n\t}\n\tstd::sort(allDist.begin(), allDist.end(), camDist::compare);\n\tstd::vector<uint> out;\n\tfor (int id = 0; id < std::min((int)cams.size(),(int)_numDistUlr); ++id) {\n\t\tout.push_back(allDist[id].id);\n\t}\n\treturn out;\n}\n\nvoid ULRV2View::setNumBlend(short int dist, short int angle)\n{\n\t// Backup masks.\n\tauto copyMasks = _ulr->getMasks();\n\n\t_numDistUlr = dist, _numAnglUlr = angle;\n\tstd::cerr << \"[ULR] setting number of images to blend \" << _numDistUlr << \" \" << _numAnglUlr << std::endl;\n\t_ulr.reset(new ULRV2Renderer(_scene->cameras()->inputCameras(), _scene->cameras()->inputCameras()[0]->w(), _scene->cameras()->inputCameras()[0]->h(), _numDistUlr + _numAnglUlr));\n\t_ulr->setMasks(copyMasks);\n\t\n}\n\nvoid ULRV2View::loadMasks(const sibr::BasicIBRScene::Ptr& ibrScene, int w, int h, const std::string& maskDir, const std::string& preFileName, const std::string& postFileName\n) {\n\tstd::string finalMaskDir = (maskDir == \"\" ? ibrScene->data()->basePathName() + \"/masks/\" : maskDir);\n\tstd::string finalPostFileName = (postFileName == \"\" ? \"-mask.jpg\" : postFileName);\n\t_ulr->loadMasks(ibrScene, finalMaskDir, preFileName, finalPostFileName, w, h);\n}\n\nvoid ULRV2View::setMasks( const std::vector<RenderTargetLum::Ptr>& masks ) {\n\t\t_ulr->setMasks(masks);\n}\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV2View.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/system/Config.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/view/ViewBase.hpp>\n# include \"core/scene/BasicIBRScene.hpp\"\n# include <core/renderer/CopyRenderer.hpp>\n# include <projects/ulr/renderer/ULRV2Renderer.hpp>\n# include <core/renderer/PoissonRenderer.hpp>\n\nnamespace sibr { \n\n\t/** View associated to ULRRenderer v2, providing interface and options. */\n\tclass SIBR_EXP_ULR_EXPORT ULRV2View : public sibr::ViewBase\n\t{\n\t\tSIBR_CLASS_PTR(ULRV2View);\n\n\t\t/** Camera selection mode. */\n\t\tenum class RenderMode { NORMAL = 0, ONLY_ONE_CAM = 1, LEAVE_ONE_OUT = 2 };\n\n\tpublic:\n\n\t\t/** Constructor.\n\t\t *\\param ibrScene the scene\n\t\t *\\param render_w rendering width\n\t\t *\\param render_h rendering height\n\t\t **/\n\t\tULRV2View( const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h );\n\n\t\t/** Destructor. */\n\t\t~ULRV2View();\n\n\t\t/** Render using the ULR algorithm.\n\t\t *\\param dst destination target\n\t\t *\\param eye novel viewpoint\n\t\t **/\n\t\tvirtual void onRenderIBR( sibr::IRenderTarget& dst, const sibr::Camera& eye );\n\n\t\t/** Update state absed on user inputs.\n\t\t *\\param input the view input\n\t\t **/\n\t\tvirtual void onUpdate(Input& input);\n\n\t\t/** Display GUI. */\n\t\tvirtual void onGUI() override;\n\n\t\t/** Select input cameras to use for rendering.\n\t\t *\\param eye the current viewpoint\n\t\t *\\return a list of camera indices.\n\t\t **/\n\t\tvirtual std::vector<uint> chosen_cameras(const sibr::Camera& eye) ;\n\n\t\t/** Select input cameras to use for rendering, based only on distance.\n\t\t *\\param eye the current viewpoint\n\t\t *\\return a list of camera indices.\n\t\t **/\n\t\tvirtual std::vector<uint> chosen_camerasNew(const sibr::Camera& eye);\n\n\t\t/** Select input cameras to use for rendering.\n\t\t *\\param eye the current viewpoint\n\t\t *\\return a list of camera indices.\n\t\t **/\n\t\tvirtual std::vector<uint> chosen_cameras_angdist(const sibr::Camera& eye);\n\n\t\t/** Set the altMesh and use instead of scene proxy.\n\t\t *\\param m mesh to use\n\t\t **/\n\t\tvoid\taltMesh(std::shared_ptr<sibr::Mesh> m)\t{ _altMesh = m; }\n\n\t\t/** Toggle occlusion testing.\n\t\t *\\param val should occlusion testing be performed\n\t\t */\n\t\tvoid\tdoOccl(bool val) { _ulr->doOccl(val); }\n\n\t\t/** \\return a pointer to the alt mesh if it exists */\n\t\tstd::shared_ptr<sibr::Mesh> \taltMesh()\t{ return _altMesh; }\n\n\t\t/** Set the number of cmaeras to select for blending.\n\t\t *\\param dist number of cameras for the distance criterion\n\t\t *\\param angle number of cameras for the angle criterion\n\t\t **/\n\t\tvoid\tsetNumBlend(short int dist, short int angle);\n\n\t\t/** Set the input RGBD textures.\n\t\t *\\param iRTs the new textures to use.\n\t\t */\n\t\tvoid\tinputRTs(const std::vector<std::shared_ptr<RenderTargetRGBA32F> >& iRTs) { _inputRTs = iRTs;}\n\n\t\t/** Set the masks for ignoring some regions of the input images.\n\t\t *\\param masks the new masks\n\t\t **/\n\t\tvoid\tsetMasks( const std::vector<RenderTargetLum::Ptr>& masks );\n\n\t\t/** Load masks from disk.\n\t\t *\\param ibrScene the scene\n\t\t *\\param w resolution width\n\t\t *\\param h resolution height\n\t\t *\\param maskDir masks directory path\n\t\t *\\param preFileName mask files prefix\n\t\t *\\param postFileName mask files suffix and extension\n\t\t */\n\t\tvoid\tloadMasks(\n\t\t\tconst sibr::BasicIBRScene::Ptr& ibrScene, int w, int h,\n\t\t\tconst std::string& maskDir = \"\",\n\t\t\tconst std::string& preFileName = \"\",\n\t\t\tconst std::string& postFileName = \"\"\n\t\t);\n\n\t\t/** Set the camera selection mode.\n\t\t *\\param mode the new mode. \n\t\t */\n\t\tvoid\t\tsetRenderMode(RenderMode mode) { _renderMode = mode; }\n\t\t/** \\return the camera selection mode. */\n\t\tRenderMode\tgetRenderMode() const { return _renderMode; }\n\n\t\t/** Set the view ID when in single view mode.\n\t\t *\\param id the camera id to use\n\t\t */\n\t\tvoid\t\tsetSingleViewId(int id) { _singleCamId = id; }\n\t\t/** \\return the current selected camera ID in single view mode. */\n\t\tint\t\t\tgetSingleViewId(void)  const { return _singleCamId; }\n\n\t\t/** Toggle poisson blending.\n\t\t *\\param val if true, Poisson blending is disabled.\n\t\t */\n\t\tvoid noPoissonBlend(bool val) { _noPoissonBlend = val; }\n\t\t/** \\return true if pOisson blending is disabled. */\n\t\tbool noPoissonBlend() const { return _noPoissonBlend; }\n\n\t\t/** Compute soft visibility map.\n\t\t *\\param depthMap view depth map\n\t\t *\\param out will contain the soft visibility map\n\t\t */\n\t\tvoid computeVisibilityMap(const sibr::ImageL32F & depthMap, sibr::ImageRGBA & out);\n\n\t\t/** \\return a pointer to the scene */\n\t\tconst std::shared_ptr<sibr::BasicIBRScene> & getScene() const { return _scene; }\n\n\tpublic:\n\t\tULRV2Renderer::Ptr\t\t_ulr; ///< ULRV2 renderer.\n\t\tPoissonRenderer::Ptr\t_poisson; ///< Poisson filling renderer.\n\n\tprotected:\n\t\t\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene; ///< the current scene.\n\t\tstd::shared_ptr<sibr::Mesh>\t_altMesh; ///< For the cases when using a different mesh than the scene\n\t\tint _numDistUlr, _numAnglUlr; ///< Number of cameras to select for each criterion.\n\n\t\tstd::vector<std::shared_ptr<RenderTargetRGBA32F> > _inputRTs; ///< input RTs -- usually RGB but can be alpha or other\n\n\t\tbool _noPoissonBlend = false; ///< Runtime status of the poisson blend.\n\n\t\tRenderTargetRGBA::Ptr _blendRT; ///< ULR destination RT.\n\t\tRenderTargetRGBA::Ptr _poissonRT; ///< Poisson filling destination RT.\n\n\t\tRenderMode _renderMode; ///< Current camera selection mode.\n\t\tint _singleCamId; ///< Selected camera in single view mode.\n\n\t\tbool testAltlULRShader; ///< TT: to switch with alternate shader with tab\n\t};\n\n} /*namespace sibr*/ \n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV3Renderer.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <projects/ulr/renderer/ULRV3Renderer.hpp>\n\n\n\nsibr::ULRV3Renderer::ULRV3Renderer(const std::vector<InputCamera::Ptr> & cameras, const uint w, const uint h, const std::string & fShader, const std::string & vShader, const bool facecull)\n{\n\t_backFaceCulling = facecull;\n\tfragString = fShader;\n\tvertexString = vShader;\n\t_maxNumCams = cameras.size();\n\t_camsCount = int(_maxNumCams);\n\n\t// Populate the cameraInfos array (will be uploaded to the GPU).\n\t_cameraInfos.clear();\n\t_cameraInfos.resize(_maxNumCams);\n\tfor (size_t i = 0; i < _maxNumCams; ++i) {\n\t\tconst auto & cam = *cameras[i];\n\t\t_cameraInfos[i].vp = cam.viewproj();\n\t\t_cameraInfos[i].pos = cam.position();\n\t\t_cameraInfos[i].dir = cam.dir();\n\t\t_cameraInfos[i].selected = cam.isActive();\n\t}\n\n\t// Compute the max number of cameras allowed.\n\tGLint maxBlockSize = 0, maxSlicesSize = 0;\n\tglGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxBlockSize);\n\tglGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxSlicesSize);\n\t// For each camera we store a matrix, 2 vecs3, 2 floats (including padding).\n\tconst unsigned int bytesPerCamera = 4 * (16 + 2 * 3 + 2);\n\tconst unsigned int maxCamerasAllowed = std::min((unsigned int)maxSlicesSize, (unsigned int)(maxBlockSize / bytesPerCamera));\n\tstd::cout << \"[ULRV3Renderer] \" << \"MAX_UNIFORM_BLOCK_SIZE: \" << maxBlockSize << \", MAX_ARRAY_TEXTURE_LAYERS: \" << maxSlicesSize << \", meaning at most \" << maxCamerasAllowed << \" cameras.\" << std::endl;\n\n\t// Create UBO.\n\t_uboIndex = 0;\n\tglGenBuffers(1, &_uboIndex);\n\tglBindBuffer(GL_UNIFORM_BUFFER, _uboIndex);\n\tglBufferData(GL_UNIFORM_BUFFER, sizeof(CameraUBOInfos)*_maxNumCams, &_cameraInfos[0], GL_DYNAMIC_DRAW);\n\tglBindBuffer(GL_UNIFORM_BUFFER, 0);\n\n\t// Setup shaders and uniforms.\n\tsetupShaders(fragString, vertexString);\n\n\t// Create the intermediate rendertarget.\n\t_depthRT.reset(new sibr::RenderTargetRGBA32F(w, h));\n\n\tCHECK_GL_ERROR;\n}\n\n\nvoid sibr::ULRV3Renderer::setupShaders(const std::string & fShader, const std::string & vShader)\n{\n\t// Create shaders.\n\tstd::cout << \"[ULRV3Renderer] Setting up shaders for \" << _maxNumCams << \" cameras.\" << std::endl;\n\tGLShader::Define::List defines;\n\tdefines.emplace_back(\"NUM_CAMS\", _maxNumCams);\n\tdefines.emplace_back(\"ULR_STREAMING\", 0);\n\n\t_ulrShader.init(\"ULRV3\",\n\t\tsibr::loadFile(sibr::getShadersDirectory(\"\") + \"/\" + vShader + \".vert\"),\n\t\tsibr::loadFile(sibr::getShadersDirectory(\"\") + \"/\" + fShader + \".frag\", defines));\n\t_depthShader.init(\"ULRV3Depth\",\n\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr_intersect.vert\"),\n\t\tsibr::loadFile(sibr::getShadersDirectory(\"ulr\") + \"/ulr_intersect.frag\", defines));\n\n\t// Setup uniforms.\n\t_nCamProj.init(_depthShader, \"proj\");\n\t_nCamPos.init(_ulrShader, \"ncam_pos\");\n\t_occTest.init(_ulrShader, \"occ_test\");\n\t_useMasks.init(_ulrShader, \"doMasking\");\n\t_discardBlackPixels.init(_ulrShader, \"discard_black_pixels\");\n\t_epsilonOcclusion.init(_ulrShader, \"epsilonOcclusion\");\n\t_areMasksBinary.init(_ulrShader, \"is_binary_mask\");\n\t_invertMasks.init(_ulrShader, \"invert_mask\");\n\t_flipRGBs.init(_ulrShader, \"flipRGBs\");\n\t_showWeights.init(_ulrShader, \"showWeights\");\n\t_winnerTakesAll.init(_ulrShader, \"winner_takes_all\");\n\t_camsCount.init(_ulrShader, \"camsCount\");\n\t_gammaCorrection.init(_ulrShader, \"gammaCorrection\");\n\n\tCHECK_GL_ERROR;\n}\n\nvoid sibr::ULRV3Renderer::process(\n\tconst sibr::Mesh & mesh,\n\tconst sibr::Camera & eye,\n\tIRenderTarget & dst,\n\tconst sibr::Texture2DArrayRGB::Ptr & inputRGBs,\n\tconst sibr::Texture2DArrayLum32F::Ptr & inputDepths,\n\tbool passthroughDepth\n) {\n\t// Render the proxy positions in world space.\n\tprocess(mesh, eye, dst, inputRGBs->handle(), inputDepths, passthroughDepth);\n}\n\nvoid sibr::ULRV3Renderer::process(\n\tconst sibr::Mesh & mesh,\n\tconst sibr::Camera & eye,\n\tIRenderTarget & dst,\n\tuint inputRGBHandle,\n\tconst sibr::Texture2DArrayLum32F::Ptr & inputDepths,\n\tbool passthroughDepth\n) {\n\tif (_profiling) {\n\t\t_depthPassTimer.tic();\n\t}\n\t// Render the proxy positions in world space.\n\trenderProxyDepth(mesh, eye);\n\tif (_profiling) {\n\t\tglFinish();\n\t\t//std::cout << \"\\nDepth Pass: \" << _depthPassTimer.deltaTimeFromLastTic() << \" ms\" << std::endl;\n\t\t_depthCost.push_back(_depthPassTimer.deltaTimeFromLastTic());\n\t}\n\tif (_profiling) {\n\t\t_blendPassTimer.tic();\n\t}\n\t// Perform ULR blending.\n\trenderBlending(eye, dst, inputRGBHandle, inputDepths, passthroughDepth);\n\tif (_profiling) {\n\t\tglFinish();\n\t\t//std::cout << \"\\nBlend Pass: \" << _blendPassTimer.deltaTimeFromLastTic() << \" ms\" << std::endl;\n\t\t_blendCost.push_back(_blendPassTimer.deltaTimeFromLastTic());\n\t}\n}\n\nvoid sibr::ULRV3Renderer::updateCameras(const std::vector<uint> & camIds) {\n\t// Reset all cameras.\n\tfor(auto & caminfos : _cameraInfos) {\n\t\tcaminfos.selected = 0;\n\t}\n\t// Enabled the ones passed as indices.\n\tfor (const auto & camId : camIds) {\n\t\t_cameraInfos[camId].selected = 1;\n\t}\n\n\t// Update the content of the UBO.\n\tglBindBuffer(GL_UNIFORM_BUFFER, _uboIndex);\n\tglBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(CameraUBOInfos)*_maxNumCams, &_cameraInfos[0]);\n\tglBindBuffer(GL_UNIFORM_BUFFER, 0);\n}\n\nvoid sibr::ULRV3Renderer::stopProfile()\n{\n\tconst std::vector<std::string> names = { \"Depth Cost: \", \"Blend Cost: \"};\n\tconst  std::vector<std::vector<float>> counts = {\n\t\t_depthCost, _blendCost};\n\tstd::string profileStr = \"\";\n\t//profileStr = \"\";\n\n\tfor (int i = 0; i < names.size(); ++i) {\n\t\t// Compute metrics: min, max, avg, variance.\n\t\tdouble miniF = std::numeric_limits<double>::max();\n\t\tdouble maxiF = 0.0;\n\t\tdouble avgF = 0.0;\n\t\tfor (size_t tid = 0; tid < counts[i].size(); ++tid) {\n\t\t\tconst double ft = double(counts[i][tid]);\n\t\t\tavgF += ft;\n\t\t\tminiF = std::min(miniF, ft);\n\t\t\tmaxiF = std::max(maxiF, ft);\n\t\t}\n\t\tavgF /= double(counts[i].size());\n\t\tdouble varF = 0.0;\n\t\tfor (size_t tid = 0; tid < counts[i].size(); ++tid) {\n\t\t\tconst double residualF = double(counts[i][tid]) - avgF;\n\t\t\tvarF += residualF * residualF;\n\t\t}\n\t\tvarF /= double(int(counts[i].size()) - 1);\n\t\tprofileStr += \"-----------\\n\";\n\t\tprofileStr += names[i] + \" num frames: \" + std::to_string(counts[i].size()) + \"\\n\";\n\t\tprofileStr += names[i] + \" min/max: \" + std::to_string(miniF) + \"/\" + std::to_string(maxiF) + \"\\n\";\n\t\tprofileStr += names[i] + \" avg/stddev: \" + std::to_string(avgF) + \"/\" + std::to_string(std::sqrt(varF)) + \"\\n\";\n\t}\n\n\tstd::cout << profileStr << std::endl;\n}\n\nvoid sibr::ULRV3Renderer::renderProxyDepth(const sibr::Mesh & mesh, const sibr::Camera & eye)\n{\n\t// Bind and clear RT.\n\t_depthRT->bind();\n\tglViewport(0, 0, _depthRT->w(), _depthRT->h());\n\tglClearColor(0, 0, 0, 1);\n\tglClearDepth(1.0);\n\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n\t// Render the mesh from the current viewpoint, output positions.\n\t_depthShader.begin();\n\t_nCamProj.set(eye.viewproj());\n\n\tmesh.render(true, _backFaceCulling);\n\t\n\t_depthShader.end();\n\t_depthRT->unbind();\n}\n\nvoid sibr::ULRV3Renderer::renderBlending(\n\tconst sibr::Camera & eye,\n\tIRenderTarget & dst,\n\tuint inputRGBHandle,\n\tconst sibr::Texture2DArrayLum32F::Ptr & inputDepths,\n\tbool passthroughDepth\n) {\n\t// Bind and clear destination rendertarget.\n\tglViewport(0, 0, dst.w(), dst.h());\n\tif (_clearDst) {\n\t\tdst.clear();\n\t}\n\tdst.bind();\n\n\t_ulrShader.begin();\n\n\t// Uniform values.\n\t_nCamPos.set(eye.position());\n\t_occTest.send();\n\t_areMasksBinary.send();\n\t_invertMasks.send();\n\t_discardBlackPixels.send();\n\t_useMasks.send();\n\t_epsilonOcclusion.send();\n\t_flipRGBs.send();\n\t_showWeights.send();\n\t_camsCount.send();\n\t_winnerTakesAll.send();\n\t_gammaCorrection.send();\n\n\t// Textures.\n\tglActiveTexture(GL_TEXTURE0);\n\tglBindTexture(GL_TEXTURE_2D, _depthRT->handle());\n\n\tglActiveTexture(GL_TEXTURE1);\n\tglBindTexture(GL_TEXTURE_2D_ARRAY, inputRGBHandle);\n\n\tglActiveTexture(GL_TEXTURE2);\n\tglBindTexture(GL_TEXTURE_2D_ARRAY, inputDepths->handle());\n\n\t// Pass the masks if enabled and available.\n\tif (_useMasks && _masks.get()) {\n\t\tglActiveTexture(GL_TEXTURE3);\n\t\tglBindTexture(GL_TEXTURE_2D_ARRAY, _masks->handle());\n\t}\n\n\t// Bind UBO to shader, after all possible textures.\n\tglBindBuffer(GL_UNIFORM_BUFFER, _uboIndex);\n\tglBindBufferBase(GL_UNIFORM_BUFFER, 4, _uboIndex);\n\tglBindBuffer(GL_UNIFORM_BUFFER, 0);\n\n\tif (passthroughDepth) {\n\t\tglEnable(GL_DEPTH_TEST);\n\t} else {\n\t\tglDisable(GL_DEPTH_TEST);\n\t}\n\n\t// Perform ULR rendering.\n\tRenderUtility::renderScreenQuad();\n\tglDisable(GL_DEPTH_TEST);\n\n\t_ulrShader.end();\n\tdst.unbind();\n}\n\nvoid sibr::ULRV3Renderer::resize(const unsigned w, const unsigned h) {\n\t_depthRT.reset(new sibr::RenderTargetRGBA32F(w, h));\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV3Renderer.hpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#pragma once\n\n# include \"Config.hpp\"\n# include <core/system/Config.hpp>\n# include <core/graphics/Texture.hpp>\n# include <core/graphics/Shader.hpp>\n# include <core/graphics/Mesh.hpp>\n# include <core/renderer/RenderMaskHolder.hpp>\n# include <core/scene/BasicIBRScene.hpp>\n# include <core/system/SimpleTimer.hpp>\n\nnamespace sibr { \n\t\n\t/**\n\t * \\class ULRV3Renderer\n\t * \\brief Perform per-pixel Unstructured Lumigraph Rendering (Buehler et al., 2001). No selection is done on the CPU side.\n\t * Relies on texture arrays and uniform buffer objects to support a high number of cameras. \n\t */\n\tclass SIBR_EXP_ULR_EXPORT ULRV3Renderer : public RenderMaskHolderArray\n\t{\n\t\tSIBR_CLASS_PTR(ULRV3Renderer);\n\t\n\tpublic:\n\n\t\t/**\n\t\t * Constructor.\n\t\t * \\param cameras The input cameras to use.\n\t\t * \\param w The width of the internal rendertargets.\n\t\t * \\param h The height of the internal rendertargets.\n\t\t * \\param fShader An optional name of the fragment shader to use (default to ulr_v3).\n\t\t * \\param vShader An optional name of the vertex shader to use (default to ulr_v3).\n\t\t * \\param facecull Should the mesh be renderer with backface culling.\n\t\t */\n\t\tULRV3Renderer(const std::vector<InputCamera::Ptr> & cameras, \n\t\t\tconst uint w, const uint h, \n\t\t\tconst std::string & fShader = \"ulr/ulr_v3\", \n\t\t\tconst std::string & vShader = \"ulr/ulr_v3\", \n\t\t\tconst bool facecull = true\n\t\t);\n\n\t\t/**\n\t\t * Change the shaders used by the ULR renderer.\n\t\t * \\param fShader The name of the fragment shader to use.\n\t\t * \\param vShader The name of the vertex shader to use.\n\t\t */\n\t\tvirtual void setupShaders(\n\t\t\tconst std::string & fShader = \"ulr/ulr_v3\",\n\t\t\tconst std::string & vShader = \"ulr/ulr_v3\"\n\t\t);\n\n\t\t/**\n\t\t * Performs ULR rendering to a given destination rendertarget.\n\t\t * \\param mesh The mesh to use as geometric proxy.\n\t\t * \\param eye The novel viewpoint.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param inputRGBs A texture array containing the input RGB images.\n\t\t * \\param inputDepths A texture array containing the input depth maps.\n\t\t * \\param passthroughDepth If true, depth from the position map will be output to the depth buffer for ulterior passes.\n\t\t */\n\t\tvirtual void process(\n\t\t\tconst sibr::Mesh & mesh,\n\t\t\tconst sibr::Camera& eye,\n\t\t\tIRenderTarget& dst,\n\t\t\tconst sibr::Texture2DArrayRGB::Ptr & inputRGBs,\n\t\t\tconst sibr::Texture2DArrayLum32F::Ptr & inputDepths,\n\t\t\tbool passthroughDepth = false\n\t\t\t);\n\n\t\t/**\n\t\t * Performs ULR rendering to a given destination rendertarget.\n\t\t * \\param mesh The mesh to use as geometric proxy.\n\t\t * \\param eye The novel viewpoint.\n\t\t * \\param dst The destination rendertarget.\n\t\t * \\param inputRGBHandle The handle of a texture array containing the input RGB images.\n\t\t * \\param inputDepths A texture array containing the input depth maps.\n\t\t * \\param passthroughDepth If true, depth from the position map will be output to the depth buffer for ulterior passes.\n\t\t */\n\t\tvirtual void process(\n\t\t\tconst sibr::Mesh & mesh,\n\t\t\tconst sibr::Camera& eye,\n\t\t\tIRenderTarget& dst,\n\t\t\tuint inputRGBHandle,\n\t\t\tconst sibr::Texture2DArrayLum32F::Ptr & inputDepths,\n\t\t\tbool passthroughDepth = false\n\t\t);\n\n\t\t/** \n\t\t *  Update which cameras should be used for rendering, based on the indices passed.\n\t\t *  \\param camIds The indices to enable.\n\t\t **/\n\t\tvoid updateCameras(const std::vector<uint> & camIds);\n\n\t\t/// Set the epsilon occlusion threshold.\n\t\tfloat & epsilonOcclusion() { return _epsilonOcclusion.get(); }\n\n\t\t/// Enable or disable the masks.\n\t\tbool & useMasks() { return _useMasks.get(); }\n\n\t\t/// Flip the RGB images before using them.\n\t\tbool & flipRGBs() { return _flipRGBs.get(); }\n\n\t\t/// Enable or diable occlusion testing.\n\t\tbool& occTest() { return _occTest.get(); }\n\n\t\t/// Show debug weights.\n\t\tbool & showWeights() { return _showWeights.get(); }\n\n\t\t/// Set winner takes all weights strategy\n\t\tbool & winnerTakesAll() { return _winnerTakesAll.get(); }\n\n\t\t/// Apply gamma correction to the output.\n\t\tbool & gammaCorrection() { return _gammaCorrection.get(); }\n\n\t\t/// Apply backface culling to the mesh.\n\t\tbool & backfaceCull() { return _backFaceCulling; }\n\n\t\t/** Resize the internal rendertargets.\n\t\t *\\param w the new width\n\t\t *\\param h the new height\n\t\t **/\n\t\tvoid resize(const unsigned int w, const unsigned int h);\n\n\t\t/// Should the final RT be cleared or not.\n\t\tbool & clearDst() { return _clearDst; }\n\n\t\t/// \\return The ID of the first pass position map texture.\n\t\tuint depthHandle() const { return _depthRT->texture(); }\n\n\t\tvoid startProfile() { \n\t\t\t_profiling = true; \n\t\t\t_depthCost.clear();\n\t\t\t_blendCost.clear();\n\t\t}\n\n\t\tvoid stopProfile();\n\n\t\t/**\n\t\t * Render the world positions of the proxy points in an intermediate rendertarget.\n\t\t * \\param mesh the proxy mesh.\n\t\t * \\param eye The novel viewpoint.\n\t\t */\n\t\tvirtual void renderProxyDepth(const sibr::Mesh & mesh, const sibr::Camera& eye);\n\n\t\t/**\n\t\t* Perform ULR blending.\n\t\t* \\param eye The novel viewpoint.\n\t\t* \\param dst The destination rendertarget.\n\t\t* \\param inputRGBHandle The handle to a texture array containing the input RGB images.\n\t\t* \\param inputDepths A texture array containing the input depth maps.\n\t\t* \\param passthroughDepth If true, depth from the position map will be output to the depth buffer for ulterior passes.\n\t\t*/\n\t\tvirtual void renderBlending(\n\t\t\tconst sibr::Camera& eye,\n\t\t\tIRenderTarget& dst,\n\t\t\tuint inputRGBHandle,\n\t\t\tconst sibr::Texture2DArrayLum32F::Ptr & inputDepths,\n\t\t\tbool passthroughDepth\n\t\t);\n\n\n\tprotected:\n\t\t/// Shader names.\n\t\tstd::string fragString, vertexString;\n\n\t\tsibr::GLShader _ulrShader;\n\t\tsibr::GLShader _depthShader;\n\n\t\tsibr::RenderTargetRGBA32F::Ptr\t\t_depthRT;\n\t\tGLuniform<Matrix4f>\t\t\t\t\t_nCamProj;\n\t\tGLuniform<Vector3f>\t\t\t\t\t_nCamPos;\n\n\t\tGLuniform<bool>\n\t\t\t_occTest = true,\n\t\t\t_useMasks = false,\n\t\t\t_discardBlackPixels = true,\n\t\t\t_areMasksBinary = true,\n\t\t\t_invertMasks = false,\n\t\t\t_flipRGBs = false,\n\t\t\t_showWeights = false,\n\t\t\t_winnerTakesAll = false,\n\t\t\t_gammaCorrection = false;\n\n\t\tsize_t _maxNumCams = 0;\n\t\tGLuniform<int> _camsCount = 0;\n\n\t\tGLuniform<float>\t\t\t\t\t_epsilonOcclusion = 0.01f;\n\t\tbool\t\t\t\t\t\t\t\t_backFaceCulling = true;\n\t\tbool\t\t\t\t\t\t\t\t_clearDst = true;\n\n\t\t/** Camera infos data structure shared between the CPU and GPU.\n\t\t\tWe have to be careful about alignment if we want to send those struct directly into the UBO. */\n\t\tstruct CameraUBOInfos {\t \n\t\t\tMatrix4f vp; ///< Matrix viewproj.\n\t\t\tVector3f pos; ///< Camera position.\n\t\t\tint selected = 0; ///< Is the camera selected (0/1).\n\t\t\tVector3f dir; ///< Camera direction.\n\t\t\tfloat dummy = 0.0f; ///< Padding to a multiple of 16 bytes for alignment on the GPU.\n\t\t};\n\n\t\tstd::vector<CameraUBOInfos> _cameraInfos;\n\t\tGLuint _uboIndex;\n\n\t\tbool\t\t_profiling = false;\n\t\tsibr::Timer\t_depthPassTimer;\n\t\tsibr::Timer\t_blendPassTimer;\n\t\tint\t\t\t\t\t\t\t\t\t\t\t_numFramesProfiling = 100;\n\t\tstd::string\t\t\t\t\t\t\t\t\t_profileStr = \"\";\n\t\tstd::vector<float>\t\t\t\t\t\t\t_depthCost, _blendCost;\n\n\t};\n\n\n} /*namespace sibr*/ \n\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV3View.cpp",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#include <projects/ulr/renderer/ULRV3View.hpp>\n#include <core/graphics/GUI.hpp>\n\nsibr::ULRV3View::ULRV3View(const sibr::BasicIBRScene::Ptr & ibrScene, uint render_w, uint render_h) :\n\t_scene(ibrScene),\n\tsibr::ViewBase(render_w, render_h)\n{\n\tconst uint w = render_w;\n\tconst uint h = render_h;\n\n\t//  Renderers.\n\t_ulrRenderer.reset(new ULRV3Renderer(ibrScene->cameras()->inputCameras(), w, h));\n\t_poissonRenderer.reset(new PoissonRenderer(w, h));\n\t_poissonRenderer->enableFix() = true;\n\n\t// Rendertargets.\n\t_poissonRT.reset(new RenderTargetRGBA(w, h, SIBR_CLAMP_UVS));\n\t_blendRT.reset(new RenderTargetRGBA(w, h, SIBR_CLAMP_UVS));\n\n\t// Tell the scene we are a priori using all active cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto & cams = ibrScene->cameras()->inputCameras();\n\tfor(size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif(cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n}\n\nvoid sibr::ULRV3View::setScene(const sibr::BasicIBRScene::Ptr & newScene) {\n\t_scene = newScene;\n\tconst uint w = getResolution().x();\n\tconst uint h = getResolution().y();\n\n\tstd::string shaderName = \"ulr_v3\";\n\tif (_weightsMode == VARIANCE_BASED_W) {\n\t\tshaderName = \"ulr_v3_alt\";\n\t}\n\telse if (_weightsMode == ULR_FAST) {\n\t\tshaderName = \"ulr_v3_fast\";\n\t}\n\n\t_ulrRenderer.reset(new ULRV3Renderer(newScene->cameras()->inputCameras(), w, h, shaderName));\n\n\t// Tell the scene we are a priori using all active cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto & cams = newScene->cameras()->inputCameras();\n\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\tif (cams[cid]->isActive()) {\n\t\t\timgs_ulr.push_back(uint(cid));\n\t\t}\n\t}\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n}\n\nvoid sibr::ULRV3View::setMode(const WeightsMode mode) {\n\t_weightsMode = mode;\n\tif (_weightsMode == VARIANCE_BASED_W) {\n\t\t_ulrRenderer->setupShaders(\"ulr/ulr_v3_alt\");\n\t}\n\telse if (_weightsMode == ULR_FAST) {\n\t\t_ulrRenderer->setupShaders(\"ulr/ulr_v3_fast\");\n\t}\n\telse {\n\t\t_ulrRenderer->setupShaders();\n\t}\n}\n\nvoid sibr::ULRV3View::onRenderIBR(sibr::IRenderTarget & dst, const sibr::Camera & eye)\n{\n\t// Perform ULR rendering, either directly to the destination RT, or to the intermediate RT when poisson blending is enabled.\n\t_ulrRenderer->process(\n\t\t\t_scene->proxies()->proxy(),\n\t\t\teye, \n\t\t\t_poissonBlend ? *_blendRT : dst,\n\t\t\t_scene->renderTargets()->getInputRGBTextureArrayPtr(),\n\t\t_scene->renderTargets()->getInputDepthMapArrayPtr()\n\t\t);\n\n\t// Perform Poisson blending if enabled and copy to the destination RT.\n\tif (_poissonBlend) {\n\t\t_poissonRenderer->process(_blendRT, _poissonRT);\n\t\tblit(*_poissonRT, dst);\n\t}\n\n}\n\nvoid sibr::ULRV3View::onUpdate(Input & input)\n{\n}\n\nvoid sibr::ULRV3View::onGUI()\n{\n\tconst std::string guiName = \"ULRV3 Settings (\" + name() + \")\";\n\tif (ImGui::Begin(guiName.c_str())) {\n\n\t\t// Poisson settings.\n\t\tImGui::Checkbox(\"Poisson \", &_poissonBlend); ImGui::SameLine();\n\t\tImGui::Checkbox(\"Poisson fix\", &_poissonRenderer->enableFix());\n\n\t\t// Other settings.\n\t\tImGui::Checkbox(\"Flip RGB \", &getULRrenderer()->flipRGBs());\n\t\tImGui::PushScaledItemWidth(150);\n\t\tImGui::InputFloat(\"Epsilon occlusion\", &_ulrRenderer->epsilonOcclusion(), 0.001f, 0.01f);\n\n\t\tImGui::Separator();\n\t\t// Rendering mode selection.\n\t\tif(ImGui::Combo(\"Rendering mode\", (int*)(&_renderMode), \"Standard\\0One image\\0Leave one out\\0Every N\\0\\0\")) {\n\t\t\tupdateCameras(true);\n\t\t}\n\n\t\t// Get the desired index, make sure it falls in the cameras range.\n\t\tif (_renderMode == ONE_CAM || _renderMode == LEAVE_ONE_OUT) {\n\t\t\tconst bool changedIndex = ImGui::InputInt(\"Selected image\", &_singleCamId, 1, 10);\n\t\t\t_singleCamId = sibr::clamp(_singleCamId, 0, (int)_scene->cameras()->inputCameras().size() - 1);\n\t\t\tif (changedIndex) {\n\t\t\t\t// If we are in \"leave one out\" or \"one camera only\" mode, we have to update the list of enabled cameras.\n\t\t\t\tupdateCameras(false);\n\t\t\t}\n\t\t}\n\n\t\tif (_renderMode == EVERY_N_CAM) {\n\t\t\tif (ImGui::InputInt(\"Selection step\", &_everyNCamStep, 1, 10)) {\n\t\t\t\t_everyNCamStep = std::max(1, _everyNCamStep);\n\t\t\t\tupdateCameras(false);\n\t\t\t}\n\t\t}\n\t\tImGui::Separator();\n\t\t// Switch the shaders for ULR rendering.\n\t\tif (ImGui::Combo(\"Weights mode\", (int*)(&_weightsMode), \"Standard ULR\\0Variance based\\0Fast ULR\\0\\0\")) {\n\t\t\tsetMode(_weightsMode);\n\t\t}\n\t\t\n\t\tImGui::Checkbox(\"Occlusion Testing\", &_ulrRenderer->occTest());\n\t\tImGui::Checkbox(\"Debug weights\", &_ulrRenderer->showWeights());\n\t\tImGui::Checkbox(\"Gamma correction\", &_ulrRenderer->gammaCorrection());\n\t\tImGui::PopItemWidth();\n\t}\n\tImGui::End();\n}\n\nvoid sibr::ULRV3View::updateCameras(bool allowResetToDefault) {\n\t// If we are here, the rendering mode or the selected index have changed, we need to update the enabled cameras.\n\tstd::vector<uint> imgs_ulr;\n\tconst auto & cams = _scene->cameras()->inputCameras();\n\n\t// Compute the cameras indices based on the new mode.\n\tif (_renderMode == RenderMode::ONE_CAM) {\n\t\t// We only use the given camera (if it is active).\n\t\tif (cams[_singleCamId]->isActive()) {\n\t\t\timgs_ulr = { (uint)_singleCamId };\n\t\t} else {\n\t\t\tstd::cerr << \"The camera is not active, using all cameras.\" << std::endl;\n\t\t}\n\t} else if (_renderMode == RenderMode::LEAVE_ONE_OUT) {\n\t\t// We use all active cameras apart from the one given.\n\t\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\t\tif (cid != (size_t)_singleCamId && cams[cid]->isActive()) {\n\t\t\t\timgs_ulr.push_back(uint(cid));\n\t\t\t}\n\t\t}\n\t}\n\telse if (_renderMode == RenderMode::EVERY_N_CAM) {\n\t\t// We pick one camera every N\n\t\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\t\tif ((cid % _everyNCamStep == 0) && cams[cid]->isActive()) {\n\t\t\t\timgs_ulr.push_back(uint(cid));\n\t\t\t}\n\t\t}\n\t} else if(allowResetToDefault){\n\t\t// We use all active cameras.\n\t\tfor (size_t cid = 0; cid < cams.size(); ++cid) {\n\t\t\tif (cams[cid]->isActive()) {\n\t\t\t\timgs_ulr.push_back(uint(cid));\n\t\t\t}\n\t\t}\n\t}\n\t// Only update if there is at least one camera enabled.\n\tif(!imgs_ulr.empty()) {\n\t\t// Update the shader informations in the renderer.\n\t\t_ulrRenderer->updateCameras(imgs_ulr);\n\t\t// Tell the scene which cameras we are using for debug visualization.\n\t\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\n\t}\n\t\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRV3View.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"Config.hpp\"\r\n# include <core/system/Config.hpp>\r\n# include <core/graphics/Mesh.hpp>\r\n# include <core/view/ViewBase.hpp>\r\n# include <core/renderer/CopyRenderer.hpp>\r\n# include <projects/ulr/renderer/ULRV3Renderer.hpp>\r\n# include <core/renderer/PoissonRenderer.hpp>\r\n\r\nnamespace sibr { \r\n\r\n\t/**\r\n\t * \\class ULRV3View\r\n\t * \\brief Wrap a ULR renderer with additional parameters and information.\r\n\t */\r\n\tclass SIBR_EXP_ULR_EXPORT ULRV3View : public sibr::ViewBase\r\n\t{\r\n\t\tSIBR_CLASS_PTR(ULRV3View);\r\n\r\n\t\t/// Rendering mode: default, use only one camera, use all cameras but one.\r\n\t\tenum RenderMode { ALL_CAMS, ONE_CAM, LEAVE_ONE_OUT, EVERY_N_CAM };\r\n\r\n\t\t/// Blending mode: keep the four best values per pixel, or aggregate them all.\r\n\t\tenum WeightsMode { ULR_W , VARIANCE_BASED_W, ULR_FAST};\r\n\r\n\tpublic:\r\n\r\n\t\t/**\r\n\t\t * Constructor\r\n\t\t * \\param ibrScene The scene to use for rendering.\r\n\t\t * \\param render_w rendering width\r\n\t\t * \\param render_h rendering height\r\n\t\t */\r\n\t\tULRV3View(const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h);\r\n\r\n\t\t/** Replace the current scene.\r\n\t\t *\\param newScene the new scene to render */\r\n\t\tvoid setScene(const sibr::BasicIBRScene::Ptr & newScene);\r\n\r\n\t\t/**\r\n\t\t * Perform rendering. Called by the view manager or rendering mode.\r\n\t\t * \\param dst The destination rendertarget.\r\n\t\t * \\param eye The novel viewpoint.\r\n\t\t */\r\n\t\tvoid onRenderIBR(sibr::IRenderTarget& dst, const sibr::Camera& eye) override;\r\n\r\n\t\t/**\r\n\t\t * Update inputs (do nothing).\r\n\t\t * \\param input The inputs state.\r\n\t\t */\r\n\t\tvoid onUpdate(Input& input) override;\r\n\r\n\t\t/**\r\n\t\t * Update the GUI.\r\n\t\t */\r\n\t\tvoid onGUI() override;\r\n\r\n\t\t/** \\return a reference to the renderer. */\r\n\t\tconst ULRV3Renderer::Ptr & getULRrenderer() const { return _ulrRenderer; }\r\n\r\n\t\t/** Set the renderer blending weights mode.\r\n\t\t *\\param mode the new mode to use\r\n\t\t *\\sa WeightsMode\r\n\t\t **/\r\n\t\tvoid setMode(const WeightsMode mode);\r\n\r\n\t\t/** \\return a reference to the scene */\r\n\t\tconst std::shared_ptr<sibr::BasicIBRScene> & getScene() const { return _scene; }\r\n\r\n\tprotected:\r\n\r\n\t\t/**\r\n\t\t * Update the camera informations in the ULR renderer based on the current rendering mode and selected index.\r\n\t\t * \\param allowResetToDefault If true, when the rendering mode is ALL_CAMS, the cameras information will be updated.\r\n\t\t */\r\n\t\tvoid updateCameras(bool allowResetToDefault);\r\n\r\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene; ///< The current scene.\r\n\t\tULRV3Renderer::Ptr\t\t_ulrRenderer; ///< The ULR renderer.\r\n\t\tPoissonRenderer::Ptr\t_poissonRenderer; ///< The poisson filling renderer.\r\n\r\n\t\tRenderTargetRGBA::Ptr\t_blendRT; ///< ULR destination RT.\r\n\t\tRenderTargetRGBA::Ptr\t_poissonRT; ///< Poisson filling destination RT.\r\n\r\n\t\tbool\t\t\t\t\t_poissonBlend = false; ///< Should Poisson filling be applied.\r\n\r\n\t\tRenderMode\t\t\t\t_renderMode = ALL_CAMS; ///< Current rendering mode.\r\n\t\tWeightsMode\t\t\t\t_weightsMode = ULR_W; ///< Current blend weights mode.\r\n\t\tint\t\t\t\t\t\t_singleCamId = 0; ///< Selected camera for the single view mode.\r\n\t\tint\t\t\t\t\t\t_everyNCamStep = 1; ///< Camera step size for the every other N mode.\r\n\t};\r\n\r\n} /*namespace sibr*/ \r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRView.cpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n/// \\todo TODO: make shorter\r\n#include \"Config.hpp\"\r\n#include <core/assets/Resources.hpp>\r\n#include <projects/ulr/renderer/ULRView.hpp>\r\n#include <core/system/Vector.hpp>\r\n#include <core/graphics/Texture.hpp>\r\n#include <core/view/ViewBase.hpp>\r\n#include <map>\r\n\r\nnamespace sibr { \r\n\r\nULRView::~ULRView( )\r\n{\r\n\t_altMesh.reset();\r\n}\r\n\r\nULRView::ULRView( const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h ) :\r\n\t_scene(ibrScene),\r\n\t\tsibr::ViewBase(render_w, render_h)\r\n{\r\n\t_altMesh.reset();\r\n\t_altMesh = nullptr;\r\n\t_numDistUlr = 10, _numAnglUlr = 14;\r\n    std::cerr << \"\\n[ULRenderer] setting number of images to blend \"<< _numDistUlr << \" \" << _numAnglUlr << std::endl;\r\n\r\n\t_ulr.reset(new ULRRenderer(render_w, render_h));\r\n\t\r\n\t_inputRTs = ibrScene->renderTargets()->inputImagesRT();\r\n}\r\n\r\nvoid ULRView::onRenderIBR( sibr::IRenderTarget& dst, const sibr::Camera& eye ) {\r\n    // Select subset of input images for ULR\r\n\tstd::vector<uint> imgs_ulr = chosen_cameras(eye);\r\n\t_scene->cameras()->debugFlagCameraAsUsed(imgs_ulr);\r\n\t_ulr->process(\r\n\t\t/* input -- images chosen */ imgs_ulr, \r\n\t\t/* input -- camera position */ eye, \r\n\t\t/* input -- scene */ _scene, \r\n\t\t/* input -- alt mesh if available */ _altMesh, \r\n\t\t/* input -- input RTs -- can be RGB or alpha */  _inputRTs,\r\n\t\t/* output */ dst);\r\n}\r\n\r\n// -----------------------------------------------------------------------\r\n/// \\todo Select a subset from imput images speed up URL\r\n/// \\todo TODO: This function needs serious cleanup\r\n//\r\nstd::vector<uint> ULRView::chosen_cameras(const sibr::Camera& eye) {\r\n    std::vector<uint> imgs_id;\r\n    std::multimap<float,uint> distMap;\t\t\t\t\t\t\t\t\t// distance wise closest input cameras\r\n\tstd::multimap<float,uint> dang;\t\t\t\t\t\t\t\t\t// angular distance from inputs to novel camera\r\n    for (uint i=0; i<_scene->cameras()->inputCameras().size(); i++ ) {\r\n        const sibr::InputCamera& inputCam = *_scene->cameras()->inputCameras()[i];\r\n        if (inputCam.isActive()) {\r\n\t\t\t// Convert following to Eigen versions\r\n            float dist = sibr::distance(inputCam.position(), eye.position());\r\n            float angle = sibr::dot(inputCam.dir(),eye.dir());\r\n //           if (angle > 0.707) {\t\t\t\t\t\t\t\t\t// cameras with 45 degrees\r\n                distMap.insert(std::make_pair(dist,i));\t\t\t\t\t// sort distances in increasing order\r\n\t\t\t\tdang.insert(std::make_pair( acos(angle),i));\t\t\t\t// sort angles in increasing order\r\n//\t\t\t}\r\n        }\r\n    }\r\n\r\n   // HACK GD -- should really look at camera angles as well and sort them\r\n////   bool not_enough = false;\r\n\t// if you have < 2 cameras, choose the (NUM_DIST+NUM_ANGL)/2 closest ones\r\n////   if( dang.size() + distMap.size() < 2 )\r\n////\t\tnot_enough = true;\r\n\tfor (uint i=0; i< _scene->cameras()->inputCameras().size(); i++) {\r\n        const sibr::InputCamera& inputCam = *_scene->cameras()->inputCameras()[i];\r\n        if (inputCam.isActive() && distMap.size() <= (_numDistUlr+_numAnglUlr)/2 ) {\r\n            float dist = sibr::distance(inputCam.position(),eye.position());\r\n            distMap.insert(std::make_pair(dist,i));\t\t\t\t\t// sort distances in increasing order\r\n\t\t\t}\r\n\t}\r\n\r\n    std::multimap<float,uint>::const_iterator d_it(distMap.begin());\t// select the _numDistUlr closest cameras\r\n\tfor (int i=0; d_it!=distMap.end() && i<_numDistUlr; d_it++,i++) {\r\n        imgs_id.push_back(d_it->second);\r\n    }\r\n\r\n\tstd::multimap<float,uint>::const_iterator a_it(dang.begin());    // select the NUM_ANG_ULR closest cameras\r\n\tfor (int i=0; a_it!=dang.end() && i<_numAnglUlr; a_it++,i++) {\r\n        imgs_id.push_back(a_it->second);\r\n    }\r\n\r\n\tstd::sort( imgs_id.begin(), imgs_id.end() );\t\t\t\t// Avoid repetitions\r\n\timgs_id.erase( std::unique( imgs_id.begin(), imgs_id.end() ), imgs_id.end() );\r\n\r\n\tSIBR_ASSERT(imgs_id.size() <= _numDistUlr + _numAnglUlr);\r\n    return imgs_id;\r\n}\r\n\r\nvoid ULRView::setMasks( const std::vector<RenderTargetLum::Ptr>& masks ) {\r\n\t\t_ulr->setMasks(masks);\r\n}\r\n\r\n} /*namespace sibr*/ \r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/ULRView.hpp",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#pragma once\r\n\r\n# include \"Config.hpp\"\r\n# include <core/system/Config.hpp>\r\n# include <core/graphics/Mesh.hpp>\r\n# include <core/scene/BasicIBRScene.hpp>\r\n# include <core/renderer/CopyRenderer.hpp>\r\n# include <projects/ulr/renderer/ULRRenderer.hpp>\r\n# include <core/view/ViewBase.hpp>\r\n\r\nnamespace sibr { \r\n\r\n\t/** View associated to ULRRenderer v1, providing interface and options. */\r\n\tclass SIBR_EXP_ULR_EXPORT ULRView : public sibr::ViewBase\r\n\t{\r\n\t\tSIBR_CLASS_PTR( ULRView );\r\n\r\n\tpublic:\r\n\r\n\t\t/** Constructor.\r\n\t\t *\\param ibrScene the scene\r\n\t\t *\\param render_w rendering width\r\n\t\t *\\param render_h rendering height\r\n\t\t **/\r\n\t\tULRView( const sibr::BasicIBRScene::Ptr& ibrScene, uint render_w, uint render_h );\r\n\r\n\t\t/** Destructor. */\r\n\t\t~ULRView();\r\n\r\n\t\t/** Render using the ULR algorithm. \r\n\t\t *\\param dst destination target\r\n\t\t *\\param eye novel viewpoint\r\n\t\t **/\r\n\t\tvirtual void onRenderIBR( sibr::IRenderTarget& dst, const sibr::Camera& eye );\r\n\r\n\t\t/** Select input cameras to use for rendering.\r\n\t\t *\\param eye the current viewpoint\r\n\t\t *\\return a list of camera indices.\r\n\t\t **/\r\n\t\tvirtual std::vector<uint> chosen_cameras(const sibr::Camera& eye) ;\r\n\r\n\t\t/** Set the altMesh and use instead of scene proxy.\r\n\t\t *\\param m mesh to use\r\n\t\t **/\r\n\t\tvoid\taltMesh(std::shared_ptr<sibr::Mesh> m)\t{ _altMesh = m; }\r\n\r\n\t\t/** Toggle occlusion testing.\r\n\t\t *\\param val should occlusion testing be performed\r\n\t\t */\r\n\t\tvoid\tdoOccl(bool val) { _ulr->doOccl(val); }\r\n\r\n\t\t/** \\return a pointer to the alt mesh if it exists */\r\n\t\tstd::shared_ptr<sibr::Mesh> \taltMesh()\t{ return _altMesh; }\r\n\r\n\t\t/** Set the number of cmaeras to select for blending.\r\n\t\t *\\param dist number of cameras for the distance criterion\r\n\t\t *\\param angle number of cameras for the angle criterion\r\n\t\t **/\r\n\t\tvoid\tsetNumBlend(short int dist, short int angle) { _numDistUlr = dist, _numAnglUlr = angle; }\r\n\r\n\t\t/** Set the input RGBD textures.\r\n\t\t *\\param iRTs the new textures to use. \r\n\t\t */\r\n\t\tvoid\tinputRTs(const std::vector<std::shared_ptr<RenderTargetRGBA32F> >& iRTs) { _inputRTs = iRTs;}\r\n\r\n\t\t/** Set the masks for ignoring some regions of the input images.\r\n\t\t *\\param masks the new masks\r\n\t\t **/\r\n\t\tvoid\tsetMasks( const std::vector<RenderTargetLum::Ptr>& masks);\r\n\r\n\tprotected:\r\n\r\n\t\tULRRenderer::Ptr\t\t_ulr; ///< Renderer.\r\n\t\tstd::shared_ptr<sibr::BasicIBRScene> _scene; ///< Scene.\r\n\t\tstd::shared_ptr<sibr::Mesh>\t_altMesh; ///< For the cases when using a different mesh than the scene\r\n\t\tshort int _numDistUlr, _numAnglUlr; ///< max number of selected cameras for each criterion.\r\n\t\tstd::vector<std::shared_ptr<RenderTargetRGBA32F> > _inputRTs; ///< input RTs -- usually RGB but can be alpha or other\r\n\r\n\t};\r\n\r\n} /*namespace sibr*/ \r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) in vec3 in_vertex;\nlayout(location = 2) in vec3 in_normal;\n\nout vec3 vertex_coord;\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex,1.0);\n    vertex_coord  = in_vertex;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr1.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location=0) out vec4 out_color0;\nlayout(location=1) out vec4 out_color1;\nlayout(location=2) out vec4 out_color2;\nlayout(location=3) out vec4 out_color3;\n\nlayout(binding=0) uniform sampler2D image;    // input image with camera-proxy distance in alpha\nlayout(binding=1) uniform sampler2D proxy;    // intersection point with proxy with depth in alpha\nlayout(binding=2) uniform sampler2D texture0; // best candidate for each pixel\nlayout(binding=3) uniform sampler2D texture1; // second best candidate for each pixel\nlayout(binding=4) uniform sampler2D texture2; // third best candidate for each pixel\nlayout(binding=5) uniform sampler2D texture3; // fourth best candidate for each pixel\nlayout(binding=6) uniform sampler2D mask; // masking texture.\n\nuniform mat4 iCamProj;     // input camera projection\nuniform vec3 iCamPos;      // input camera position\nuniform vec3 iCamDir;      // novel camera projection\nuniform vec3 nCamPos;      // novel camera position\nuniform bool occlTest;\t// do occlusion test\nuniform bool doMasking;\t// do masking\n\n// vertex coordinates of the 2D screen size quad,\n// used for computing texture coordinates\nin vec3 vertex_coord;\n\n#define EPSILON 1e-2\n#define BETA \t1e-1  \t/* Relative importance of resolution penalty */\n\nvec3 project(vec3 point, mat4 proj) {\n  vec4 p1 = proj * vec4(point, 1.0);\n  vec3 p2 = (p1.xyz/p1.w);\n  return (p2.xyz*vec3(0.5) + vec3(0.5));\n}\n\nbool frustumTest(vec3 p, vec2 uv) {\n  vec3 d1 = iCamDir;\n  vec3 d2 = p - iCamPos;\n  bool r = dot(d1,d2)>0.0 && uv.x<1.0 && uv.x>0.0 && uv.y<1.0 && uv.y>0.0;\n  return r;\n}\n\nvoid main(void) {\n\n  float penalty_res = 0;  \t\t/* Resolution penalty */\n  float penalty_ang = 0;\t\t\t/* Angular penalty */\n\n  vec2 texcoord = (vertex_coord.xy + vec2(1.0)) / 2.0;\n\n  vec4  point  = texture(proxy,    texcoord);\n  vec4  color0 = texture(texture0, texcoord);\n  vec4  color1 = texture(texture1, texcoord);\n  vec4  color2 = texture(texture2, texcoord);\n  vec4  color3 = texture(texture3, texcoord);\n  \n  vec3 uvd = project(point.xyz, iCamProj);\n  vec2 uv = uvd.xy;\n\n  vec4 color = texture(image, uv);\n\n  out_color0 = color0;\n  out_color1 = color1;\n  out_color2 = color2;\n  out_color3 = color3;\n\n  if(doMasking){\n    float masked = texture(mask, uv).r;\n    if(masked < 0.5){\n\t return;\n    }\n  }\n\n  if (frustumTest(point.xyz, uv))\n  {\n    float dist_i2p \t= distance(point.xyz, iCamPos);\n    float dist_n2p \t= distance(point.xyz, nCamPos);\n    penalty_res \t= max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\n\n    //if (abs(dist-color.w) < EPSILON) {\n\n    vec3 v1 = normalize(point.xyz - iCamPos);\n    vec3 v2 = normalize(point.xyz - nCamPos);\n    if (occlTest && abs(uvd.z-color.w) < EPSILON) {\t\t/* occlusion test */\n      //color.w = max(0.0001, acos(dot(v1,v2)));\n      penalty_ang = max(0.0001, acos(dot(v1,v2)));\n      } else if( occlTest ) {\n        return;;\n        //color.w = 5.0 + max(0.001, acos(dot(v1,v2))); /* increase the penalty */\n      }\n\t  if (all(equal(color.xyz, vec3(0,0,0)))){\n\t\treturn;\n\t\t}\n\n      color.w = penalty_ang + BETA*penalty_res;\n\n      // compare with best four candiates and insert at the\n      // appropriate rank\n      bool done = false;\n      if (!done && color.w<color0.w) {    // better than best candidate\n        out_color0 = color;\n        out_color1 = color0;\n        out_color2 = color1;\n        out_color3 = color2;\n        done = true;\n      }\n      if (!done && color.w<color1.w) {    // better than second best candidate\n        out_color0 = color0;\n        out_color1 = color;\n        out_color2 = color1;\n        out_color3 = color2;\n        done = true;\n      }\n      if (!done && color.w<color2.w) {    // better than third best candidate\n        out_color0 = color0;\n        out_color1 = color1;\n        out_color2 = color;\n        out_color3 = color2;\n        done = true;\n      }\n      if (!done && color.w<color3.w) {    // better than fourth best candidate\n        out_color0 = color0;\n        out_color1 = color1;\n        out_color2 = color2;\n        out_color3 = color;\n        done = true;\n      }\n    }\n  }\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr2.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location=0) out vec4 out_color;\n\nlayout(binding=0) uniform sampler2D proxy;  // intersection point with proxy with depth in alpha\nlayout(binding=1) uniform sampler2D ulr0;   // best candidate for each pixel\nlayout(binding=2) uniform sampler2D ulr1;   // second best candidate for each pixel\nlayout(binding=3) uniform sampler2D ulr2;   // third best candidate for each pixel\nlayout(binding=4) uniform sampler2D ulr3;   // fourth best candidate for each pixel\n\n// vertex coordinates of the 2D screen size quad,\n// used for computing texture coordinates\nin vec3 vertex_coord;\n\nvoid main(void) {\n    vec2 texcoord = (vertex_coord.xy + vec2(1.0)) / 2.0;\n\n    vec4  point  = texture(proxy, texcoord);\n    vec4  color0 = texture(ulr0,  texcoord);\n    vec4  color1 = texture(ulr1,  texcoord);\n    vec4  color2 = texture(ulr2,  texcoord);\n    vec4  color3 = texture(ulr3,  texcoord);\n\n    float thresh = 1.0000001 * color3.w;\n    color0.w = max(0, 1.0 - color0.w/thresh);\n    color1.w = max(0, 1.0 - color1.w/thresh);\n    color2.w = max(0, 1.0 - color2.w/thresh);\n    color3.w = max(0, 1.0 - color3.w/thresh);\n\n    // ignore any candidate which is black\n    if (all(equal(color0.xyz, vec3(0,0,0)))) color0.w = 0;\n    if (all(equal(color1.xyz, vec3(0,0,0)))) color1.w = 0;\n    if (all(equal(color2.xyz, vec3(0,0,0)))) color2.w = 0;\n    if (all(equal(color3.xyz, vec3(0,0,0)))) color3.w = 0;\n\n    // only blend the best two candidates\n    /*color2.w = 0.00001;\n    color3.w = 0.00001;*/\n\n    // blending\n    out_color.xyz = (color0.xyz*color0.w +\n            color1.xyz*color1.w +\n            color2.xyz*color2.w +\n            color3.xyz*color3.w\n            ) / (color0.w + color1.w + color2.w + color3.w);\n    out_color.w = 1.0;\n    gl_FragDepth = point.w;\n\n    // discard if there was no intersection with the proxy\n    if (point.w <= 0) {\n        discard;\n    }\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_intersect.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nlayout(location = 0) out vec4 out_color;\n\nin vec3 vertex_coord;\n\nvoid main(void) {\n\tout_color = vec4(vertex_coord, gl_FragCoord.z);\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_intersect.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\nuniform mat4 proj;\n\nlayout(location = 0) in vec3 in_vertex;\n\nout vec3 vertex_coord;\n\nvoid main(void) {\n\tgl_Position = proj * vec4(in_vertex,1.0);\n    vertex_coord  = in_vertex;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v2.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n#define NUM_CAMS (12)\n\nlayout(location = 0) out vec4 out_color;\n\nlayout(binding=0) uniform sampler2D proxy;\nlayout(binding=1) uniform sampler2DArray soft_visibility_maps;\n\nuniform vec3 ncam_pos;\nuniform sampler2D input_rgb[NUM_CAMS];\nuniform sampler2D masks[NUM_CAMS];\nuniform vec3 icam_pos[NUM_CAMS];\nuniform vec3 icam_dir[NUM_CAMS];\nuniform mat4 icam_proj[NUM_CAMS];\nuniform int selected_cams[NUM_CAMS];\nuniform bool occ_test;\nuniform bool invert_mask;\nuniform bool is_binary_mask;\nuniform bool discard_black_pixels;\nuniform bool doMasking;\nuniform float softVisibilityThreshold;\nuniform bool useSoftVisibility;\nuniform int camsCount;\nuniform float epsilonOcclusion = 1e-2;\n\nin vec2 vertex_coord;\n\n\n#define INFTY_W 100000.0\n\n#define BETA \t1e-1  \t/* Relative importance of resolution penalty */\n#define FOV_BLENDING_BORDER 0.6\n#define ENABLE_BORDERS_BLENDING /// \\todo TODO SR: investigate effect of this additional blending.\n\n\nvec3 project(vec3 point, mat4 proj) {\n  vec4 p1 = proj * vec4(point, 1.0);\n  vec3 p2 = (p1.xyz/p1.w);\n  return (p2.xyz*0.5 + 0.5);\n}\n\nbool frustumTest(vec3 p, vec2 ndc, int cam_id) {\n  vec3 d1 = icam_dir[cam_id];\n  vec3 d2 = p - icam_pos[cam_id];\n  return !any(greaterThan(ndc, vec2(1.0))) && dot(d1,d2)>0.0;\n}\n\n\n\n\nvoid main(void){\n  \t\t\n  vec4 point = texture(proxy, vertex_coord);\n  // discard if there was no intersection with the proxy\n  if ( point.w >= 1.0) {\n\tdiscard;\n  }\n\n  vec4  color0 = vec4(0.0,0.0,0.0,INFTY_W);\n  vec4  color1 = vec4(0.0,0.0,0.0,INFTY_W);\n  vec4  color2 = vec4(0.0,0.0,0.0,INFTY_W);\n  vec4  color3 = vec4(0.0,0.0,0.0,INFTY_W);\n\n  // We need to keep the uvs of the selected colors for the fov blending.\n  vec4 uvs01 = vec4(0.0,0.0,0.0,0.0);\n  vec4 uvs23 = vec4(0.0,0.0,0.0,0.0);\n\n  for(int cam_id = 0; cam_id < NUM_CAMS; cam_id++){\n\tif(cam_id >= camsCount){\n\t  break;\n    }\n\tvec3 uvd = project(point.xyz, icam_proj[cam_id]);\n\tvec2 ndc = abs(2.0*uvd.xy-1.0);\n\n\tif (frustumTest(point.xyz, ndc, cam_id))\n\t{\n        vec4 color = texture(input_rgb[cam_id], uvd.xy);\n\t\t\n\t\t\n\t\tif(doMasking){\n            \n\t\t\tfloat masked = texture(masks[cam_id], uvd.xy).r;\n             \n            if( invert_mask ){\n                masked = 1.0 - masked;\n            }\n            \n            if( is_binary_mask ){\n                if( masked < 0.5) {\n                    continue;\n                }\n\t\t\t} else {\n                color.xyz = masked*color.xyz;\n            }\n\t\t\t\n\t\t}\n\t\t\n\n\t\t/// \\todo Separate uniform and per-pixel branching. TODO SR: test impact.\n\n\t\tif (discard_black_pixels){\n\t\t\tif(all(equal(color.xyz, vec3(0.0)))){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n \t\t\n\t\tif (occ_test){\n\t\t\tif(abs(uvd.z-color.w) >= epsilonOcclusion) {\t  \n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} \n\t\t\n\t\tvec3 v1 = (point.xyz - icam_pos[cam_id]);\n\t\tvec3 v2 = (point.xyz - ncam_pos);\n\t\tfloat dist_i2p \t= length(v1);\n\t\tfloat dist_n2p \t= length(v2);\n\n\t\tfloat penalty_ang = float(occ_test) * max(0.0001, acos(dot(v1,v2)/(dist_i2p*dist_n2p)));\n\n\t\tfloat penalty_res = max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\n\t\t \n\t\tcolor.w = penalty_ang + BETA*penalty_res;\n\t\t  \n        if(useSoftVisibility){\n\n            //vec4 dist_from_edge = vec4(2.0,2.0,2.0,2.0); // texture(soft_visibility_maps, vec3(uvd.xy, cam_true_id));\n            vec4 dist_from_edge = texture(soft_visibility_maps, vec3(uvd.xy, selected_cams[cam_id]));\n            float weight_visibility = min(dist_from_edge.x/softVisibilityThreshold,1.0);\n            color.w /= weight_visibility; \n\n        }\n        \n        \n\t\t// compare with best four candiates and insert at the\n\t\t// appropriate rank\n\t\tif (color.w<color3.w) {    // better than fourth best candidate\n\t\t\t\n\t\t\tif (color.w<color2.w) {    // better than third best candidate\n\t\t\t\tcolor3 = color2;\n\t\t\t\tuvs23.zw = uvs23.xy;\n\t\t\t\t\n\t\t\t\tif (color.w<color1.w) {    // better than second best candidate\n\t\t\t\t\tcolor2 = color1;\n\t\t\t\t\tuvs23.xy = uvs01.zw;\n\n\t\t\t\t\tif (color.w<color0.w) {    // better than best candidate\n\t\t\t\t\t\tcolor1 = color0;\n\t\t\t\t\t\tuvs01.zw = uvs01.xy;\n\n\t\t\t\t\t\tcolor0 = color;\n\t\t\t\t\t\tuvs01.xy = ndc;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcolor1 = color;\n\t\t\t\t\t\tuvs01.zw = ndc;\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\t\t\t\t\tcolor2 = color;\n\t\t\t\t\tuvs23.xy = ndc;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tcolor3 = color;\n\t\t\t\tuvs23.zw = ndc;\n\t\t\t}\n\t\t}\n\t }  \n   }\n   \n\tfloat thresh = 1.0000001 * color3.w;\n    color0.w = max(0, 1.0 - color0.w/thresh);\n    color1.w = max(0, 1.0 - color1.w/thresh);\n    color2.w = max(0, 1.0 - color2.w/thresh);\n    color3.w = 1.0 - 1.0/1.0000001;\n\n    // ignore any candidate which is uninit\n\tif (color0.w == INFTY_W) color0.w = 0;\n    if (color1.w == INFTY_W) color1.w = 0;\n    if (color2.w == INFTY_W) color2.w = 0;\n    //if (color3.w == INFTY_W) color3.w = 0; uneeded, color3.w = 1.0 - 1.0/1.0000001\n\n    // Blending on the sides of input images.\n#ifdef ENABLE_BORDERS_BLENDING\n\t// Compute the attenuation factors.\n\tvec4 fcs01 = 1.0 - smoothstep(vec4(FOV_BLENDING_BORDER), vec4(1.0), uvs01);\n\tvec4 fcs23 = 1.0 - smoothstep(vec4(FOV_BLENDING_BORDER), vec4(1.0), uvs23);\n\tfcs01.xz *= fcs01.yw;\n\tfcs23.xz *= fcs23.yw;\n\n\tcolor0.w *= fcs01.x;\n\tcolor1.w *= fcs01.z;\n\tcolor2.w *= fcs23.x;\n\tcolor3.w *= fcs23.z;\n#endif\n\n    // blending\n    out_color.w = 1.0;\n    out_color.xyz = (color0.w*color0.xyz +\n             color1.w*color1.xyz +\n             color2.w*color2.xyz +\n             color3.w*color3.xyz\n            ) / (color0.w + color1.w + color2.w + color3.w);\n    gl_FragDepth = point.w;\n\t\n\n}\n\n\n/* // ATTENTION - non updated code, tentative to use all samples with a different weighting function.\nvoid main(void) {\n\tvec4 point = texture(proxy, vertex_coord);\n\tvec3 color = vec3(0.0);\n\tfloat mask = 0.0;\n\tfloat sumWeight = 0.0;\n\tvec4 colors[NUM_CAMS];\n\tbool discarded[NUM_CAMS];\n\n\tfloat maxPenalty = 0.0;\n\tfor(int cam_id = 0; NUM_CAMS < camsCount; cam_id++){\n\t\t vec3 uvd = project(point.xyz, icam_proj[cam_id]);\n\t\t vec2 uv = uvd.xy;\n\n\t\t discarded[cam_id] = true;\n\t\t\n\t\tif (frustumTest(point.xyz, uv, cam_id)) // multiply instead\n\t\t{\n   \n\t\t\tvec4 inputColor = texture(input_rgb[cam_id], uv);\n\t\t\t\n\t\t\tif ( !all(equal(inputColor.xyz, vec3(0,0,0))) && \n\t\t\t\tabs(uvd.z-inputColor.w) < epsilonOcclusion) {\t\t\n\t\t\t    vec3 v1 = point.xyz - icam_pos[cam_id];\n\t\t\t    vec3 v2 = point.xyz - ncam_pos;\n\t\t\t\tfloat dist_i2p = length(v1);//distance(point.xyz, icam_pos[cam_id]);\n\t\t\t\tfloat dist_n2p = length(v2);//distance(point.xyz, ncam_pos);\n\t\t\t    float penalty_ang = max(0.0001, acos(dot(v1,v2)/(dist_i2p*dist_n2p)));\n\t\t\t\tfloat penalty_res \t= max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\n\t\t\t\tfloat penalty = penalty_ang + BETA*penalty_res;\n\t\t\t\tmaxPenalty = max(maxPenalty, penalty);\n\t\t\t\n\t\t\t\t\n\t\t\t\tcolors[cam_id] = vec4(inputColor.rgb, penalty);\n\t\t\t\tdiscarded[cam_id] = false;\n\t\t\t} \n\t\t}\n\t}\n\t\n\tfor(int i = 0; i < NUM_CAMS; i++){\n\t\tif(discarded[i]){\n\t\t\tcontinue;\n\t\t}\n\t\tfloat weight = max(0.0, 1.0 - colors[i].w/(maxPenalty*1.0000001));\n\t\tsumWeight += weight;\n\t\tcolor += weight*colors[i].rgb;\n\t}\n\t\n\tout_color.rgb = color / sumWeight;\n\tout_color.a = 1.0;\n}\n*/"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v2.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n\nlayout(location = 0) in vec3 in_vertex;\n\nout vec2 vertex_coord;\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex,1.0);\n    vertex_coord  = in_vertex.xy * 0.5 + 0.5;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v2_alt.frag",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n#define NUM_CAMS (12)\n\nlayout(location = 0) out vec4 out_color;\n\nlayout(binding=0) uniform sampler2D proxy;\nlayout(binding=1) uniform sampler2DArray soft_visibility_maps;\n\nuniform vec3 ncam_pos;\nuniform sampler2D input_rgb[NUM_CAMS];\nuniform sampler2D masks[NUM_CAMS];\nuniform vec3 icam_pos[NUM_CAMS];\nuniform vec3 icam_dir[NUM_CAMS];\nuniform mat4 icam_proj[NUM_CAMS];\nuniform int selected_cams[NUM_CAMS];\nuniform bool occ_test;\nuniform bool invert_mask;\nuniform bool is_binary_mask;\nuniform bool discard_black_pixels;\nuniform bool doMasking;\nuniform float softVisibilityThreshold;\nuniform bool useSoftVisibility;\nuniform int camsCount;\n\nin vec2 vertex_coord;\n\n\n#define INFTY_W 100000.0\n#define EPSILON 1e-2\n#define BETA \t1e-1  \t/* Relative importance of resolution penalty */\n#define FOV_BLENDING_BORDER 0.6\n#define ENABLE_BORDERS_BLENDING /// \\todo TODO SR: investigate effect of this additional blending.\n\nvec3 project(vec3 point, mat4 proj) {\n  vec4 p1 = proj * vec4(point, 1.0);\n  vec3 p2 = (p1.xyz/p1.w);\n  return (p2.xyz*0.5 + 0.5);\n}\n\nbool frustumTest(vec3 p, vec2 ndc, int cam_id) {\n  vec3 d1 = icam_dir[cam_id];\n  vec3 d2 = p - icam_pos[cam_id];\n  return !any(greaterThan(ndc, vec2(1.0))) && dot(d1,d2)>0.0;\n}\n\nvoid main(void){\n  \t\t\n\tvec4 point = texture(proxy, vertex_coord);\n\t  // discard if there was no intersection with the proxy\n\tif ( point.w >= 1.0) {\n\t\tdiscard;\n\t}\n\n\tvec4  color_sum = vec4(0.0,0.0,0.0,0.0);\n\t//vec3  color_sum_simple = vec3(0.0,0.0,0.0);\n\t//vec3  color_sum_square = vec3(0.0,0.0,0.0);\n\t//vec3  color_sum_square_w = vec3(0.0,0.0,0.0);\n\t//float num = 0.0;\n\t\n\tvec3 v2 = (point.xyz - ncam_pos);\n\tfloat dist_n2p \t= length(v2);\n\t  \n\tfor(int cam_id = 0; cam_id < NUM_CAMS; cam_id++){\n\t\t\n\t\tif(cam_id >= camsCount){\n\t\t  break;\n\t\t}\n\t\tvec3 uvd = project(point.xyz, icam_proj[cam_id]);\n\t\tvec2 ndc = abs(2.0*uvd.xy-1.0);\n\t\t\n\t\tif (!frustumTest(point.xyz, ndc, cam_id)) {\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tvec4 color = texture(input_rgb[cam_id], uvd.xy);\n\t\t\t\t\n\t\tif(doMasking){\t\n\t\t\tfloat masked = texture(masks[cam_id], uvd.xy).r;\n\t\t\t \n\t\t\tif( invert_mask ){\n\t\t\t\tmasked = 1.0 - masked;\n\t\t\t}\n\t\t\t\n\t\t\tif( is_binary_mask ){\n\t\t\t\tif( masked < 0.5) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcolor.xyz = masked*color.xyz;\n\t\t\t}\t\n\t\t}\t\t\n\n\t\tif (discard_black_pixels){\n\t\t\tif(all(equal(color.xyz, vec3(0.0)))){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (occ_test){\n\t\t\tif(abs(uvd.z-color.w) >= EPSILON) {\t  \n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} \n\t\t\n\t\tvec3 v1 = (point.xyz - icam_pos[cam_id]);\n\t\tfloat dist_i2p \t= length(v1);\n\t\t\n\t\tfloat penalty_ang = float(occ_test) * max(0.0001, acos(dot(v1,v2)/(dist_i2p*dist_n2p)));\n\n\t\tfloat penalty_res = max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\n\t\t \n\t\tcolor.w = penalty_ang + BETA*penalty_res;\n\t\t  \n\t\tif(useSoftVisibility){\n\t\t\tvec4 dist_from_edge = texture(soft_visibility_maps, vec3(uvd.xy, selected_cams[cam_id]));\n\t\t\tfloat weight_visibility = min(dist_from_edge.x/softVisibilityThreshold,1.0);\n\t\t\tcolor.w /= (weight_visibility*weight_visibility); \n\t\t}\n\t\t\n\t\tcolor.w = 1.0 / color.w;\n\t\t\n\t\tcolor_sum.xyz += color.w * color.xyz;\n\t\tcolor_sum.w += color.w;\n\t\t\n\t\t//color_sum_simple += color.xyz;\n\t\t//color_sum_square += color.xyz*color.xyz;\n\t\t//color_sum_square_w += color.w * color.xyz*color.xyz;\n\t\t//num++;\n\t}\n\t\n\t//vec3 mean = color_sum_simple / num;\n\t//vec3 variance = color_sum_square / num - mean*mean;\n\t//vec3 deviation = sqrt(variance);\n\t\n\t//vec3 mean = color_sum.xyz / color_sum.w;\n\t//vec3 variance = color_sum_square_w / color_sum.w - mean*mean;\n\t//vec3 deviation = 3.0*sqrt(variance);\n\t\n\tcolor_sum.xyz /= color_sum.w;\n\t\n    // blending\n    out_color.w = 1.0;\n    out_color.xyz = color_sum.xyz;\n\t//out_color.xyz = deviation;\n\t\n\tgl_FragDepth = point.w;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v3.frag",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\n#define NUM_CAMS (12)\r\n#define ULR_STREAMING (0)\r\n\r\nin vec2 vertex_coord;\r\nlayout(location = 0) out vec4 out_color;\r\n\r\n// 2D proxy texture.\r\nlayout(binding=0) uniform sampler2D proxy;\r\n\r\n// Input cameras.\r\nstruct CameraInfos\r\n{\r\n  mat4 vp;\r\n  vec3 pos;\r\n  int selected;\r\n  vec3 dir;\r\n};\r\n// They are stored in a contiguous buffer (UBO), lifting most limitations on the number of uniforms.\r\nlayout(std140, binding=4) uniform InputCameras\r\n{\r\n  CameraInfos cameras[NUM_CAMS];\r\n};\r\n\r\n// Uniforms.\r\nuniform int camsCount;\r\nuniform vec3 ncam_pos;\r\nuniform bool occ_test = true;\r\nuniform bool invert_mask = false;\r\nuniform bool is_binary_mask = true;\r\nuniform bool discard_black_pixels = true;\r\nuniform bool doMasking = false;\r\nuniform bool flipRGBs = false;\r\nuniform bool showWeights = false;\r\nuniform float epsilonOcclusion = 1e-2;\r\nuniform bool winner_takes_all = false;\r\n\r\n// for uv derivatives blending\r\nuniform bool useUVDerivatives = false;\r\nuniform float uvDerivativesAlphaBlending = 0.5f;\r\nuniform float uvDerivativesScaleFactor = 1.0f;\r\nuniform vec2 rtResolution = vec2(1.0);\r\n\r\n#define INFTY_W 100000.0\r\n#define BETA \t1e-1  \t/* Relative importance of resolution penalty */\r\n\r\n// Textures.\r\n// To support both the regular version (using texture arrays) and the streaming version (using 2D RTs),\r\n// we wrap the texture accesses in two helpers that hide the difference.\r\n\r\n#if ULR_STREAMING\r\n\r\nuniform sampler2D input_rgbds[NUM_CAMS];\r\nuniform sampler2D input_masks[NUM_CAMS];\r\n\r\nvec4 getRGBD(vec3 xy_camid){\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\tvec4 rgbd = texture(input_rgbds[int(xy_camid.z)], xy_camid.xy);\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\treturn rgbd;\r\n}\r\n\r\nfloat getMask(vec3 xy_camid){\r\n\treturn texture(input_masks[int(xy_camid.z)], xy_camid.xy).r;\r\n}\r\n\r\n#else\r\n\r\nlayout(binding=1) uniform sampler2DArray input_rgbs;\r\nlayout(binding=2) uniform sampler2DArray input_depths;\r\nlayout(binding=3) uniform sampler2DArray input_masks;\r\n\r\nvec4 getRGBD(vec3 xy_camid){\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\tvec3 rgb = texture(input_rgbs, xy_camid).rgb;\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\tfloat depth = texture(input_depths, xy_camid).r;\r\n    return vec4(rgb,depth);\r\n}\r\n\r\nfloat getMask(vec3 xy_camid){\r\n\treturn texture(input_masks, xy_camid).r;\r\n}\r\n\r\n#endif\r\n\r\n// Helpers.\r\n\r\nvec3 project(vec3 point, mat4 proj) {\r\n  vec4 p1 = proj * vec4(point, 1.0);\r\n  vec3 p2 = (p1.xyz/p1.w);\r\n  return (p2.xyz*0.5 + 0.5);\r\n}\r\n\r\nbool frustumTest(vec3 p, vec2 ndc, int i) {\r\n  vec3 d1 = cameras[i].dir;\r\n  vec3 d2 = p - cameras[i].pos;\r\n  return !any(greaterThan(ndc, vec2(1.0))) && dot(d1,d2)>0.0;\r\n}\r\n\r\nvec3 getRandomColor(int x);\r\n\r\nvoid main(void){\r\n  \t\t\r\n  vec4 point = texture(proxy, vertex_coord);\r\n  // discard if there was no intersection with the proxy\r\n  if ( point.w >= 1.0) {\r\n\tdiscard;\r\n  }\r\n\r\n  vec4  color0 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4  color1 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4  color2 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4  color3 = vec4(0.0,0.0,0.0,INFTY_W);\r\n\r\n  bool atLeastOneValid = false;\r\n  \r\n  for(int i = 0; i < NUM_CAMS; i++){\r\n\tif(i>=camsCount){\r\n\t\tcontinue;\r\n\t}\r\n\tif(cameras[i].selected == 0){\r\n\t\tcontinue;\r\n\t}\r\n\r\n\tvec3 uvd = project(point.xyz, cameras[i].vp);\r\n\tvec2 ndc = abs(2.0*uvd.xy-1.0);\r\n\r\n\tvec2 uv_ddx = dFdx(uvd.xy * rtResolution); \r\n\tvec2 uv_ddy = dFdy(uvd.xy * rtResolution);\r\n\r\n\r\n\tif (frustumTest(point.xyz, ndc, i)){\r\n\t\tvec3 xy_camid = vec3(uvd.xy,i);\r\n\t\t\r\n\t\t\r\n\t\tvec4 color = getRGBD(xy_camid);\r\n\r\n\t\t\r\n\r\n\t\tif(doMasking){        \r\n\t\t\tfloat masked = getMask(xy_camid);\r\n             \r\n            if( invert_mask ){\r\n                masked = 1.0 - masked;\r\n            }\r\n            \r\n            if( is_binary_mask ){\r\n                if( masked < 0.5) {\r\n                    continue;\r\n                }\r\n\t\t\t} else {\r\n                color.xyz = masked*color.xyz;\r\n            }\r\n\t\t\t\r\n\t\t}\r\n\t\t\r\n\t\tif (discard_black_pixels){\r\n\t\t\tif(all(equal(color.xyz, vec3(0.0)))){\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n \t\t\r\n\t\tif (occ_test){\r\n\t\t\tif(abs(uvd.z-color.w) >= epsilonOcclusion) {\t  \r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Support output weights as random colors for debug.\r\n\t\tif(showWeights){\r\n\t\t\tcolor.xyz = getRandomColor(i);\r\n\t\t}\r\n\r\n\t\tfloat penaltyValue = 0;\r\n\r\n\t\tif (!useUVDerivatives) {\r\n\t\t\t// classic ulr\r\n\t\t\tvec3 v1 = (point.xyz - cameras[i].pos);\r\n\t\t\tvec3 v2 = (point.xyz - ncam_pos);\r\n\t\t\tfloat dist_i2p \t= length(v1);\r\n\t\t\tfloat dist_n2p \t= length(v2);\r\n\r\n\t\t\tfloat penalty_ang = float(occ_test) * max(0.0001, acos(dot(v1,v2)/(dist_i2p*dist_n2p)));\r\n\r\n\t\t\tfloat penalty_res = max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\r\n\t\t \r\n\t\t\tpenaltyValue = penalty_ang + BETA*penalty_res;\r\n\t\t} else {\r\n\t\t\t/// use uv derivatives\r\n\t\t\t/// \\todo TODO: check if uv needs to be scale by screen size as needed in unity hlsl\r\n\r\n\t\t\tvec3 crossProduct = cross(vec3(uv_ddx, 0), vec3(uv_ddy, 0));\r\n\t\t\tfloat transformScale = length (crossProduct);\r\n\t\t\tfloat weight = 1.0f / transformScale;\r\n\t\t\tpenaltyValue = weight;\r\n\t\t}\r\n\r\n        atLeastOneValid = true;\r\n\t\tcolor.w = penaltyValue;\r\n\t\t  \r\n\t\t// compare with best four candiates and insert at the\r\n\t\t// appropriate rank\r\n\t\tif (color.w<color3.w) {    // better than fourth best candidate\r\n\t\t\tif (color.w<color2.w) {    // better than third best candidate\r\n\t\t\t\tcolor3 = color2;\r\n\t\t\t\tif (color.w<color1.w) {    // better than second best candidate\r\n\t\t\t\t\tcolor2 = color1;\r\n\t\t\t\t\tif (color.w<color0.w) {    // better than best candidate\r\n\t\t\t\t\t\tcolor1 = color0;\r\n\t\t\t\t\t\tcolor0 = color;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tcolor1 = color;\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tcolor2 = color;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tcolor3 = color;\r\n\t\t\t}\r\n\t\t}\r\n\t }  \r\n   }\r\n   \r\n   if(!atLeastOneValid){\r\n        discard;\r\n   }\r\n   \r\n   \tif(winner_takes_all){\r\n\t\tgl_FragDepth = point.w;\r\n\t\tout_color.w = 1.0;\r\n\t\tout_color.xyz = color0.xyz;\r\n\t\treturn;\r\n\t}\r\n    \r\n\tfloat thresh = 1.0000001 * color3.w;\r\n    color0.w = max(0, 1.0 - color0.w/thresh);\r\n    color1.w = max(0, 1.0 - color1.w/thresh);\r\n    color2.w = max(0, 1.0 - color2.w/thresh);\r\n    color3.w = 1.0 - 1.0/1.0000001;\r\n\r\n    // ignore any candidate which is uninit\r\n\tif (color0.w == INFTY_W) color0.w = 0;\r\n    if (color1.w == INFTY_W) color1.w = 0;\r\n    if (color2.w == INFTY_W) color2.w = 0;\r\n    //if (color3.w == INFTY_W) color3.w = 0; uneeded, color3.w = 1.0 - 1.0/1.0000001\r\n\t\r\n\t\r\n    // blending\r\n    out_color.w = 1.0;\r\n    out_color.xyz = (color0.w*color0.xyz +\r\n             color1.w*color1.xyz +\r\n             color2.w*color2.xyz +\r\n             color3.w*color3.xyz\r\n            ) / (color0.w + color1.w + color2.w + color3.w);\r\n    gl_FragDepth = point.w;\r\n\t\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n/*\r\nfloat getPenalizeStretch(vec2 uv)\r\n{\r\n\t//uv = uv * reso;\r\n      // Source:\r\n      // - Hyperlapse papers [Kopf et al. 2014]\r\n      // - http://www.lucidarme.me/?p=4624\r\n\r\n      mat2 jacobian = mat2(\r\n        dFdx(uv),\r\n        dFdy(uv)\r\n        );\r\n\r\n      float a = jacobian[0][0];\r\n      float b = jacobian[1][0];\r\n      float c = jacobian[0][1];\r\n      float d = jacobian[1][1];\r\n      float aa = a*a;\r\n      float bb = b*b;\r\n      float cc = c*c;\r\n      float dd = d*d;\r\n\r\n      float S1 = aa + bb + cc + dd;\r\n      float S1a = (aa+bb-cc-dd);\r\n      float S1b = (a*c + b*d);\r\n      float S2 = sqrt(S1a*S1a + 4*S1b*S1b);\r\n\r\n      vec2  sigma = vec2(sqrt((S1+S2)/2.0), sqrt((S1-S2)/2.0));\r\n      return 1.0 - min(sigma.x, sigma.y)/max(sigma.x, sigma.y);\r\n}\r\n*/\r\n\r\n\r\n// Random number generation:\r\n// \"Quality hashes collection\" (https://www.shadertoy.com/view/Xt3cDn)\r\n// by nimitz 2018 (twitter: @stormoid)\r\n// The MIT License\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n\r\n/** Compute the based hash for a given index.\r\n\t\\param p the index\r\n\t\\return the hash\r\n*/\r\nuint baseHash(uint p) {\r\n\tp = 1103515245U*((p >> 1U)^(p));\r\n\tuint h32 = 1103515245U*((p)^(p>>3U));\r\n\treturn h32^(h32 >> 16);\r\n}\r\n\r\n/** Generate a random vec3 from an index seed (see http://random.mat.sbg.ac.at/results/karl/server/node4.html).\r\n\t\\param x the seed\r\n\t\\return a random vec3\r\n*/\r\nvec3 getRandomColor(int x) {\r\n\t// Color 0 is black, so we shift everything.\r\n\tx = x+1;\r\n\tuint n = baseHash(uint(x));\r\n\tuvec3 rz = uvec3(n, n*16807U, n*48271U);\r\n\treturn vec3(rz & uvec3(0x7fffffffU))/float(0x7fffffff);\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v3.vert",
    "content": "/*\n * Copyright (C) 2020, Inria\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\n * All rights reserved.\n *\n * This software is free for non-commercial, research and evaluation use \n * under the terms of the LICENSE.md file.\n *\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\n */\n\n\n#version 420\n\n\nlayout(location = 0) in vec3 in_vertex;\n\nout vec2 vertex_coord;\n\nvoid main(void) {\n\tgl_Position = vec4(in_vertex,1.0);\n    vertex_coord  = in_vertex.xy * 0.5 + 0.5;\n}\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v3_alt.frag",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\n#define NUM_CAMS (12)\r\n#define ULR_STREAMING (0)\r\n\r\nin vec2 vertex_coord;\r\nlayout(location = 0) out vec4 out_color;\r\n\r\n// 2D proxy texture.\r\nlayout(binding=0) uniform sampler2D proxy;\r\n\r\n// Input cameras.\r\nstruct CameraInfos\r\n{\r\n  mat4 vp;\r\n  vec3 pos;\r\n  int selected;\r\n  vec3 dir;\r\n};\r\n// They are stored in a contiguous buffer (UBO), lifting most limitations on the number of uniforms.\r\nlayout(std140, binding=4) uniform InputCameras\r\n{\r\n  CameraInfos cameras[NUM_CAMS];\r\n};\r\n\r\n// Uniforms.\r\nuniform int camsCount;\r\nuniform vec3 ncam_pos;\r\nuniform bool occ_test = true;\r\nuniform bool invert_mask = false;\r\nuniform bool is_binary_mask = true;\r\nuniform bool discard_black_pixels = true;\r\nuniform bool doMasking = false;\r\nuniform bool flipRGBs = false;\r\nuniform bool showWeights = false;\r\nuniform float epsilonOcclusion = 1e-2;\r\n#define INFTY_W 100000.0\r\n#define BETA \t1e-1  \t/* Relative importance of resolution penalty */\r\n\r\n// Textures.\r\n// To support both the regular version (using texture arrays) and the streaming version (using 2D RTs),\r\n// we wrap the texture accesses in two helpers that hide the difference.\r\n\r\n#if ULR_STREAMING\r\n\r\nuniform sampler2D input_rgbds[NUM_CAMS];\r\nuniform sampler2D input_masks[NUM_CAMS];\r\n\r\nvec4 getRGBD(vec3 xy_camid){\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\tvec4 rgbd = texture(input_rgbds[int(xy_camid.z)], xy_camid.xy);\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\treturn rgbd;\r\n}\r\n\r\nfloat getMask(vec3 xy_camid){\r\n\treturn texture(input_masks[int(xy_camid.z)], xy_camid.xy).r;\r\n}\r\n\r\n#else\r\n\r\nlayout(binding=1) uniform sampler2DArray input_rgbs;\r\nlayout(binding=2) uniform sampler2DArray input_depths;\r\nlayout(binding=3) uniform sampler2DArray input_masks;\r\n\r\nvec4 getRGBD(vec3 xy_camid){\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\tvec3 rgb = texture(input_rgbs, xy_camid).rgb;\r\n\tif(flipRGBs){\r\n\t\txy_camid.y = 1.0 - xy_camid.y;\r\n\t}\r\n\tfloat depth = texture(input_depths, xy_camid).r;\r\n    return vec4(rgb,depth);\r\n}\r\n\r\nfloat getMask(vec3 xy_camid){\r\n\treturn texture(input_masks, xy_camid).r;\r\n}\r\n\r\n#endif\r\n\r\n// Helpers.\r\n\r\nvec3 project(vec3 point, mat4 proj) {\r\n  vec4 p1 = proj * vec4(point, 1.0);\r\n  vec3 p2 = (p1.xyz/p1.w);\r\n  return (p2.xyz*0.5 + 0.5);\r\n}\r\n\r\nbool frustumTest(vec3 p, vec2 ndc, int i) {\r\n  vec3 d1 = cameras[i].dir;\r\n  vec3 d2 = p - cameras[i].pos;\r\n  return !any(greaterThan(ndc, vec2(1.0))) && dot(d1,d2)>0.0;\r\n}\r\n\r\nvec3 getRandomColor(int x);\r\n\r\nvoid main(void){\r\n  \t\t\r\n\tvec4 point = texture(proxy, vertex_coord);\r\n\t  // discard if there was no intersection with the proxy\r\n\tif ( point.w >= 1.0) {\r\n\t\tdiscard;\r\n\t}\r\n\r\n\tvec4  color_sum = vec4(0.0,0.0,0.0,0.0);\r\n\t//vec3  color_sum_simple = vec3(0.0,0.0,0.0);\r\n\t//vec3  color_sum_square = vec3(0.0,0.0,0.0);\r\n\t//vec3  color_sum_square_w = vec3(0.0,0.0,0.0);\r\n\t//float num = 0.0;\r\n\t\r\n\tvec3 v2 = (point.xyz - ncam_pos);\r\n\tfloat dist_n2p \t= length(v2);\r\n\t  \r\n\t  for(int i = 0; i < NUM_CAMS; i++){\r\n\t\tif(i>=camsCount){\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif(cameras[i].selected == 0){\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\t\r\n\t\tvec3 uvd = project(point.xyz, cameras[i].vp);\r\n\t\tvec2 ndc = abs(2.0*uvd.xy-1.0);\r\n\t\t\r\n\t\tif (!frustumTest(point.xyz, ndc, i)) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\t\r\n\t\tvec3 xy_camid = vec3(uvd.xy,i);\r\n\t\tvec4 color = getRGBD(xy_camid);\r\n\t\t\r\n\t\t// Support output weights as random colors for debug.\r\n\t\tif(showWeights){\r\n\t\t\tcolor.xyz = getRandomColor(i);\r\n\t\t}\r\n\r\n\t\tif(doMasking){\t\r\n\t\t\tfloat masked = getMask(xy_camid);\r\n\t\t\t \r\n\t\t\tif( invert_mask ){\r\n\t\t\t\tmasked = 1.0 - masked;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif( is_binary_mask ){\r\n\t\t\t\tif( masked < 0.5) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tcolor.xyz = masked*color.xyz;\r\n\t\t\t}\t\r\n\t\t}\t\t\r\n\r\n\t\tif (discard_black_pixels){\r\n\t\t\tif(all(equal(color.xyz, vec3(0.0)))){\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tif (occ_test){\r\n\t\t\tif(abs(uvd.z-color.w) >= epsilonOcclusion) {\t  \r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t} \r\n\t\t\r\n\t\tvec3 v1 = (point.xyz - cameras[i].pos);\r\n\t\tfloat dist_i2p \t= length(v1);\r\n\t\t\r\n\t\tfloat penalty_ang = float(occ_test) * max(0.0001, acos(dot(v1,v2)/(dist_i2p*dist_n2p)));\r\n\r\n\t\tfloat penalty_res = max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\r\n\t\t \r\n\t\tcolor.w = penalty_ang + BETA*penalty_res;\r\n\t\t  \r\n\t\tcolor.w = 1.0 / color.w;\r\n\t\t\r\n\t\tcolor_sum.xyz += color.w * color.xyz;\r\n\t\tcolor_sum.w += color.w;\r\n\t\t\r\n\t\t//color_sum_simple += color.xyz;\r\n\t\t//color_sum_square += color.xyz*color.xyz;\r\n\t\t//color_sum_square_w += color.w * color.xyz*color.xyz;\r\n\t\t//num++;\r\n\t}\r\n\t\r\n\t//vec3 mean = color_sum_simple / num;\r\n\t//vec3 variance = color_sum_square / num - mean*mean;\r\n\t//vec3 deviation = sqrt(variance);\r\n\t\r\n\t//vec3 mean = color_sum.xyz / color_sum.w;\r\n\t//vec3 variance = color_sum_square_w / color_sum.w - mean*mean;\r\n\t//vec3 deviation = 3.0*sqrt(variance);\r\n\t\r\n\tcolor_sum.xyz /= color_sum.w;\r\n\t\r\n    // blending\r\n    out_color.w = 1.0;\r\n    out_color.xyz = color_sum.xyz;\r\n\t//out_color.xyz = deviation;\r\n\t\r\n\tgl_FragDepth = point.w;\r\n}\r\n\r\n\r\n// Random number generation:\r\n// \"Quality hashes collection\" (https://www.shadertoy.com/view/Xt3cDn)\r\n// by nimitz 2018 (twitter: @stormoid)\r\n// The MIT License\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n\r\n/** Compute the based hash for a given index.\r\n\t\\param p the index\r\n\t\\return the hash\r\n*/\r\nuint baseHash(uint p) {\r\n\tp = 1103515245U*((p >> 1U)^(p));\r\n\tuint h32 = 1103515245U*((p)^(p>>3U));\r\n\treturn h32^(h32 >> 16);\r\n}\r\n\r\n/** Generate a random vec3 from an index seed (see http://random.mat.sbg.ac.at/results/karl/server/node4.html).\r\n\t\\param x the seed\r\n\t\\return a random vec3\r\n*/\r\nvec3 getRandomColor(int x) {\r\n\tuint n = baseHash(uint(x));\r\n\tuvec3 rz = uvec3(n, n*16807U, n*48271U);\r\n\treturn vec3(rz & uvec3(0x7fffffffU))/float(0x7fffffff);\r\n}\r\n"
  },
  {
    "path": "envs/gs/SIBR_viewers/src/projects/ulr/renderer/shaders/ulr_v3_fast.frag",
    "content": "/*\r\n * Copyright (C) 2020, Inria\r\n * GRAPHDECO research group, https://team.inria.fr/graphdeco\r\n * All rights reserved.\r\n *\r\n * This software is free for non-commercial, research and evaluation use \r\n * under the terms of the LICENSE.md file.\r\n *\r\n * For inquiries contact sibr@inria.fr and/or George.Drettakis@inria.fr\r\n */\r\n\r\n\r\n#version 420\r\n\r\n#define NUM_CAMS (12)\r\n#define ULR_STREAMING (0)\r\n\r\nin vec2 vertex_coord;\r\nlayout(location = 0) out vec4 out_color;\r\n\r\n// 2D proxy texture.\r\nlayout(binding=0) uniform sampler2D proxy;\r\n\r\n// Input cameras.\r\nstruct CameraInfos\r\n{\r\n  mat4 vp;\r\n  vec3 pos;\r\n  int selected;\r\n  vec3 dir;\r\n};\r\n// They are stored in a contiguous buffer (UBO), lifting most limitations on the number of uniforms.\r\nlayout(std140, binding=4) uniform InputCameras\r\n{\r\n  CameraInfos cameras[NUM_CAMS];\r\n};\r\n\r\n// Uniforms.\r\nuniform int camsCount;\r\nuniform vec3 ncam_pos;\r\nuniform bool occ_test = true;\r\nuniform bool invert_mask = false;\r\nuniform bool is_binary_mask = true;\r\nuniform bool discard_black_pixels = true;\r\nuniform bool doMasking = false;\r\nuniform bool flipRGBs = false;\r\nuniform bool showWeights = false;\r\nuniform bool gammaCorrection = false;\r\nuniform float epsilonOcclusion = 1e-2;\r\n\r\n\r\n#define INFTY_W 100000.0\r\n/* Relative importance of resolution penalty */\r\n#define BETA \t1e-1\r\n/* Relative importance of edges penalty */\r\n#define BETA_UV 0.0\r\n\r\n// Textures.\r\n// To support both the regular version (using texture arrays) and the streaming version (using 2D RTs),\r\n// we wrap the texture accesses in two helpers that hide the difference.\r\n\r\nlayout(binding=1) uniform sampler2DArray input_rgbs;\r\nlayout(binding=2) uniform sampler2DArray input_depths;\r\nlayout(binding=3) uniform sampler2DArray input_masks;\r\n\r\n\r\nfloat getMask(vec3 xy_camid){\r\n\treturn texture(input_masks, xy_camid).r;\r\n}\r\n\r\n\r\n// Helpers.\r\n\r\nvec3 project(vec3 point, mat4 proj) {\r\n  vec4 p1 = proj * vec4(point, 1.0);\r\n  vec3 p2 = (p1.xyz/p1.w);\r\n  return (p2.xyz*0.5 + 0.5);\r\n}\r\n\r\nbool frustumTest(vec3 p, vec2 ndc, int i) {\r\n  vec3 d1 = cameras[i].dir;\r\n  vec3 d2 = p - cameras[i].pos;\r\n  return !any(greaterThan(ndc, vec2(1.0))) && dot(d1,d2)>0.0;\r\n}\r\n\r\nvec3 getRandomColor(int x);\r\n\r\nvoid main(void){\r\n  \t\t\r\n  vec4 point = texture(proxy, vertex_coord);\r\n  // discard if there was no intersection with the proxy\r\n  if ( point.w >= 1.0) {\r\n\tdiscard;\r\n  }\r\n\r\n  vec4  color0 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4  color1 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4  color2 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4  color3 = vec4(0.0,0.0,0.0,INFTY_W);\r\n  vec4 masks = vec4(1.0);\r\n  for(int i = 0; i < NUM_CAMS; i++){\r\n\tif(i>=camsCount){\r\n\t\tcontinue;\r\n\t}\r\n\tif(cameras[i].selected == 0){\r\n\t\tcontinue;\r\n\t}\r\n\r\n\tvec3 uvd = project(point.xyz, cameras[i].vp);\r\n\tvec2 ndc = abs(2.0*uvd.xy-1.0);\r\n\r\n\tif (frustumTest(point.xyz, ndc, i)){\r\n\t\tvec3 xy_camid = vec3(uvd.xy,i);\r\n\t\t\r\n\t\tfloat inputDepth = texture(input_depths, xy_camid).r;\r\n\r\n\t\tif (occ_test){\r\n\t\t\tif(abs(uvd.z-inputDepth) >= epsilonOcclusion) {\t  \r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\r\n\r\n\t\tfloat masked = 1.0;\r\n\t\tif(doMasking){        \r\n\t\t\tmasked = getMask(xy_camid);\r\n             \r\n            if( invert_mask ){\r\n                masked = 1.0 - masked;\r\n            }\r\n            \r\n            if( is_binary_mask ){\r\n                if( masked < 0.5) {\r\n                    continue;\r\n                }\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tfloat penaltyValue = 0;\r\n\r\n\t\t\r\n\t\t// classic ulr\r\n\t\tvec3 v1 = (point.xyz - cameras[i].pos);\r\n\t\tvec3 v2 = (point.xyz - ncam_pos);\r\n\t\tfloat dist_i2p \t= length(v1);\r\n\t\tfloat dist_n2p \t= length(v2);\r\n\r\n\t\tfloat penalty_ang = float(occ_test) * max(0.0001, acos(dot(v1,v2)/(dist_i2p*dist_n2p)));\r\n\r\n\t\tfloat penalty_res = max(0.0001, (dist_i2p - dist_n2p)/dist_i2p );\r\n\t\t \r\n\t\tvec2 fc = vec2(1.0) - smoothstep(vec2(0.7), vec2(1.0), abs(2.0*uvd.xy-1.0));\r\n    \tfloat penalty_uv = 1.0 - fc.x * fc.y; \r\n\r\n\t\tpenaltyValue = penalty_ang + BETA*penalty_res + BETA_UV*penalty_uv;\r\n\t\t\r\n\r\n\r\n\t\tvec4 color = vec4(xy_camid, penaltyValue);\r\n\t\tif(flipRGBs){\r\n\t\t\tcolor.y = 1.0 - color.y;\r\n\t\t}\r\n\t\t \r\n\t\t// compare with best four candiates and insert at the\r\n\t\t// appropriate rank\r\n\t\tif (color.w<color3.w) {    // better than fourth best candidate\r\n\t\t\tif (color.w<color2.w) {    // better than third best candidate\r\n\t\t\t\tcolor3 = color2;\r\n\t\t\t\tif (color.w<color1.w) {    // better than second best candidate\r\n\t\t\t\t\tcolor2 = color1;\r\n\t\t\t\t\tif (color.w<color0.w) {    // better than best candidate\r\n\t\t\t\t\t\tcolor1 = color0;\r\n\t\t\t\t\t\tcolor0 = color;\r\n\t\t\t\t\t\tmasks.x = masked;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tcolor1 = color;\r\n\t\t\t\t\t\tmasks.y = masked;\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tcolor2 = color;\r\n\t\t\t\t\tmasks.z = masked;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tcolor3 = color;\r\n\t\t\t\tmasks.w = masked;\r\n\t\t\t}\r\n\t\t}\r\n\t }  \r\n   }\r\n   \r\n\r\n\r\n\tif(color0.w == INFTY_W){\r\n\t\tdiscard;\r\n\t}\r\n\r\n\tfloat thresh = 1.0000001 * color3.w;\r\n    color0.w = max(0, 1.0 - color0.w/thresh);\r\n    color1.w = max(0, 1.0 - color1.w/thresh);\r\n    color2.w = max(0, 1.0 - color2.w/thresh);\r\n    color3.w = 1.0 - 1.0/1.0000001;\r\n\r\n    // ignore any candidate which is uninit\r\n\tif (color0.w == INFTY_W) color0.w = 0;\r\n    if (color1.w == INFTY_W) color1.w = 0;\r\n    if (color2.w == INFTY_W) color2.w = 0;\r\n    //if (color3.w == INFTY_W) color3.w = 0; uneeded, color3.w = 1.0 - 1.0/1.0000001\r\n\t\r\n\t// Support output weights as random colors for debug.\r\n\tif(showWeights){\r\n\t\tcolor0.rgb = getRandomColor(int(color0.z));\r\n\t\tcolor1.rgb = getRandomColor(int(color1.z));\r\n\t\tcolor2.rgb = getRandomColor(int(color2.z));\r\n\t\tcolor3.rgb = getRandomColor(int(color3.z));\r\n\t} else {\r\n\t\t// Read from textures and apply masking.\r\n\t\tcolor0.rgb = masks.x*texture(input_rgbs, color0.rgb).rgb;\r\n\t\tcolor1.rgb = masks.y*texture(input_rgbs, color1.rgb).rgb;\r\n\t\tcolor2.rgb = masks.z*texture(input_rgbs, color2.rgb).rgb;\r\n\t\tcolor3.rgb = masks.w*texture(input_rgbs, color3.rgb).rgb;\r\n\t}\r\n\r\n    // blending\r\n    out_color.w = 1.0;\r\n    out_color.xyz = (color0.w*color0.xyz +\r\n             color1.w*color1.xyz +\r\n             color2.w*color2.xyz +\r\n             color3.w*color3.xyz\r\n            ) / (color0.w + color1.w + color2.w + color3.w);\r\n    gl_FragDepth = point.w;\r\n\r\n\tif(gammaCorrection){\r\n\t\tout_color.xyz = pow(out_color.xyz, vec3(1.0/2.2));\r\n\t}\r\n\t\r\n}\r\n\r\n\r\n\r\n// Random number generation:\r\n// \"Quality hashes collection\" (https://www.shadertoy.com/view/Xt3cDn)\r\n// by nimitz 2018 (twitter: @stormoid)\r\n// The MIT License\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n\r\n/** Compute the based hash for a given index.\r\n\t\\param p the index\r\n\t\\return the hash\r\n*/\r\nuint baseHash(uint p) {\r\n\tp = 1103515245U*((p >> 1U)^(p));\r\n\tuint h32 = 1103515245U*((p)^(p>>3U));\r\n\treturn h32^(h32 >> 16);\r\n}\r\n\r\n/** Generate a random vec3 from an index seed (see http://random.mat.sbg.ac.at/results/karl/server/node4.html).\r\n\t\\param x the seed\r\n\t\\return a random vec3\r\n*/\r\nvec3 getRandomColor(int x) {\r\n\t// Color 0 is black, so we shift everything.\r\n\tx = x+1;\r\n\tuint n = baseHash(uint(x));\r\n\tuvec3 rz = uvec3(n, n*16807U, n*48271U);\r\n\treturn vec3(rz & uvec3(0x7fffffffU))/float(0x7fffffff);\r\n}\r\n"
  },
  {
    "path": "envs/ue/save_ue_sim_here.txt",
    "content": ""
  },
  {
    "path": "requirements.txt",
    "content": "msgpack-rpc-python\nairsim\n\nopencv-python\nnumpy\npyyaml\n\nunrealcv\npsutil\nrequests\npandas\n\naiofiles\nscipy\nimportlib_metadata"
  },
  {
    "path": "scene_data/pcd_map/save_pcd_here.txt",
    "content": ""
  },
  {
    "path": "scene_data/seg_map/env_airsim_16.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.22000000000003Y=-866.76Z=33.82895278930664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=19.779999999999973Y=-818.76Z=41.85809326171875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-54.22000000000003Y=-820.76Z=43.749515533447266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=281.78Y=-816.76Z=31.152681350708008.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-60.22000000000003Y=-802.76Z=31.15268325805664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-26.220000000000027Y=-794.76Z=26.277366638183594.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=213.77999999999997Y=-786.76Z=75.77371215820312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=187.77999999999997Y=-790.76Z=29.999147415161133.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=271.78Y=-784.76Z=26.277347564697266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=55.77999999999997Y=-780.76Z=31.15268325805664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=33.77999999999997Y=-780.76Z=43.74951934814453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=33.77999999999997Y=-762.76Z=33.82895278930664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=13.779999999999973Y=-774.76Z=45.9813117980957.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=145.77999999999997Y=-760.76Z=46.32895278930664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=109.77999999999997Y=-754.76Z=26.2773494720459.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-78.22000000000003Y=-744.76Z=41.858097076416016.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=293.78Y=-744.76Z=41.85808563232422.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=195.77999999999997Y=-744.76Z=45.9813232421875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-28.220000000000027Y=-744.76Z=47.450008392333984.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-128.22000000000003Y=-740.76Z=120.43132781982422.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=51.77999999999997Y=-732.76Z=80.65592193603516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=17.779999999999973Y=-734.76Z=35.758094787597656.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-104.22000000000003Y=-728.76Z=43.74951934814453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=147.77999999999997Y=-724.76Z=45.98131561279297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=127.77999999999997Y=-716.76Z=24.743167877197266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=103.77999999999997Y=-710.76Z=27.614891052246094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-42.22000000000003Y=-716.76Z=37.023414611816406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=33.77999999999997Y=-710.76Z=26.8066463470459.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=293.78Y=-704.76Z=62.99250030517578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=267.78Y=-696.76Z=19.127731323242188.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=239.77999999999997Y=-702.76Z=35.75809097290039.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-48.22000000000003Y=-682.76Z=19.02252960205078.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-68.22000000000003Y=-684.76Z=43.749515533447266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=213.77999999999997Y=-682.76Z=26.2773494720459.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-110.22000000000003Y=-672.76Z=45.98131561279297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-132.22000000000003Y=-670.76Z=41.85808563232422.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=243.77999999999997Y=-660.76Z=28.817462921142578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=285.78Y=-660.76Z=46.328956604003906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-70.22000000000003Y=-656.76Z=19.02251434326172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=239.77999999999997Y=-632.76Z=15.631072998046875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-140.22000000000003Y=-634.76Z=19.12773895263672.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=215.77999999999997Y=-618.76Z=62.992496490478516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-52.22000000000003Y=-622.76Z=62.993255615234375.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=277.78Y=-616.76Z=21.7431697845459.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.22000000000003Y=-616.76Z=29.480031967163086.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-50.22000000000003Y=-580.76Z=22.326499938964844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=275.78Y=-580.76Z=43.74953079223633.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1096.22Y=-572.76Z=22.32649803161621.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1186.22Y=-570.76Z=24.725357055664062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-980.22Y=-568.76Z=30.4920654296875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-858.22Y=-566.76Z=62.99250030517578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1132.22Y=-568.76Z=15.631057739257812.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1006.22Y=-560.76Z=19.625028610229492.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.22Y=-560.76Z=115.72896575927734.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=225.77999999999997Y=-548.76Z=19.979496002197266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-892.22Y=-560.76Z=115.72895050048828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-70.22000000000003Y=-560.76Z=45.9813117980957.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=305.78Y=-544.76Z=18.206756591796875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-842.22Y=-542.76Z=19.625015258789062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1076.22Y=-546.76Z=19.62502098083496.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1094.22Y=-546.76Z=15.631064414978027.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=303.78Y=-528.76Z=16.326499938964844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-50.22000000000003Y=-564.76Z=19.625015258789062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1174.22Y=-538.76Z=19.606903076171875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1024.22Y=-528.76Z=24.407751083374023.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.22Y=-526.76Z=105.46575927734375.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-50.22000000000003Y=-512.76Z=36.195491790771484.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.22000000000003Y=-520.76Z=41.85808181762695.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-994.22Y=-520.76Z=75.57371520996094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-124.22000000000003Y=-530.76Z=115.82896423339844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-836.22Y=-516.76Z=120.43131256103516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=275.78Y=-546.76Z=119.45807647705078.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=233.77999999999997Y=-506.76Z=43.649513244628906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1144.22Y=-516.76Z=15.631073951721191.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1174.22Y=-504.76Z=20.011371612548828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1190.22Y=-504.76Z=22.30837059020996.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-902.22Y=-510.76Z=73.40774536132812.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1046.22Y=-504.76Z=33.778953552246094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1088.22Y=-504.76Z=21.211502075195312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1124.22Y=-528.76Z=15.631061553955078.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1128.22Y=-464.76Z=20.011369705200195.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-870.22Y=-462.76Z=24.40650177001953.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-894.22Y=-462.76Z=17.919937133789062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-914.22Y=-464.76Z=33.62895965576172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1072.22Y=-464.76Z=33.77894973754883.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1196.22Y=-460.76Z=19.0044002532959.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=271.78Y=-456.76Z=35.911102294921875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1008.22Y=-460.76Z=26.808521270751953.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1164.22Y=-458.76Z=13.306509971618652.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-988.22Y=-458.76Z=46.31147003173828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.22000000000003Y=-450.76Z=120.43132019042969.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-834.22Y=-450.76Z=119.43807220458984.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-864.22Y=-440.76Z=33.81114959716797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-888.22Y=-440.76Z=18.972524642944336.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=295.78Y=-448.76Z=41.85808181762695.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=237.77999999999997Y=-450.76Z=43.749515533447266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1052.22Y=-438.76Z=24.725357055664062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-52.22000000000003Y=-450.76Z=43.74951934814453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1094.22Y=-432.76Z=19.02252769470215.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1166.22Y=-434.76Z=15.631057739257812.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1010.22Y=-430.76Z=15.631073951721191.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.22000000000003Y=-440.76Z=46.3289680480957.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-826.22Y=-414.76Z=36.399024963378906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-862.22Y=-414.76Z=35.958091735839844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-912.22Y=-420.76Z=25.21367073059082.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-890.22Y=-406.76Z=19.97949981689453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-986.22Y=-418.76Z=38.81114959716797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1010.22Y=-406.76Z=18.70958137512207.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.22Y=-412.76Z=62.992496490478516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1130.22Y=-410.76Z=15.631073951721191.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1170.22Y=-404.76Z=28.817459106445312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=293.78Y=-408.76Z=45.98131561279297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=273.78Y=-386.76Z=22.326496124267578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.22000000000003Y=-390.76Z=35.75809097290039.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-66.22000000000003Y=-390.76Z=45.981327056884766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=219.77999999999997Y=-412.76Z=33.62895584106445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-842.22Y=-378.76Z=33.778953552246094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-872.22Y=-378.76Z=24.62649917602539.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-902.22Y=-378.76Z=35.93984603881836.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1048.22Y=-378.76Z=15.631077766418457.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-982.22Y=-374.76Z=43.64950942993164.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-794.22Y=-372.76Z=91.3502197265625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1002.22Y=-364.76Z=28.817461013793945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1158.22Y=-364.76Z=28.817447662353516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1188.22Y=-368.76Z=43.73076248168945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-886.22Y=-356.76Z=33.77894973754883.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=277.78Y=-354.76Z=43.649513244628906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-118.22000000000003Y=-356.76Z=19.02252769470215.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-852.22Y=-356.76Z=41.838096618652344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1088.22Y=-366.76Z=45.98131561279297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-758.22Y=-362.76Z=35.758094787597656.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-780.22Y=-346.76Z=33.62895965576172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-912.22Y=-350.76Z=62.992496490478516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=285.78Y=-336.76Z=28.81746482849121.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-810.22Y=-336.76Z=120.43131256103516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-990.22Y=-336.76Z=120.43131256103516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-854.22Y=-328.76Z=35.73809051513672.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1130.22Y=-340.76Z=16.876724243164062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1158.22Y=-334.76Z=15.631058692932129.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1190.22Y=-336.76Z=15.631037712097168.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-110.22000000000003Y=-332.76Z=43.749515533447266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-762.22Y=-324.76Z=24.255592346191406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-912.22Y=-322.76Z=28.817459106445312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1048.22Y=-342.76Z=75.77371978759766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1090.22Y=-330.76Z=62.9681282043457.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-52.22000000000003Y=-354.76Z=28.817462921142578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=283.78Y=-314.76Z=19.979496002197266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=217.77999999999997Y=-340.76Z=52.05461883544922.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=271.78Y=-304.76Z=22.326496124267578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-866.22Y=-294.76Z=24.509084701538086.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=299.78Y=-294.76Z=19.625015258789062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1024.22Y=-290.76Z=104.03649139404297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1100.22Y=-290.76Z=36.58022689819336.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-816.22Y=-284.76Z=28.81746482849121.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1128.22Y=-286.76Z=24.40774917602539.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1178.22Y=-286.76Z=28.817461013793945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-794.22Y=-284.76Z=16.326499938964844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1194.22Y=-290.76Z=43.73139190673828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1160.22Y=-284.76Z=22.526508331298828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=213.77999999999997Y=-276.76Z=28.817459106445312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-128.22000000000003Y=-302.76Z=33.62895584106445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-54.22000000000003Y=-282.76Z=43.7495002746582.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1034.22Y=-272.76Z=28.817461013793945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-864.22Y=-270.76Z=36.24481201171875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-760.22Y=-282.76Z=80.30593872070312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-904.22Y=-274.76Z=120.43132781982422.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-784.22Y=-264.76Z=16.326499938964844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1094.22Y=-264.76Z=19.30900764465332.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1160.22Y=-260.76Z=22.306507110595703.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-988.22Y=-282.76Z=35.758094787597656.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=27.779999999999973Y=-252.76Z=46.328956604003906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-88.22000000000003Y=-244.76Z=28.885204315185547.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-110.22000000000003Y=-244.76Z=17.902725219726562.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1194.22Y=-254.76Z=15.631075859069824.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.22Y=-244.76Z=18.206758499145508.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1106.22Y=-242.76Z=16.308687210083008.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-766.22Y=-244.76Z=29.911479949951172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-832.22Y=-244.76Z=38.81147003173828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-866.22Y=-244.76Z=62.99250030517578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-904.22Y=-244.76Z=45.98133087158203.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=241.77999999999997Y=-242.76Z=31.135181427001953.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=293.78Y=-240.76Z=46.32895278930664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=141.77999999999997Y=-236.76Z=21.725059509277344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=189.77999999999997Y=-242.76Z=120.43131256103516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1086.22Y=-236.76Z=36.34694290161133.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=57.77999999999997Y=-242.76Z=45.98131561279297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-26.220000000000027Y=-240.76Z=115.8289566040039.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-988.22Y=-234.76Z=30.72243309020996.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1160.22Y=-232.76Z=28.81746482849121.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=111.77999999999997Y=-236.76Z=115.81021881103516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1024.22Y=-228.76Z=15.631059646606445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1066.22Y=-234.76Z=16.308687210083008.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1106.22Y=-226.76Z=16.310564041137695.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1132.22Y=-238.76Z=15.631072998046875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=19.779999999999973Y=-224.76Z=16.326496124267578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=265.78Y=-222.76Z=43.731388092041016.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=235.77999999999997Y=-220.76Z=24.40837860107422.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-132.22000000000003Y=-234.76Z=37.284645080566406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-74.22000000000003Y=-218.76Z=24.408693313598633.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-102.22000000000003Y=-218.76Z=43.732017517089844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-802.22Y=-208.76Z=36.2666015625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1002.22Y=-198.76Z=20.029499053955078.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=289.78Y=-204.76Z=35.739967346191406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=241.77999999999997Y=-198.76Z=106.40836334228516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=175.77999999999997Y=-198.76Z=19.00502586364746.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-852.22Y=-202.76Z=31.152685165405273.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1088.22Y=-198.76Z=19.606277465820312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1122.22Y=-196.76Z=13.306506156921387.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-34.22000000000003Y=-198.76Z=62.992496490478516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-78.22000000000003Y=-198.76Z=31.134557723999023.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-132.22000000000003Y=-198.76Z=46.31084060668945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-878.22Y=-200.76Z=62.99251174926758.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=197.77999999999997Y=-196.76Z=46.328956604003906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.22Y=-194.76Z=15.631065368652344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-912.22Y=-200.76Z=28.580947875976562.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-986.22Y=-190.76Z=112.63230895996094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=25.779999999999973Y=-190.76Z=31.152687072753906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1054.22Y=-188.76Z=20.135772705078125.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=53.77999999999997Y=-188.76Z=38.74562072753906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-758.22Y=-184.76Z=33.62895965576172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-800.22Y=-180.76Z=24.643159866333008.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1028.22Y=-178.76Z=15.631059646606445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1118.22Y=-170.76Z=13.306495666503906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.22000000000003Y=-162.76Z=19.606889724731445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=53.77999999999997Y=-160.76Z=24.743167877197266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=109.77999999999997Y=-172.76Z=26.277362823486328.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=135.77999999999997Y=-166.76Z=75.77371978759766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-124.22000000000003Y=-156.76Z=45.98133087158203.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-804.22Y=-150.76Z=19.3077392578125.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.22Y=-148.76Z=16.326496124267578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1162.22Y=-150.76Z=19.32649040222168.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=193.77999999999997Y=-156.76Z=119.44090270996094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-44.22000000000003Y=-144.76Z=20.029502868652344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-78.22000000000003Y=-144.76Z=46.31114959716797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=259.78Y=-136.76Z=29.48006248474121.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-102.22000000000003Y=-132.76Z=43.732032775878906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-810.22Y=-128.76Z=18.726768493652344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1058.22Y=-128.76Z=31.15268325805664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-856.22Y=-142.76Z=35.55809783935547.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.22Y=-130.76Z=62.99250030517578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1090.22Y=-128.76Z=43.74951934814453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-752.22Y=-124.75999999999999Z=24.64316177368164.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1190.22Y=-144.76Z=13.30649471282959.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-42.22000000000003Y=-112.75999999999999Z=19.022525787353516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=275.78Y=-118.75999999999999Z=15.631075859069824.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=291.78Y=-150.76Z=43.73139572143555.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=181.77999999999997Y=-110.75999999999999Z=62.992496490478516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-78.22000000000003Y=-110.75999999999999Z=35.73996353149414.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-102.22000000000003Y=-98.75999999999999Z=17.901809692382812.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-128.22000000000003Y=-108.75999999999999Z=120.43131256103516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.22000000000003Y=-92.75999999999999Z=29.48004722595215.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=111.77999999999997Y=-102.75999999999999Z=31.152685165405273.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1172.22Y=-92.75999999999999Z=16.326496124267578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-754.22Y=-88.75999999999999Z=33.62895965576172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1124.22Y=-88.75999999999999Z=42.0580940246582.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1196.22Y=-88.75999999999999Z=22.32649803161621.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1070.22Y=-86.75999999999999Z=46.328956604003906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=25.779999999999973Y=-90.75999999999999Z=80.45591735839844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-12.220000000000027Y=-94.75999999999999Z=15.631072998046875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=53.77999999999997Y=-88.75999999999999Z=26.277347564697266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-882.22Y=-82.75999999999999Z=19.02252960205078.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=137.77999999999997Y=-92.75999999999999Z=26.2773494720459.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-910.22Y=-116.75999999999999Z=62.992496490478516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-856.22Y=-82.75999999999999Z=80.30593872070312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-990.22Y=-120.75999999999999Z=106.4264907836914.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1020.22Y=-82.75999999999999Z=17.81597900390625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-82.22000000000003Y=-64.75999999999999Z=15.631073951721191.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-800.22Y=-64.75999999999999Z=19.28302001953125.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-764.22Y=-64.75999999999999Z=19.979496002197266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1056.22Y=-62.75999999999999Z=33.82895278930664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-12.220000000000027Y=-62.75999999999999Z=26.808521270751953.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1078.22Y=-60.75999999999999Z=35.910858154296875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.22000000000003Y=-62.75999999999999Z=46.31084060668945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1136.22Y=-62.75999999999999Z=20.029512405395508.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1190.22Y=-60.75999999999999Z=19.526493072509766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=291.78Y=-60.75999999999999Z=46.31114959716797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.22000000000003Y=-60.75999999999999Z=45.981327056884766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1002.22Y=-44.75999999999999Z=19.625015258789062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=261.78Y=-52.75999999999999Z=43.731388092041016.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-98.22000000000003Y=-40.75999999999999Z=43.731712341308594.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-22.220000000000027Y=-34.75999999999999Z=22.30837059020996.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=199.77999999999997Y=-32.75999999999999Z=24.725666046142578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-822.22Y=-30.75999999999999Z=16.30837059020996.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1068.22Y=-30.75999999999999Z=35.75809097290039.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1140.22Y=-30.75999999999999Z=22.326496124267578.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=141.77999999999997Y=-32.75999999999999Z=20.011369705200195.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=59.77999999999997Y=-28.75999999999999Z=20.011375427246094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-800.22Y=-30.75999999999999Z=24.64316177368164.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1112.22Y=-30.75999999999999Z=21.743165969848633.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-40.22000000000003Y=-32.75999999999999Z=19.00470542907715.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-948.22Y=-34.75999999999999Z=119.2580795288086.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.22000000000003Y=-22.75999999999999Z=16.308996200561523.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-862.22Y=-34.75999999999999Z=38.828956604003906.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=283.78Y=-20.75999999999999Z=45.981327056884766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=243.77999999999997Y=-18.75999999999999Z=15.631059646606445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-130.22000000000003Y=-26.75999999999999Z=46.31084442138672.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-892.22Y=-28.75999999999999Z=45.98131561279297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=203.77999999999997Y=-14.759999999999991Z=19.00535011291504.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=101.77999999999997Y=-28.75999999999999Z=21.95790672302246.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=19.779999999999973Y=-12.759999999999991Z=21.957853317260742.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1002.22Y=-16.75999999999999Z=45.9813117980957.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1052.22Y=-2.759999999999991Z=16.0699520111084.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-752.22Y=-2.759999999999991Z=20.792186737060547.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-932.22Y=-2.759999999999991Z=103.85460662841797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-964.22Y=-2.759999999999991Z=106.22649383544922.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1130.22Y=-4.759999999999991Z=45.981327056884766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1172.22Y=-2.759999999999991Z=20.029497146606445.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1022.22Y=31.24000000000001Z=16.326494216918945.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-886.22Y=31.24000000000001Z=42.05809783935547.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-978.22Y=31.24000000000001Z=35.75809097290039.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1116.22Y=31.24000000000001Z=19.309011459350586.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1142.22Y=31.24000000000001Z=19.292741775512695.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-934.22Y=31.24000000000001Z=45.9813232421875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-800.22Y=33.24000000000001Z=18.108877182006836.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.22Y=33.24000000000001Z=24.725040435791016.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1068.22Y=37.24000000000001Z=45.981327056884766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-762.22Y=39.24000000000001Z=31.152685165405273.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-762.22Y=67.24000000000001Z=43.74951934814453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-864.22Y=63.24000000000001Z=45.981327056884766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1006.22Y=65.24000000000001Z=30.522645950317383.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-810.22Y=67.24000000000001Z=19.625017166137695.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-894.22Y=65.24000000000001Z=43.749515533447266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-930.22Y=65.24000000000001Z=21.743167877197266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-966.22Y=75.24000000000001Z=13.30650806427002.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1068.22Y=69.24000000000001Z=41.840274810791016.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1142.22Y=73.24000000000001Z=16.30900764465332.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1196.22Y=63.24000000000001Z=15.631073951721191.png\"}"
  },
  {
    "path": "scene_data/seg_map/env_airsim_18.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=172.0Y=-392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=80.0Y=-384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-88.0Y=-380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=12.0Y=-368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=84.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-8.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=168.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1192.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1072.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-84.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=808.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=756.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1564.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=100.0Y=-224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.0Y=-224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1260.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1208.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1064.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=708.0Y=-224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=564.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=-212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=168.0Y=-204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-68.0Y=-184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=56.0Y=-184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=-184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1200.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1248.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1164.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=792.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1648.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1600.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1548.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=740.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=64.0Y=-128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.0Y=-128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-28.0Y=-128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=548.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=-88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1612.0Y=-68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1288.0Y=-68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=708.0Y=-68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1568.0Y=-68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1976.0Y=-64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1876.0Y=-64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-60.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=368.0Y=-48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=828.0Y=-52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=300.0Y=-44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=232.0Y=-40.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=112.0Y=-44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1932.0Y=-56.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1488.0Y=-52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1784.0Y=-44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=928.0Y=-32.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=20.0Y=-40.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2012.0Y=-40.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1052.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1204.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1152.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=388.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=272.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=136.0Y=0.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1564.0Y=0.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=872.0Y=4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1760.0Y=4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=16.0Y=8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1828.0Y=20.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1224.0Y=36.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1108.0Y=36.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2012.0Y=40.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=908.0Y=44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1052.0Y=44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=948.0Y=44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=852.0Y=44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1564.0Y=52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=796.0Y=52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1292.0Y=28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=84.0Y=56.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=556.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=320.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=740.0Y=68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1608.0Y=76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1052.0Y=84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=908.0Y=84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1308.0Y=96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2012.0Y=96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1836.0Y=108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=948.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1772.0Y=120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=568.0Y=120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1952.0Y=128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1560.0Y=136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1444.0Y=136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1052.0Y=140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=948.0Y=156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=228.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=364.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=188.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1612.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1516.0Y=180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1464.0Y=180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1124.0Y=152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=840.0Y=184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1052.0Y=188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1960.0Y=188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1752.0Y=172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=448.0Y=176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=80.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=88.0Y=260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1768.0Y=284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1260.0Y=284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=944.0Y=292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=320.0Y=276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1824.0Y=300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1144.0Y=300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=792.0Y=296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.0Y=344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-120.0Y=344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1992.0Y=328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1924.0Y=356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1760.0Y=352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1300.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1836.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=600.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=508.0Y=368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=340.0Y=368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-28.0Y=372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1256.0Y=384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1772.0Y=392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1092.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=436.0Y=384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=888.0Y=396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2004.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-192.0Y=420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1768.0Y=436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=364.0Y=436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=516.0Y=436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=456.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=420.0Y=436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-124.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=596.0Y=428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1996.0Y=468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1420.0Y=484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=716.0Y=484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=344.0Y=484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1512.0Y=460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1236.0Y=456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=820.0Y=468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1756.0Y=492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1816.0Y=488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=948.0Y=464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-160.0Y=516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-228.0Y=516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-264.0Y=516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=528.0Y=528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=412.0Y=528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1604.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1552.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1516.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1460.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1252.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1200.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1164.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1104.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=900.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=848.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=812.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1992.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1860.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=484.0Y=568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=548.0Y=624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=492.0Y=624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=400.0Y=624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1752.0Y=640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=836.0Y=640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1848.0Y=652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1976.0Y=660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1304.0Y=676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1428.0Y=664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=752.0Y=728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1600.0Y=700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1504.0Y=728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1092.0Y=708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1304.0Y=744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=940.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=752.0Y=780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1208.0Y=820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=792.0Y=824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1936.0Y=812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1512.0Y=808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1856.0Y=840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1432.0Y=808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1956.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1904.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1812.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1516.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1296.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1208.0Y=876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1140.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1096.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=836.0Y=988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=1012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=1048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=792.0Y=1056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=752.0Y=1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=1104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=752.0Y=1132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=940.0Y=1100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=1160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=792.0Y=1176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=1188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=956.0Y=1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=772.0Y=1248.0Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_airsim_23.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-156.0Y=-404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=192.0Y=-352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.0Y=-352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=192.0Y=-308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=108.0Y=-308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.0Y=-308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-164.0Y=-300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-228.0Y=-300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-32.0Y=-284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=16.0Y=-252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-16.0Y=-236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=320.0Y=-224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=244.0Y=-224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=168.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=88.0Y=-204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-164.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-228.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-300.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=12.0Y=-180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-108.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-164.0Y=-144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=352.0Y=-136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=140.0Y=-144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.0Y=-152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=108.0Y=-104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=68.0Y=-104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-244.0Y=-96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=236.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-164.0Y=-76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=100.0Y=-52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.0Y=-32.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-264.0Y=-32.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=-16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-88.0Y=-36.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=100.0Y=20.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=148.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-44.0Y=88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-136.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=312.0Y=116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=148.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-88.0Y=96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=60.0Y=136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=212.0Y=120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=96.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-44.0Y=184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.0Y=200.0Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_airsim_26.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=168.0Y=-3236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=100.0Y=-3236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=604.0Y=-3344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1112.0Y=-3208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-964.0Y=-3136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1432.0Y=-3164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=-3108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=920.0Y=-3100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=528.0Y=-3116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=416.0Y=-3116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=176.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=0.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-416.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-520.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-612.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-980.0Y=-3072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-540.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=76.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=0.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-172.0Y=-3024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-416.0Y=-3028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1128.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1012.0Y=-3024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=920.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=284.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=228.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-200.0Y=-3024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-612.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-924.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1004.0Y=-3012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1024.0Y=-2952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1240.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-392.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-440.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=284.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=228.0Y=-2944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=168.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-4.0Y=-2952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-520.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-940.0Y=-2936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1308.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=96.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-612.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=96.0Y=-2876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-600.0Y=-2868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-2856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-396.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1304.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=688.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=452.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=288.0Y=-2848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=168.0Y=-2848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.0Y=-2848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-444.0Y=-2868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1032.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=992.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=824.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=-2912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=500.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=448.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1088.0Y=-2792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1276.0Y=-2780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=-2780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-96.0Y=-2780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=-2780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1012.0Y=-2780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=204.0Y=-2776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-520.0Y=-2776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1096.0Y=-2744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=-2740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=-2740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=308.0Y=-2704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=184.0Y=-2704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=472.0Y=-2708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-96.0Y=-2712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-388.0Y=-2752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-440.0Y=-2752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1012.0Y=-2692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1308.0Y=-2696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-2696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=936.0Y=-2692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=908.0Y=-2692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=400.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-104.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-408.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=528.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=216.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-276.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-332.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1012.0Y=-2584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.0Y=-2556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-40.0Y=-2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-172.0Y=-2564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-272.0Y=-2564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-2568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=-2584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=-2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=324.0Y=-2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=204.0Y=-2548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=112.0Y=-2564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-104.0Y=-2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1188.0Y=-2444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1120.0Y=-2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-872.0Y=-2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1128.0Y=-2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1288.0Y=-2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1156.0Y=-2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-108.0Y=-2308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-876.0Y=-2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-572.0Y=-2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-680.0Y=-2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-936.0Y=-2296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1064.0Y=-2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=236.0Y=-2296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-772.0Y=-2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-816.0Y=-2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1260.0Y=-2276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1276.0Y=-2224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1040.0Y=-2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-680.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1172.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1124.0Y=-2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=-2200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=384.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=-2216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-288.0Y=-2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-576.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-876.0Y=-2128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1220.0Y=-2136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1300.0Y=-2152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-708.0Y=-2128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-948.0Y=-2128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-216.0Y=-2120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-2120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-308.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=436.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-108.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1316.0Y=-2112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1236.0Y=-2084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-712.0Y=-2072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-152.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-60.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1172.0Y=-2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-576.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1016.0Y=-2012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-76.0Y=-2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-712.0Y=-2012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-876.0Y=-2012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-948.0Y=-2044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1060.0Y=-1996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=-2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-572.0Y=-1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-712.0Y=-1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=524.0Y=-1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=184.0Y=-1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=288.0Y=-1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=-1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.0Y=-1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-576.0Y=-1888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-656.0Y=-1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-704.0Y=-1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-956.0Y=-1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1144.0Y=-1872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-576.0Y=-1824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.0Y=-1816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=68.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-948.0Y=-1768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=644.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=156.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-1708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=532.0Y=-1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=372.0Y=-1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=164.0Y=-1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=68.0Y=-1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=-1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-560.0Y=-1688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-592.0Y=-1688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=520.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=424.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=340.0Y=-1632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=768.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=176.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=124.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-576.0Y=-1628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-268.0Y=-1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-684.0Y=-1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=672.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-180.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-268.0Y=-1548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-128.0Y=-1548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=56.0Y=-1536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=468.0Y=-1504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=860.0Y=-1488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=-1484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=380.0Y=-1500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-180.0Y=-1480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=148.0Y=-1500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-268.0Y=-1452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=772.0Y=-1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=736.0Y=-1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=60.0Y=-1440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-184.0Y=-1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-124.0Y=-1432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=-1368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-1360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-152.0Y=-1368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=-1344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=-1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-1324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=148.0Y=-1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=512.0Y=-1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=-1316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=836.0Y=-1348.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1040.0Y=-1312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=-1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-152.0Y=-1320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=880.0Y=-1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=836.0Y=-1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=-1232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=528.0Y=-1224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=332.0Y=-1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=756.0Y=-1216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=172.0Y=-1216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=124.0Y=-1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=60.0Y=-1188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=-1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=860.0Y=-1192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=860.0Y=-1116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=-1084.0Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_airsim_gz.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=656.0Y=-3300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=768.0Y=-3292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=712.0Y=-3272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-256.0Y=-3272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2620.0Y=-3268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2172.0Y=-3268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-668.0Y=-3264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4192.0Y=-3256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2912.0Y=-3256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-684.0Y=-3248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4180.0Y=-3224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4144.0Y=-3224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3592.0Y=-3224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2876.0Y=-3220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4020.0Y=-3212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3864.0Y=-3204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3004.0Y=-3208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4592.0Y=-3200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4144.0Y=-3192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4184.0Y=-3188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3352.0Y=-3188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3840.0Y=-3160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-644.0Y=-3156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3888.0Y=-3156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3460.0Y=-3164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3688.0Y=-3152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-988.0Y=-3152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4124.0Y=-3148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3788.0Y=-3144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-936.0Y=-3148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5072.0Y=-3140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3512.0Y=-3152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4172.0Y=-3140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3340.0Y=-3136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=428.0Y=-3128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4052.0Y=-3120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2868.0Y=-3132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-324.0Y=-3132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-880.0Y=-3128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1136.0Y=-3128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3672.0Y=-3120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1632.0Y=-3120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4008.0Y=-3120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3964.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3872.0Y=-3108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3836.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5084.0Y=-3096.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3636.0Y=-3092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-636.0Y=-3104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-692.0Y=-3088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2264.0Y=-3084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3316.0Y=-3080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2324.0Y=-3072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4756.0Y=-3060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4708.0Y=-3060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3384.0Y=-3064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5096.0Y=-3056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4616.0Y=-3076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4124.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3476.0Y=-3044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4528.0Y=-3036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3536.0Y=-3032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3228.0Y=-3024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2184.0Y=-3024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3296.0Y=-3012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1000.0Y=-3020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4300.0Y=-3000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1164.0Y=-3012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4644.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4212.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-652.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-732.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2108.0Y=-2992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3868.0Y=-2988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2408.0Y=-2984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3392.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2016.0Y=-3008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2948.0Y=-2980.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2048.0Y=-2984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4392.0Y=-2976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2176.0Y=-2968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4300.0Y=-2964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2684.0Y=-2960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4108.0Y=-2964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2100.0Y=-2952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3312.0Y=-2960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3216.0Y=-2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2368.0Y=-2984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2576.0Y=-2964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2924.0Y=-2940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2088.0Y=-2944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3784.0Y=-2956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2732.0Y=-2936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1732.0Y=-2924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1612.0Y=-2924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3716.0Y=-2920.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3112.0Y=-2936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2420.0Y=-2916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2780.0Y=-2928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1508.0Y=-2900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2068.0Y=-2900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2000.0Y=-2904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2936.0Y=-2896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2884.0Y=-2892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2116.0Y=-2900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-172.0Y=-2904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1572.0Y=-2908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=-2892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1720.0Y=-2884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-596.0Y=-2880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1544.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3200.0Y=-2852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-280.0Y=-2892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3372.0Y=-2844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2340.0Y=-2844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1804.0Y=-2848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2424.0Y=-2844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1620.0Y=-2840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-648.0Y=-2856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=996.0Y=-2836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1732.0Y=-2828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3300.0Y=-2816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2864.0Y=-2812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=812.0Y=-2820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-608.0Y=-2824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-688.0Y=-2820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2764.0Y=-2820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2924.0Y=-2812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2620.0Y=-2792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2324.0Y=-2792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2272.0Y=-2788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2200.0Y=-2816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-180.0Y=-2808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-268.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2396.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-364.0Y=-2788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1792.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1852.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2732.0Y=-2796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3016.0Y=-2764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2592.0Y=-2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2848.0Y=-2748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1132.0Y=-2736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1352.0Y=-2724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1068.0Y=-2728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1688.0Y=-2716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2380.0Y=-2748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-856.0Y=-2676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1144.0Y=-2676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1380.0Y=-2648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-664.0Y=-2676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=948.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=820.0Y=-2628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3040.0Y=-2632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=908.0Y=-2632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1428.0Y=-2672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=-2628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3168.0Y=-2620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-616.0Y=-2612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1136.0Y=-2612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1388.0Y=-2608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2880.0Y=-2608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1308.0Y=-2608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-932.0Y=-2628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2568.0Y=-2596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1804.0Y=-2600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=-2580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-300.0Y=-2648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3176.0Y=-2572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1388.0Y=-2572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-504.0Y=-2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-724.0Y=-2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=408.0Y=-2552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-980.0Y=-2556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2196.0Y=-2540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2492.0Y=-2540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2820.0Y=-2536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=-2532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=164.0Y=-2520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3012.0Y=-2520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=276.0Y=-2532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-184.0Y=-2556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2972.0Y=-2508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4288.0Y=-2488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2520.0Y=-2488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3144.0Y=-2484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1656.0Y=-2476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2140.0Y=-2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1744.0Y=-2468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1996.0Y=-2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1780.0Y=-2464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3888.0Y=-2492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1536.0Y=-2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4444.0Y=-2444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2972.0Y=-2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1908.0Y=-2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-720.0Y=-2436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1672.0Y=-2436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-804.0Y=-2428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3016.0Y=-2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3108.0Y=-2428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-772.0Y=-2424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1708.0Y=-2428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2520.0Y=-2424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2352.0Y=-2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-936.0Y=-2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1848.0Y=-2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-268.0Y=-2416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3224.0Y=-2416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-352.0Y=-2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2844.0Y=-2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2188.0Y=-2388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2272.0Y=-2388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-640.0Y=-2392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-804.0Y=-2388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2096.0Y=-2380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-496.0Y=-2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-596.0Y=-2400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1028.0Y=-2376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=160.0Y=-2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-688.0Y=-2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2688.0Y=-2380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=260.0Y=-2380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-40.0Y=-2368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-924.0Y=-2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=328.0Y=-2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-4.0Y=-2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1200.0Y=-2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1532.0Y=-2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1856.0Y=-2352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2932.0Y=-2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4712.0Y=-2468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2836.0Y=-2336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1480.0Y=-2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2788.0Y=-2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1512.0Y=-2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-724.0Y=-2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2120.0Y=-2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2492.0Y=-2336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=16.0Y=-2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-980.0Y=-2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2420.0Y=-2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2512.0Y=-2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2684.0Y=-2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=428.0Y=-2328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=-2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1160.0Y=-2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2728.0Y=-2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.0Y=-2308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-856.0Y=-2308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3076.0Y=-2296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1472.0Y=-2292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1996.0Y=-2284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3228.0Y=-2284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4268.0Y=-2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2212.0Y=-2284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4488.0Y=-2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=-2288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=256.0Y=-2288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2584.0Y=-2276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-16.0Y=-2272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-404.0Y=-2276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1752.0Y=-2272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2952.0Y=-2276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4096.0Y=-2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3468.0Y=-2356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3132.0Y=-2308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1684.0Y=-2280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1932.0Y=-2264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2476.0Y=-2268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2548.0Y=-2268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1080.0Y=-2260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3768.0Y=-2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-936.0Y=-2248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-976.0Y=-2244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.0Y=-2268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2148.0Y=-2240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2840.0Y=-2236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=-2232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-592.0Y=-2264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1740.0Y=-2232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2800.0Y=-2228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-500.0Y=-2216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2376.0Y=-2216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=428.0Y=-2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2748.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=232.0Y=-2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2304.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=288.0Y=-2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=124.0Y=-2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1180.0Y=-2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3216.0Y=-2196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3020.0Y=-2200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3156.0Y=-2192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2448.0Y=-2196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3128.0Y=-2188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=180.0Y=-2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-48.0Y=-2180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-496.0Y=-2184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=500.0Y=-2184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2772.0Y=-2180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=344.0Y=-2176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1876.0Y=-2172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1988.0Y=-2176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2256.0Y=-2164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2576.0Y=-2160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=92.0Y=-2168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2972.0Y=-2156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1052.0Y=-2156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1324.0Y=-2152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2876.0Y=-2152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=436.0Y=-2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1704.0Y=-2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1740.0Y=-2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1896.0Y=-2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2116.0Y=-2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=368.0Y=-2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1860.0Y=-2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-56.0Y=-2136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-100.0Y=-2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=316.0Y=-2132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=88.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2528.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1540.0Y=-2112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2412.0Y=-2116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.0Y=-2128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2140.0Y=-2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-484.0Y=-2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1452.0Y=-2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-52.0Y=-2092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.0Y=-2092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-164.0Y=-2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1996.0Y=-2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1280.0Y=-2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-204.0Y=-2084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-604.0Y=-2096.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1092.0Y=-2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2584.0Y=-2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1960.0Y=-2076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4880.0Y=-2512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=-2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2860.0Y=-2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2472.0Y=-2072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=124.0Y=-2072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-256.0Y=-2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1180.0Y=-2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1700.0Y=-2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1736.0Y=-2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1880.0Y=-2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-616.0Y=-2060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2708.0Y=-2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2088.0Y=-2084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-156.0Y=-2056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-100.0Y=-2056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2672.0Y=-2076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2520.0Y=-2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2240.0Y=-2060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-500.0Y=-2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-760.0Y=-2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1392.0Y=-2056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-540.0Y=-2044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=196.0Y=-2044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2316.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-824.0Y=-2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2172.0Y=-2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1956.0Y=-2032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1984.0Y=-2032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=8.0Y=-2024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1552.0Y=-2024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1316.0Y=-2032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=140.0Y=-2020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4616.0Y=-2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=176.0Y=-2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-728.0Y=-2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-764.0Y=-2004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1320.0Y=-2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2560.0Y=-2004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=308.0Y=-2000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2660.0Y=-2020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1664.0Y=-2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1444.0Y=-1992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3220.0Y=-1992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=8.0Y=-1984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1800.0Y=-1996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2712.0Y=-1984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-60.0Y=-1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-132.0Y=-2000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-840.0Y=-1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2220.0Y=-1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=244.0Y=-1964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2824.0Y=-1964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3024.0Y=-1964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1520.0Y=-1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1304.0Y=-1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1244.0Y=-1956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-236.0Y=-1988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2508.0Y=-1948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2592.0Y=-1952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2932.0Y=-1952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2504.0Y=-1964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1956.0Y=-1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2440.0Y=-1944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2384.0Y=-1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2160.0Y=-1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=152.0Y=-1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2348.0Y=-1944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3136.0Y=-1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=112.0Y=-1932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2312.0Y=-1936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2696.0Y=-1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3064.0Y=-1932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-44.0Y=-1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1104.0Y=-1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1312.0Y=-1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2556.0Y=-1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1400.0Y=-1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2444.0Y=-1912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2476.0Y=-1912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1068.0Y=-1904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1464.0Y=-1908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2388.0Y=-1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-148.0Y=-1904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2156.0Y=-1892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2212.0Y=-1892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=124.0Y=-1892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1140.0Y=-1888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-84.0Y=-1888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2572.0Y=-1884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=192.0Y=-1884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2840.0Y=-1880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2900.0Y=-1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-8.0Y=-1880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2456.0Y=-1868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1524.0Y=-1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2780.0Y=-1860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=112.0Y=-1856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1136.0Y=-1852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2380.0Y=-1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2412.0Y=-1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2888.0Y=-1840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2296.0Y=-1840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2924.0Y=-1832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2484.0Y=-1820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2984.0Y=-1820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.0Y=-1820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-116.0Y=-1816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1076.0Y=-1812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1116.0Y=-1812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2684.0Y=-1808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=936.0Y=-1800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2884.0Y=-1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2536.0Y=-1796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2588.0Y=-1796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=424.0Y=-1792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=512.0Y=-1792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=300.0Y=-1796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3116.0Y=-1780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3196.0Y=-1772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1532.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=172.0Y=-1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4540.0Y=-1764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4012.0Y=-1880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1872.0Y=-1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=336.0Y=-1724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2048.0Y=-1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2108.0Y=-1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1992.0Y=-1716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3544.0Y=-1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2332.0Y=-1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1728.0Y=-1716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=488.0Y=-1708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1544.0Y=-1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2204.0Y=-1740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2052.0Y=-1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1924.0Y=-1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=-1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=212.0Y=-1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-176.0Y=-1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4824.0Y=-1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1372.0Y=-1688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1260.0Y=-1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-276.0Y=-1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1504.0Y=-1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1360.0Y=-1724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2608.0Y=-1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2080.0Y=-1676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4688.0Y=-1732.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1140.0Y=-1752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1080.0Y=-1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1992.0Y=-1664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2540.0Y=-1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=-1664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2168.0Y=-1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1888.0Y=-1660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2380.0Y=-1656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=372.0Y=-1652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1732.0Y=-1656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=-1652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-204.0Y=-1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-244.0Y=-1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-276.0Y=-1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1828.0Y=-1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4804.0Y=-1640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2112.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2212.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2056.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-500.0Y=-1644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=472.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1768.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1312.0Y=-1608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1616.0Y=-1616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1708.0Y=-1612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2016.0Y=-1608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1896.0Y=-1604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-808.0Y=-1608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1844.0Y=-1596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2172.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=-1600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2028.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1884.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-724.0Y=-1592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2428.0Y=-1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1640.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-320.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-352.0Y=-1552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=304.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=244.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=456.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-520.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1548.0Y=-1540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1308.0Y=-1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-888.0Y=-1532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1492.0Y=-1524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-984.0Y=-1512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2216.0Y=-1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2072.0Y=-1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1928.0Y=-1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=432.0Y=-1496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1364.0Y=-1468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1452.0Y=-1504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-896.0Y=-1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1052.0Y=-1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=224.0Y=-1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1092.0Y=-1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1264.0Y=-1468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=960.0Y=-1456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-972.0Y=-1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1644.0Y=-1444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1064.0Y=-1432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=380.0Y=-1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=184.0Y=-1416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-980.0Y=-1408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1356.0Y=-1412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1012.0Y=-1408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1512.0Y=-1428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1312.0Y=-1400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-924.0Y=-1396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=104.0Y=-1388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1412.0Y=-1384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1264.0Y=-1392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1024.0Y=-1352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1328.0Y=-1324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1264.0Y=-1324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=960.0Y=-1332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1544.0Y=-1336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=68.0Y=-1284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1416.0Y=-1292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1120.0Y=-1296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1344.0Y=-1264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1284.0Y=-1252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1908.0Y=-1180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2328.0Y=-1180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2020.0Y=-1176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2576.0Y=-1184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1860.0Y=-1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2276.0Y=-1180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1792.0Y=-1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1888.0Y=-1140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1924.0Y=-1140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2144.0Y=-1140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2184.0Y=-1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2248.0Y=-1132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2004.0Y=-1136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2196.0Y=-1108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2256.0Y=-1084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2520.0Y=-1084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1548.0Y=-1092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1656.0Y=-1060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1460.0Y=-1056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1696.0Y=-1036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1336.0Y=-1052.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2844.0Y=-1012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2880.0Y=-996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3008.0Y=-976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1496.0Y=-964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3008.0Y=-940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3044.0Y=-936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1756.0Y=-912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1924.0Y=-908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1416.0Y=-900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1540.0Y=-928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5160.0Y=-892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3736.0Y=-896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2824.0Y=-904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1744.0Y=-876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3632.0Y=-884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1844.0Y=-876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3532.0Y=-876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3428.0Y=-864.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3080.0Y=-852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-920.0Y=-868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1908.0Y=-848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1744.0Y=-832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2240.0Y=-832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4224.0Y=-844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3264.0Y=-840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2812.0Y=-832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3116.0Y=-832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2404.0Y=-816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4576.0Y=-824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1648.0Y=-816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1900.0Y=-800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2428.0Y=-796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1728.0Y=-784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3340.0Y=-800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3540.0Y=-784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2992.0Y=-788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2932.0Y=-776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2936.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1324.0Y=-788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2752.0Y=-776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2692.0Y=-772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2856.0Y=-764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2988.0Y=-764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3204.0Y=-784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-160.0Y=-764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3436.0Y=-768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4640.0Y=-772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2528.0Y=-748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2140.0Y=-748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1700.0Y=-740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2592.0Y=-744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4140.0Y=-744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2764.0Y=-760.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1732.0Y=-736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2944.0Y=-732.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2892.0Y=-772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2964.0Y=-728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-328.0Y=-724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2676.0Y=-728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2008.0Y=-720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2880.0Y=-724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4860.0Y=-736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2824.0Y=-712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2400.0Y=-712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2292.0Y=-704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2788.0Y=-712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4396.0Y=-780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2520.0Y=-696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2672.0Y=-692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2096.0Y=-688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3724.0Y=-772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3276.0Y=-680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2984.0Y=-692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1852.0Y=-676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4864.0Y=-676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4508.0Y=-696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2888.0Y=-680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2008.0Y=-684.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4600.0Y=-656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2580.0Y=-648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1796.0Y=-648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2520.0Y=-644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1952.0Y=-648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3208.0Y=-644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3452.0Y=-672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4156.0Y=-640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2944.0Y=-636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1540.0Y=-640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1196.0Y=-616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1848.0Y=-612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4868.0Y=-604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1340.0Y=-624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3644.0Y=-600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1708.0Y=-608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2524.0Y=-596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2596.0Y=-580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2568.0Y=-580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3512.0Y=-596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2132.0Y=-600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=-604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=936.0Y=-584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1288.0Y=-584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1552.0Y=-580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1420.0Y=-612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3120.0Y=-604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4576.0Y=-564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1812.0Y=-564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=872.0Y=-564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3316.0Y=-552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1116.0Y=-544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2092.0Y=-556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=-544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4780.0Y=-536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1416.0Y=-544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=664.0Y=-540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1324.0Y=-536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2380.0Y=-524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2296.0Y=-524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=-528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2460.0Y=-520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3016.0Y=-548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2940.0Y=-548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1960.0Y=-524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=508.0Y=-516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4488.0Y=-540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2848.0Y=-540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2776.0Y=-564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3168.0Y=-512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2036.0Y=-504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2304.0Y=-496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1044.0Y=-504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=308.0Y=-496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4148.0Y=-492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1536.0Y=-492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1388.0Y=-516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1716.0Y=-504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1980.0Y=-484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1424.0Y=-484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1488.0Y=-484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4772.0Y=-476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1316.0Y=-476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2612.0Y=-1500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1804.0Y=-484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1812.0Y=-464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3108.0Y=-484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2508.0Y=-456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1152.0Y=-456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1016.0Y=-460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4352.0Y=-532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1456.0Y=-472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1716.0Y=-448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1292.0Y=-444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3672.0Y=-448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=164.0Y=-456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3448.0Y=-440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4704.0Y=-468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3532.0Y=-444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=964.0Y=-444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2108.0Y=-440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1892.0Y=-456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=900.0Y=-444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-696.0Y=-428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-844.0Y=-424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-812.0Y=-424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1836.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=844.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=796.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=652.0Y=-420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2124.0Y=-448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1412.0Y=-404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=396.0Y=-412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3208.0Y=-428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1376.0Y=-400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4152.0Y=-408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3032.0Y=-436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1164.0Y=-396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1828.0Y=-396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2344.0Y=-412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2952.0Y=-428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2504.0Y=-396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=972.0Y=-396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1800.0Y=-392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-712.0Y=-392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1344.0Y=-388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2852.0Y=-424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2476.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-676.0Y=-388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4944.0Y=-1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4752.0Y=-384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3472.0Y=-380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2772.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1452.0Y=-396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=152.0Y=-380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1580.0Y=-392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1764.0Y=-380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1948.0Y=-404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=472.0Y=-400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2716.0Y=-376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4252.0Y=-380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2600.0Y=-376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1908.0Y=-368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1276.0Y=-384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=944.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2480.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1544.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=828.0Y=-368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1144.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2064.0Y=-360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3644.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1404.0Y=-352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1664.0Y=-356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-40.0Y=-412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=-344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=332.0Y=-344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1824.0Y=-344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3736.0Y=-388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2012.0Y=-332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1340.0Y=-336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1532.0Y=-336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1452.0Y=-336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2988.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1176.0Y=-328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2008.0Y=-324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1012.0Y=-336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4048.0Y=-400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=980.0Y=-324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=880.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1212.0Y=-332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2600.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1696.0Y=-320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=364.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-532.0Y=-352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1444.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1772.0Y=-332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1532.0Y=-328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3668.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3120.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1352.0Y=-304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4744.0Y=-300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3304.0Y=-308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3212.0Y=-320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1104.0Y=-316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1580.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-808.0Y=-328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2148.0Y=-300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1800.0Y=-332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1296.0Y=-292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2000.0Y=-296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2396.0Y=-292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4924.0Y=-296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1156.0Y=-292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2456.0Y=-288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4560.0Y=-308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4448.0Y=-320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2956.0Y=-284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2864.0Y=-280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2572.0Y=-284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=996.0Y=-284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=472.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2260.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4100.0Y=-280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3120.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=-264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=620.0Y=-264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1888.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=-264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2404.0Y=-276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2048.0Y=-256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=-268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2452.0Y=-256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3136.0Y=-256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1548.0Y=-252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2040.0Y=-252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4760.0Y=-260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4704.0Y=-248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1420.0Y=-248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1220.0Y=-252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2208.0Y=-248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=968.0Y=-240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1644.0Y=-244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=-240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1364.0Y=-240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3020.0Y=-236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=468.0Y=-236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=136.0Y=-264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4844.0Y=-228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4316.0Y=-300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2156.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2588.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1728.0Y=-228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2228.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-808.0Y=-224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2316.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1596.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=432.0Y=-204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4440.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1676.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1520.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-604.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3100.0Y=-196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-708.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=224.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=152.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1416.0Y=-192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1588.0Y=-204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2624.0Y=-192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3024.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1548.0Y=-192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=928.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=764.0Y=-1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-84.0Y=-240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-420.0Y=-212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1520.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4524.0Y=-196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2160.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-552.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1976.0Y=-176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1020.0Y=-176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=856.0Y=-180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1904.0Y=-180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1616.0Y=-176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1116.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1180.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1052.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1672.0Y=-180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4820.0Y=-164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-592.0Y=-164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-744.0Y=-184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1364.0Y=-180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1560.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4772.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2116.0Y=-168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1676.0Y=-160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2940.0Y=-148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4652.0Y=-160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1824.0Y=-144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1884.0Y=-144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1920.0Y=-144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1316.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3100.0Y=-140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1924.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-708.0Y=-136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-748.0Y=-140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1568.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1956.0Y=-128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1988.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1860.0Y=-160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=784.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=236.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1208.0Y=-176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1408.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1104.0Y=-128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=932.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=872.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=628.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=496.0Y=-124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4764.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1352.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2740.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1296.0Y=-108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1092.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4048.0Y=-108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-960.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1864.0Y=-104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=432.0Y=-96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-104.0Y=-96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1924.0Y=-100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1028.0Y=-96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1140.0Y=-88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=784.0Y=-88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=660.0Y=-92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=328.0Y=-76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=584.0Y=-64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=8.0Y=-68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2296.0Y=-92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2164.0Y=-104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3408.0Y=-64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1892.0Y=-48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=-48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=-36.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=72.0Y=-36.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1564.0Y=-20.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3396.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1140.0Y=-16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1616.0Y=-16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1592.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.0Y=-28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1512.0Y=-76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2012.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4776.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4740.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1508.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4812.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-856.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1108.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4144.0Y=12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3252.0Y=-44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1672.0Y=8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1028.0Y=0.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3428.0Y=4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3392.0Y=20.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3300.0Y=16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3124.0Y=-24.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-568.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-444.0Y=-60.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3000.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2288.0Y=28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3064.0Y=0.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3504.0Y=16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1864.0Y=28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1924.0Y=28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2400.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1700.0Y=8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2476.0Y=0.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3172.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4988.0Y=52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1532.0Y=56.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2272.0Y=56.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2352.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=784.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4332.0Y=28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.0Y=56.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4208.0Y=68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2460.0Y=76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-716.0Y=68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-792.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-564.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2176.0Y=52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2392.0Y=84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3300.0Y=88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=624.0Y=84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-864.0Y=76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4116.0Y=92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=444.0Y=92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2844.0Y=52.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=808.0Y=96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3420.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=176.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1764.0Y=92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2272.0Y=92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3972.0Y=44.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=216.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1672.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-596.0Y=108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1584.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=564.0Y=84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=512.0Y=88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2200.0Y=112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.0Y=116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1884.0Y=116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2344.0Y=112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=764.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2300.0Y=120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3224.0Y=124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2564.0Y=72.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3440.0Y=132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3396.0Y=136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2840.0Y=132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=660.0Y=128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3480.0Y=132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2600.0Y=144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=808.0Y=144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3132.0Y=156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4568.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3024.0Y=152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3416.0Y=172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3136.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=576.0Y=164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4100.0Y=172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4188.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=824.0Y=184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=620.0Y=184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=480.0Y=192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4580.0Y=196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4256.0Y=188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=448.0Y=196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4152.0Y=176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2344.0Y=196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2556.0Y=196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3208.0Y=216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-860.0Y=224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1340.0Y=232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1396.0Y=232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=844.0Y=240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=808.0Y=236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=604.0Y=240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=452.0Y=236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1596.0Y=208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2240.0Y=208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3260.0Y=240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=240.0Y=192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1536.0Y=228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4284.0Y=248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2856.0Y=248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3372.0Y=252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4584.0Y=244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2956.0Y=256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-808.0Y=236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1740.0Y=244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2464.0Y=224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-700.0Y=264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1128.0Y=256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1988.0Y=264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3432.0Y=260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-532.0Y=200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3108.0Y=260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1660.0Y=256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4112.0Y=268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=768.0Y=280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4644.0Y=260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3328.0Y=288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4536.0Y=272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4236.0Y=276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2200.0Y=288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2780.0Y=276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4076.0Y=304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4032.0Y=288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3140.0Y=292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3432.0Y=308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1876.0Y=304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2292.0Y=312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=316.0Y=316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-88.0Y=208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3400.0Y=316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=524.0Y=324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=480.0Y=320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2508.0Y=312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3976.0Y=300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=840.0Y=308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2480.0Y=336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=568.0Y=324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=176.0Y=332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1520.0Y=328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4248.0Y=332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1920.0Y=232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3868.0Y=340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1676.0Y=344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=752.0Y=360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1776.0Y=312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2324.0Y=352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=824.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1516.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=508.0Y=368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=448.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1352.0Y=340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=904.0Y=356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=876.0Y=372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2124.0Y=340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4192.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4904.0Y=380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3852.0Y=384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=772.0Y=388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2844.0Y=372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=448.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=492.0Y=408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=256.0Y=380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-288.0Y=404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4036.0Y=416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2796.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4104.0Y=400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3220.0Y=412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=544.0Y=432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=244.0Y=432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=36.0Y=408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1128.0Y=412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2020.0Y=436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=448.0Y=444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2784.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4536.0Y=448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2536.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4736.0Y=456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3288.0Y=444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=204.0Y=456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1288.0Y=460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4920.0Y=464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4968.0Y=448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=312.0Y=468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-672.0Y=476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-728.0Y=472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-776.0Y=472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.0Y=472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=244.0Y=476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=164.0Y=444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2648.0Y=472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2440.0Y=476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1552.0Y=488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1940.0Y=488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3852.0Y=472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1600.0Y=480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=888.0Y=500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-868.0Y=496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3912.0Y=480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2008.0Y=508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1476.0Y=480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3248.0Y=500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.0Y=432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1336.0Y=508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1828.0Y=524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3852.0Y=524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=968.0Y=532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-468.0Y=476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1636.0Y=536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2232.0Y=480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2780.0Y=520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1124.0Y=524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2468.0Y=528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-416.0Y=364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-556.0Y=536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-700.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2784.0Y=528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4600.0Y=548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2408.0Y=552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-824.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2600.0Y=560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-744.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-780.0Y=560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1588.0Y=560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2528.0Y=544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3520.0Y=572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4496.0Y=564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4716.0Y=572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3716.0Y=536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2496.0Y=564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=428.0Y=576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3332.0Y=532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=820.0Y=584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1868.0Y=584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2440.0Y=584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=780.0Y=568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3652.0Y=592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1912.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=232.0Y=588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2776.0Y=584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4280.0Y=572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2776.0Y=596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=936.0Y=596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2740.0Y=540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4852.0Y=600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4120.0Y=580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4020.0Y=600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1716.0Y=600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2296.0Y=608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4972.0Y=616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2552.0Y=616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-56.0Y=608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2436.0Y=616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2644.0Y=608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5012.0Y=600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3544.0Y=612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1396.0Y=608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4396.0Y=632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2504.0Y=640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2436.0Y=636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=944.0Y=624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4880.0Y=640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4728.0Y=648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3312.0Y=648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4944.0Y=660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3936.0Y=660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2460.0Y=664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4184.0Y=672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=208.0Y=652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2040.0Y=636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2636.0Y=664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4872.0Y=676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1460.0Y=600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2768.0Y=656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=576.0Y=676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3180.0Y=676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4332.0Y=664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3820.0Y=676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2028.0Y=312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4120.0Y=692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1120.0Y=688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1072.0Y=692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=456.0Y=704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3012.0Y=700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1404.0Y=688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=212.0Y=712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1772.0Y=712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4036.0Y=712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2464.0Y=716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1572.0Y=712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1656.0Y=716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4756.0Y=724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2784.0Y=716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3992.0Y=732.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2908.0Y=712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2536.0Y=700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3644.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3956.0Y=744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2576.0Y=744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1564.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1004.0Y=732.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2328.0Y=740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4672.0Y=740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1140.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2240.0Y=740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2452.0Y=756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-696.0Y=752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3864.0Y=752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1652.0Y=756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1696.0Y=760.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2644.0Y=756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1336.0Y=756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2404.0Y=716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4444.0Y=772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3536.0Y=784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2468.0Y=788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1952.0Y=776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1080.0Y=776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=572.0Y=796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=408.0Y=796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4380.0Y=728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=816.0Y=800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=756.0Y=800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1880.0Y=792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2768.0Y=792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1668.0Y=784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1032.0Y=804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1068.0Y=804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2080.0Y=784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4100.0Y=820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2632.0Y=820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1592.0Y=820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4828.0Y=828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=356.0Y=804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3816.0Y=828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4400.0Y=824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4216.0Y=816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3652.0Y=832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=216.0Y=844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2484.0Y=840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3580.0Y=820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=496.0Y=844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3884.0Y=844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3384.0Y=808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=564.0Y=852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=328.0Y=852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=44.0Y=828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1872.0Y=848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2444.0Y=848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4608.0Y=836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3540.0Y=852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1276.0Y=844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1680.0Y=856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4836.0Y=860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4168.0Y=864.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3232.0Y=832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1456.0Y=872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-388.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2440.0Y=876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3176.0Y=876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4744.0Y=852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=532.0Y=884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4340.0Y=880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3816.0Y=884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-360.0Y=888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2620.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3044.0Y=884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-564.0Y=772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-716.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5184.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=320.0Y=900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=244.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2984.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2440.0Y=908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2736.0Y=904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1080.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-832.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2780.0Y=900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3232.0Y=928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4756.0Y=924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4580.0Y=916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4492.0Y=896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1324.0Y=932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1672.0Y=912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1916.0Y=940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-812.0Y=944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2024.0Y=928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-704.0Y=952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-964.0Y=948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1120.0Y=928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1600.0Y=944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3812.0Y=940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4400.0Y=948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2436.0Y=960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2596.0Y=948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5204.0Y=956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1280.0Y=964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1176.0Y=968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2740.0Y=972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3200.0Y=976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1984.0Y=976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1864.0Y=980.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4356.0Y=984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1796.0Y=984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4708.0Y=952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2776.0Y=980.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-824.0Y=992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1808.0Y=988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4972.0Y=932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2224.0Y=980.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1568.0Y=992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3248.0Y=972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3932.0Y=984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2756.0Y=1000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4736.0Y=1008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1516.0Y=1004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4156.0Y=992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2768.0Y=1016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1604.0Y=1008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1460.0Y=1008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2040.0Y=1016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2640.0Y=1024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3156.0Y=1016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=1024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1144.0Y=1016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1508.0Y=1024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3980.0Y=1032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4592.0Y=1032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4076.0Y=1004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3816.0Y=1016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3668.0Y=1016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2416.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1208.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1640.0Y=1008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2040.0Y=1044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1776.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=408.0Y=1044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1460.0Y=1012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=304.0Y=1044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1584.0Y=1032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2584.0Y=1032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3228.0Y=1048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3660.0Y=1048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1712.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1464.0Y=1052.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1404.0Y=1036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1356.0Y=1060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=576.0Y=1060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=356.0Y=1064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4604.0Y=1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1128.0Y=1044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=536.0Y=1072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=252.0Y=1048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4500.0Y=1076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4080.0Y=1076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1312.0Y=1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2352.0Y=1084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4840.0Y=1092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2436.0Y=1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=808.0Y=1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2588.0Y=1064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1060.0Y=1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4924.0Y=1100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=436.0Y=1084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2768.0Y=1092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1612.0Y=1100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1152.0Y=1112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-316.0Y=1076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1412.0Y=1108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3664.0Y=1084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2844.0Y=1100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1524.0Y=1096.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=568.0Y=1120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3820.0Y=1092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2292.0Y=1116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2552.0Y=1120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4036.0Y=1112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3768.0Y=1120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3720.0Y=1124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2340.0Y=1116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3156.0Y=1124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1196.0Y=1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=312.0Y=1104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-680.0Y=1128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2484.0Y=1132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2944.0Y=1120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=804.0Y=1136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-976.0Y=1132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1540.0Y=1124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4160.0Y=1140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1988.0Y=1140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1264.0Y=1140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-756.0Y=1136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2436.0Y=1136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=80.0Y=1132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=212.0Y=1152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-352.0Y=1152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-928.0Y=1152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1016.0Y=1152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4064.0Y=1160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2216.0Y=1120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1468.0Y=1160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1428.0Y=1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2620.0Y=1144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1156.0Y=1164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1224.0Y=1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3220.0Y=1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=552.0Y=1172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=1172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2344.0Y=1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3008.0Y=1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-304.0Y=1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1408.0Y=1152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3112.0Y=1180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1352.0Y=1184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1532.0Y=1192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3920.0Y=1184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2588.0Y=1196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-428.0Y=1172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1828.0Y=1160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1712.0Y=1204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=764.0Y=1204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=188.0Y=1200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2348.0Y=1208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1000.0Y=1208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1936.0Y=1208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2284.0Y=1148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1980.0Y=1200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5208.0Y=1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2568.0Y=1224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=80.0Y=1204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-8.0Y=1224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-436.0Y=1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2320.0Y=1192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3928.0Y=1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3236.0Y=1224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2924.0Y=1192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1440.0Y=1236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1596.0Y=1240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1504.0Y=1244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1260.0Y=1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-308.0Y=1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1556.0Y=1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1032.0Y=1252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4712.0Y=1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1704.0Y=1256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4552.0Y=1260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2012.0Y=1256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4252.0Y=1252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2992.0Y=1256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3064.0Y=1256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.0Y=1268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=1268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-448.0Y=1264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-516.0Y=1236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1196.0Y=1276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2636.0Y=1268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1932.0Y=1268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2372.0Y=1292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1980.0Y=1284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3612.0Y=1292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1996.0Y=1304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1236.0Y=1240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2308.0Y=1300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2788.0Y=1292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=224.0Y=1316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3904.0Y=1312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=1324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=812.0Y=1328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=72.0Y=1296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4832.0Y=1336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3972.0Y=1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2280.0Y=1340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-160.0Y=1364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1052.0Y=1364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=312.0Y=1376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.0Y=1376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4048.0Y=1376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1724.0Y=1372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=396.0Y=1384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-392.0Y=1372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1776.0Y=1384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-580.0Y=1388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1992.0Y=1384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1440.0Y=1392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1168.0Y=1392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=988.0Y=1392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1476.0Y=1400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4372.0Y=1408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3612.0Y=1400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2104.0Y=1396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1040.0Y=1412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2832.0Y=1412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4776.0Y=1408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2964.0Y=1416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4844.0Y=1420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3156.0Y=1420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2444.0Y=1376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2312.0Y=1404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3128.0Y=1424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2532.0Y=1412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1840.0Y=1424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=1428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2556.0Y=1408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2868.0Y=1428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3992.0Y=1420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2888.0Y=1428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=996.0Y=1432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4436.0Y=1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3920.0Y=1420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2048.0Y=1440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=480.0Y=1444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4848.0Y=1448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3520.0Y=1440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2096.0Y=1448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2832.0Y=1432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1792.0Y=1448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1188.0Y=1452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=1440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=568.0Y=1456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=828.0Y=1432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4976.0Y=1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1860.0Y=1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=976.0Y=1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2848.0Y=1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4120.0Y=1456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2752.0Y=1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1360.0Y=1448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1084.0Y=1468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1556.0Y=1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2880.0Y=1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4040.0Y=1476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2192.0Y=1452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4928.0Y=1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1812.0Y=1476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2140.0Y=1436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2756.0Y=1428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2072.0Y=1492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4276.0Y=1476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4196.0Y=1492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2320.0Y=1424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-800.0Y=1492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4796.0Y=1476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4240.0Y=1472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=1444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5160.0Y=1496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3684.0Y=1480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=980.0Y=1504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=1508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2964.0Y=1480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4928.0Y=1512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4960.0Y=1512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1236.0Y=1508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1188.0Y=1508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3528.0Y=1500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=340.0Y=1528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2896.0Y=1524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2512.0Y=1532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2992.0Y=1532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2276.0Y=1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2312.0Y=1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4264.0Y=1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2828.0Y=1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3612.0Y=1540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2624.0Y=1536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4720.0Y=1548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2340.0Y=1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2780.0Y=1548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2484.0Y=1512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2256.0Y=1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=860.0Y=1564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=204.0Y=1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3004.0Y=1568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2692.0Y=1564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2896.0Y=1576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4300.0Y=1544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2848.0Y=1584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=332.0Y=1596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4212.0Y=1596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=1600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1356.0Y=1604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=824.0Y=1604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.0Y=1608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2476.0Y=1608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4576.0Y=1612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4000.0Y=1624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2412.0Y=1616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2812.0Y=1624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2332.0Y=1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2624.0Y=1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.0Y=1564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1464.0Y=1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1512.0Y=1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2564.0Y=1636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4280.0Y=1656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3708.0Y=1644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4556.0Y=1664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4164.0Y=1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3748.0Y=1672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-496.0Y=1576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2976.0Y=1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2856.0Y=1656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2884.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3596.0Y=1684.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3272.0Y=1684.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3640.0Y=1688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3108.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3156.0Y=1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1360.0Y=1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1332.0Y=1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2696.0Y=1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1540.0Y=1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1488.0Y=1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1420.0Y=1708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2760.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3204.0Y=1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2040.0Y=1708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3080.0Y=1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2316.0Y=1692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3628.0Y=1724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1552.0Y=1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2816.0Y=1724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2756.0Y=1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=420.0Y=1728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=376.0Y=1728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2604.0Y=1724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4228.0Y=1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2100.0Y=1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1360.0Y=1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=312.0Y=1728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2984.0Y=1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2496.0Y=1712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-728.0Y=1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1832.0Y=1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1876.0Y=1740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3788.0Y=1692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=260.0Y=1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1980.0Y=1752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3752.0Y=1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1764.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1444.0Y=1764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.0Y=1764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2516.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2728.0Y=1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-928.0Y=1760.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2108.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=972.0Y=1768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=236.0Y=1768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1448.0Y=1784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2324.0Y=1740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1524.0Y=1792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2748.0Y=1792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2944.0Y=1788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2156.0Y=1788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1792.0Y=1800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3668.0Y=1752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1760.0Y=1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1872.0Y=1796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4856.0Y=1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3632.0Y=1788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2672.0Y=1816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2796.0Y=1792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2564.0Y=1812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2848.0Y=1824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1152.0Y=1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2940.0Y=1828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1748.0Y=1840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2512.0Y=1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1440.0Y=1832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2496.0Y=1816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2920.0Y=1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2112.0Y=1852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2404.0Y=1828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2784.0Y=1860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2820.0Y=1868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2352.0Y=1848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4788.0Y=1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=192.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2932.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2496.0Y=1872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2940.0Y=1880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4076.0Y=1872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2688.0Y=1872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1128.0Y=1856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=276.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3008.0Y=1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2776.0Y=1892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2288.0Y=1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2560.0Y=1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2616.0Y=1900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2960.0Y=1908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2992.0Y=1908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3312.0Y=1912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=764.0Y=1904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=584.0Y=1908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2800.0Y=1872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2676.0Y=1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1436.0Y=1920.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-748.0Y=1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4072.0Y=1936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2496.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-960.0Y=1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4028.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1460.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=420.0Y=1948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2124.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1336.0Y=1904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3852.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2868.0Y=1956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2324.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2972.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3884.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=920.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=380.0Y=1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1268.0Y=1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3272.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=324.0Y=1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3056.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4304.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3744.0Y=1952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2760.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-764.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1556.0Y=1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=552.0Y=1976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2928.0Y=1964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2976.0Y=1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2504.0Y=1984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=264.0Y=1968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2068.0Y=1988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2324.0Y=1988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=180.0Y=1996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1232.0Y=2012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1900.0Y=2012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2664.0Y=2012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3156.0Y=2004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=264.0Y=2024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3248.0Y=2028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-928.0Y=2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2288.0Y=2020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2644.0Y=2044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2796.0Y=2036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4268.0Y=2016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1488.0Y=2020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3012.0Y=2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=2036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=888.0Y=2036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=380.0Y=2036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1060.0Y=2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-940.0Y=2064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1236.0Y=2060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2308.0Y=2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1816.0Y=2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2060.0Y=2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=180.0Y=2084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-756.0Y=2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1240.0Y=2092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3668.0Y=2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=328.0Y=2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1972.0Y=2056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1368.0Y=2104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2620.0Y=2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1160.0Y=2108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=904.0Y=2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-788.0Y=2108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2324.0Y=2112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1276.0Y=2112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3064.0Y=2136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3164.0Y=2124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1028.0Y=2124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2068.0Y=2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1888.0Y=2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=352.0Y=2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1180.0Y=2152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4232.0Y=2156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.0Y=2060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4124.0Y=2172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4096.0Y=2164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1264.0Y=2148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4456.0Y=2168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1476.0Y=2176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=2148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-952.0Y=2156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1380.0Y=2160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4160.0Y=2188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3708.0Y=2184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2092.0Y=2184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1892.0Y=2184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2640.0Y=2160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3860.0Y=2196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=412.0Y=2156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1156.0Y=2192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3356.0Y=2200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1676.0Y=2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2976.0Y=2188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4268.0Y=2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2496.0Y=2092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=364.0Y=2196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3144.0Y=2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3988.0Y=2196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4720.0Y=2176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1336.0Y=2220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1212.0Y=2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3304.0Y=2220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4164.0Y=2232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3020.0Y=2236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3856.0Y=2248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2880.0Y=2244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-836.0Y=2240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-712.0Y=2236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1172.0Y=2240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4672.0Y=2256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3648.0Y=2232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2704.0Y=2272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3104.0Y=2268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3016.0Y=2272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1072.0Y=2276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4460.0Y=2252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3592.0Y=2280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2080.0Y=2264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3860.0Y=2284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=872.0Y=2264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=564.0Y=2224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4204.0Y=2296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1688.0Y=2292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3152.0Y=2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-696.0Y=2292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-760.0Y=2288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2780.0Y=2292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4348.0Y=2304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2204.0Y=2284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1540.0Y=2240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=256.0Y=2304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4624.0Y=2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4560.0Y=2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4240.0Y=2304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2148.0Y=2272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-792.0Y=2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4076.0Y=2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3716.0Y=2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3620.0Y=2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3592.0Y=2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2756.0Y=2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2108.0Y=2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3144.0Y=2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=2304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4100.0Y=2336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3948.0Y=2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=872.0Y=2328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2948.0Y=2336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3844.0Y=2336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3008.0Y=2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3880.0Y=2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3108.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2756.0Y=2352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2304.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2688.0Y=2352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1608.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4080.0Y=2356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2060.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1872.0Y=2356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-892.0Y=2348.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4664.0Y=2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2200.0Y=2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1244.0Y=2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1688.0Y=2348.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2384.0Y=2344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2264.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1720.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-832.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4736.0Y=2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4240.0Y=2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3612.0Y=2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3520.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2780.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1828.0Y=2380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3708.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1768.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=688.0Y=2392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1828.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3880.0Y=2388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3840.0Y=2400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3180.0Y=2392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2204.0Y=2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2168.0Y=2392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-900.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1980.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3132.0Y=2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3072.0Y=2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2900.0Y=2344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2680.0Y=2392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4696.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1516.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4424.0Y=2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4176.0Y=2400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2880.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-880.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2304.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2640.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4084.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2388.0Y=2400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2344.0Y=2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=2408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1680.0Y=2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1612.0Y=2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2100.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2384.0Y=2416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=996.0Y=2424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1260.0Y=2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1220.0Y=2456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-700.0Y=2440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.0Y=2444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1000.0Y=2444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4312.0Y=2456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3024.0Y=2456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1928.0Y=2464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=224.0Y=2464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1992.0Y=2460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2780.0Y=2468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3124.0Y=2480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2936.0Y=2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=176.0Y=2476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=388.0Y=2484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-184.0Y=2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2056.0Y=2484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=428.0Y=2436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3892.0Y=2488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=832.0Y=2428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-380.0Y=2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3072.0Y=2500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3332.0Y=2504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2876.0Y=2492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3824.0Y=2512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1768.0Y=2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4076.0Y=2516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2164.0Y=2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=2504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=356.0Y=2524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2072.0Y=2524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-804.0Y=2528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3700.0Y=2528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2836.0Y=2528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2760.0Y=2544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2704.0Y=2556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3068.0Y=2560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-828.0Y=2568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2936.0Y=2576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2132.0Y=2516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2328.0Y=2580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-772.0Y=2584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1900.0Y=2576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1252.0Y=2604.0Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_airsim_sh.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1588.0Y=-3128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1904.0Y=-3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1452.0Y=-3128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1864.0Y=-3100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1320.0Y=-3084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1440.0Y=-3068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1332.0Y=-3060.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1264.0Y=-3020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1324.0Y=-2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1196.0Y=-2984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=316.0Y=-2900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1064.0Y=-2916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1120.0Y=-2900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=648.0Y=-2860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=608.0Y=-2856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=480.0Y=-2856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1108.0Y=-2840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=-2848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=340.0Y=-2820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=968.0Y=-2800.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=540.0Y=-2792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=444.0Y=-2768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=-2768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=896.0Y=-2748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=952.0Y=-2744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=352.0Y=-2748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1640.0Y=-2720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-640.0Y=-2864.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=500.0Y=-2708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=632.0Y=-2684.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=856.0Y=-2684.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=-2680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=728.0Y=-2692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=668.0Y=-2644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=396.0Y=-2664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2260.0Y=-2644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1672.0Y=-2648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=-2632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1204.0Y=-2632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=500.0Y=-2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1616.0Y=-2620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1060.0Y=-2620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=-2620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-2612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1680.0Y=-2600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=-2592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=-2584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1232.0Y=-2580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=688.0Y=-2568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1116.0Y=-2548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1188.0Y=-2544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2196.0Y=-2528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=860.0Y=-2512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=776.0Y=-2496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1396.0Y=-2496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=604.0Y=-2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=836.0Y=-2468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1412.0Y=-2460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1292.0Y=-2456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2220.0Y=-2444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=704.0Y=-2460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1172.0Y=-2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1252.0Y=-2416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=568.0Y=-2416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=992.0Y=-2404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=784.0Y=-2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2240.0Y=-2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=724.0Y=-2368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1436.0Y=-2380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1076.0Y=-2364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=-2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2048.0Y=-2352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=-2324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=736.0Y=-2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-2304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=616.0Y=-2296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1632.0Y=-2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2244.0Y=-2276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=956.0Y=-2264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1088.0Y=-2244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=736.0Y=-2224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=988.0Y=-2220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1888.0Y=-2228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=608.0Y=-2208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=56.0Y=-2200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2232.0Y=-2192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=876.0Y=-2172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=596.0Y=-2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2256.0Y=-2132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1568.0Y=-2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1784.0Y=-2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1844.0Y=-2092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1904.0Y=-2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1700.0Y=-2076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=-2068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1556.0Y=-2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1656.0Y=-2028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=704.0Y=-2016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1808.0Y=-1996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=-1976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1940.0Y=-1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=708.0Y=-1924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2120.0Y=-1900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2068.0Y=-1856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=680.0Y=-1856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=836.0Y=-1808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1988.0Y=-1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=844.0Y=-1788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=748.0Y=-1780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-1784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=868.0Y=-1768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=656.0Y=-1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1060.0Y=-1772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2256.0Y=-1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1172.0Y=-1772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1920.0Y=-1752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1976.0Y=-1740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2068.0Y=-1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=724.0Y=-1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=624.0Y=-1696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1120.0Y=-1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1156.0Y=-1700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=896.0Y=-1688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2248.0Y=-1672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1852.0Y=-1624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=620.0Y=-1628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-1628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1044.0Y=-1644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1520.0Y=-1604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=-1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.0Y=-1656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-560.0Y=-1572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-80.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1508.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1180.0Y=-1572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=680.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1612.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=812.0Y=-1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1032.0Y=-1556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-280.0Y=-1536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=816.0Y=-1520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1756.0Y=-1532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-100.0Y=-1512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.0Y=-1500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1824.0Y=-1516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-560.0Y=-1496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2084.0Y=-1492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=-1492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1940.0Y=-1500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1180.0Y=-1492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1512.0Y=-1480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=-1476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1020.0Y=-1472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-444.0Y=-1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-616.0Y=-1460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1684.0Y=-1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2068.0Y=-1448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-496.0Y=-1428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1800.0Y=-1420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2056.0Y=-1404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1096.0Y=-1408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=860.0Y=-1396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1152.0Y=-1424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-660.0Y=-1384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=-1376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1524.0Y=-1324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-1336.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1556.0Y=-1316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=732.0Y=-1312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2484.0Y=-1308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=-1304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2172.0Y=-1300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1608.0Y=-1292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1764.0Y=-1292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2500.0Y=-1264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1512.0Y=-1264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=-1256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-832.0Y=-1268.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-892.0Y=-1252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1676.0Y=-1244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-660.0Y=-1232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2144.0Y=-1236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1592.0Y=-1228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2072.0Y=-1228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2188.0Y=-1224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2520.0Y=-1216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2256.0Y=-1212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1740.0Y=-1212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1508.0Y=-1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1016.0Y=-1248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1964.0Y=-1208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1764.0Y=-1196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1580.0Y=-1192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1768.0Y=-1192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=768.0Y=-1160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1064.0Y=-1172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-796.0Y=-1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-876.0Y=-1164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1116.0Y=-1164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-944.0Y=-1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1836.0Y=-1148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=956.0Y=-1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1772.0Y=-1128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1484.0Y=-1128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=848.0Y=-1112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=-1108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1092.0Y=-1108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-808.0Y=-1108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2100.0Y=-1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1464.0Y=-1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=684.0Y=-1088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=532.0Y=-1168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1064.0Y=-1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1096.0Y=-1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2320.0Y=-1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-976.0Y=-1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-888.0Y=-1076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.0Y=-1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2064.0Y=-1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1084.0Y=-1064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1284.0Y=-1064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1256.0Y=-1056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1660.0Y=-1084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1800.0Y=-1052.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1876.0Y=-1048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.0Y=-1032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=812.0Y=-1012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-884.0Y=-1008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1956.0Y=-1024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-976.0Y=-1000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1260.0Y=-1008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1456.0Y=-1004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1048.0Y=-1004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1864.0Y=-992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1100.0Y=-996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2164.0Y=-984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1816.0Y=-1000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=668.0Y=-964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=392.0Y=-968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1172.0Y=-984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1436.0Y=-960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1248.0Y=-964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2228.0Y=-964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2000.0Y=-972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=720.0Y=-944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2316.0Y=-956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2252.0Y=-936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=508.0Y=-944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1524.0Y=-932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=772.0Y=-928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1708.0Y=-936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2148.0Y=-920.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-412.0Y=-940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1592.0Y=-928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1784.0Y=-924.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2132.0Y=-900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2252.0Y=-904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1924.0Y=-888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1872.0Y=-872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1000.0Y=-876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=-880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2208.0Y=-864.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1180.0Y=-896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1976.0Y=-880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1504.0Y=-868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1064.0Y=-860.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2192.0Y=-848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=700.0Y=-868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1584.0Y=-848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1412.0Y=-848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2316.0Y=-836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2144.0Y=-828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1764.0Y=-832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1472.0Y=-816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1904.0Y=-820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2116.0Y=-808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1904.0Y=-812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1244.0Y=-816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1952.0Y=-804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=-816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1340.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1188.0Y=-796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1856.0Y=-784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2104.0Y=-784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-968.0Y=-780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1148.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1752.0Y=-788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1100.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1476.0Y=-776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-880.0Y=-792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1256.0Y=-768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1816.0Y=-748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1780.0Y=-760.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=-748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=276.0Y=-740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1988.0Y=-744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1580.0Y=-732.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1192.0Y=-728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=936.0Y=-716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2248.0Y=-720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=416.0Y=-712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=576.0Y=-708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.0Y=-712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=636.0Y=-704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1184.0Y=-704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1152.0Y=-716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1188.0Y=-716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-968.0Y=-692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1344.0Y=-688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1752.0Y=-700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1772.0Y=-688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=568.0Y=-676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2164.0Y=-668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1476.0Y=-692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1520.0Y=-696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2304.0Y=-680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=920.0Y=-668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1440.0Y=-688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1872.0Y=-648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2228.0Y=-652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-948.0Y=-656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1104.0Y=-656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1348.0Y=-640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.0Y=-656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2076.0Y=-640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=628.0Y=-652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=184.0Y=-636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1596.0Y=-628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1932.0Y=-644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2128.0Y=-616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1156.0Y=-632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1192.0Y=-632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1780.0Y=-620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1880.0Y=-612.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1348.0Y=-644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-740.0Y=-608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2220.0Y=-608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2108.0Y=-600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1252.0Y=-644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=300.0Y=-596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1340.0Y=-596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=940.0Y=-588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=600.0Y=-588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=332.0Y=-584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=272.0Y=-588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=152.0Y=-588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1456.0Y=-592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1932.0Y=-580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=656.0Y=-580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1516.0Y=-588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1876.0Y=-576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1540.0Y=-616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=-568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1588.0Y=-564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=252.0Y=-556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=864.0Y=-556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2252.0Y=-556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1784.0Y=-548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1748.0Y=-560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=380.0Y=-540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1876.0Y=-536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=972.0Y=-540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=912.0Y=-540.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1936.0Y=-536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2360.0Y=-568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=260.0Y=-524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=124.0Y=-520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1996.0Y=-520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=336.0Y=-508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=108.0Y=-504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=-528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1408.0Y=-508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2056.0Y=-508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=536.0Y=-504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=248.0Y=-496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1256.0Y=-516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1940.0Y=-496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1116.0Y=-508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2276.0Y=-504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1308.0Y=-500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2116.0Y=-492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1784.0Y=-484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1884.0Y=-480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=316.0Y=-468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1368.0Y=-500.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2064.0Y=-468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-816.0Y=-460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1312.0Y=-488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=524.0Y=-456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2152.0Y=-452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=60.0Y=-456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=-440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2432.0Y=-452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1240.0Y=-448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=-432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=580.0Y=-420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1292.0Y=-428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1580.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2088.0Y=-424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1180.0Y=-420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=720.0Y=-464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1468.0Y=-416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1352.0Y=-420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2040.0Y=-404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.0Y=-404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2324.0Y=-404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1916.0Y=-388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1164.0Y=-392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1228.0Y=-400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=236.0Y=-376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1992.0Y=-384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=688.0Y=-372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2260.0Y=-392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1400.0Y=-364.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=-352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1020.0Y=-360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1928.0Y=-352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1052.0Y=-356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1132.0Y=-356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2188.0Y=-368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=528.0Y=-344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1148.0Y=-340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1224.0Y=-324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2120.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1040.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2048.0Y=-312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=640.0Y=-284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1304.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-44.0Y=-264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1936.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=0.0Y=-260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2168.0Y=-272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1204.0Y=-256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1968.0Y=-256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1184.0Y=-256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=620.0Y=-236.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2128.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=684.0Y=-216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1100.0Y=-220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2084.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2044.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2156.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1004.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=764.0Y=-192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=788.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1872.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1112.0Y=-200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1296.0Y=-204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.0Y=-188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1220.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1528.0Y=-172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=876.0Y=-164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1500.0Y=-164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=904.0Y=-160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=232.0Y=-152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1140.0Y=-152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1520.0Y=-164.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=376.0Y=-152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1440.0Y=-148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1412.0Y=-148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=88.0Y=-140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2052.0Y=-136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=484.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-100.0Y=-128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1484.0Y=-132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1292.0Y=-120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=-112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2324.0Y=-208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1420.0Y=-108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1608.0Y=-100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1972.0Y=-92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=848.0Y=-104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-192.0Y=-88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=-84.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1188.0Y=-100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2228.0Y=-88.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=-76.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1684.0Y=-112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1804.0Y=-60.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.0Y=-28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2028.0Y=-16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2288.0Y=-4.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1692.0Y=-8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1932.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1976.0Y=-12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=12.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2168.0Y=16.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-196.0Y=28.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1680.0Y=8.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1392.0Y=36.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1448.0Y=40.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1328.0Y=48.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1176.0Y=64.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1268.0Y=60.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2244.0Y=68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=140.0Y=20.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=576.0Y=68.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2024.0Y=92.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1528.0Y=24.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1732.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2096.0Y=96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2420.0Y=96.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1284.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1656.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1928.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1460.0Y=108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1416.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1400.0Y=100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-136.0Y=116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-40.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1732.0Y=132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-320.0Y=116.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2300.0Y=104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=360.0Y=72.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1308.0Y=140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1360.0Y=148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2020.0Y=160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1584.0Y=128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2188.0Y=160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1928.0Y=160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=772.0Y=128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=228.0Y=180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2096.0Y=176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1000.0Y=172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=196.0Y=184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1644.0Y=172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1480.0Y=188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1308.0Y=184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=668.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1504.0Y=156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1352.0Y=188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1532.0Y=200.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=216.0Y=196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1408.0Y=192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1976.0Y=204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1820.0Y=168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=232.0Y=212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2152.0Y=216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=200.0Y=216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-380.0Y=188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1068.0Y=208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1324.0Y=196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2240.0Y=224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1524.0Y=232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1412.0Y=232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2208.0Y=240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2128.0Y=248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1584.0Y=248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=956.0Y=244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1324.0Y=252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2092.0Y=256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2016.0Y=240.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1488.0Y=248.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1700.0Y=252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2048.0Y=260.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=148.0Y=208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1652.0Y=180.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2184.0Y=276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1572.0Y=256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=728.0Y=252.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-216.0Y=276.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-320.0Y=256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1424.0Y=280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1380.0Y=288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2244.0Y=284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1768.0Y=296.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1660.0Y=300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1856.0Y=300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2140.0Y=300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2060.0Y=308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1824.0Y=320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2236.0Y=324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1220.0Y=280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1556.0Y=316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1960.0Y=332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2180.0Y=348.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1440.0Y=328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2152.0Y=360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-404.0Y=324.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1744.0Y=328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1984.0Y=344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1856.0Y=360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1724.0Y=368.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=668.0Y=348.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2100.0Y=376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2076.0Y=376.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2152.0Y=392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1364.0Y=360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2232.0Y=404.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1096.0Y=396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1676.0Y=384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1600.0Y=360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1524.0Y=392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=848.0Y=420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2304.0Y=416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1432.0Y=396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=540.0Y=416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2144.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1344.0Y=432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1096.0Y=436.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-120.0Y=356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=988.0Y=424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1704.0Y=460.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1844.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1452.0Y=464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1228.0Y=472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1080.0Y=476.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1452.0Y=496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2424.0Y=492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=620.0Y=484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2524.0Y=492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-400.0Y=440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=728.0Y=512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2220.0Y=504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=500.0Y=508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1540.0Y=496.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2356.0Y=512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2168.0Y=508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1640.0Y=524.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-668.0Y=512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1460.0Y=528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1852.0Y=532.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2444.0Y=536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2076.0Y=488.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-516.0Y=536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1908.0Y=548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1372.0Y=516.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=992.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1448.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2316.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2376.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1472.0Y=564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1040.0Y=556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1816.0Y=568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1776.0Y=572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=596.0Y=560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=688.0Y=576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=36.0Y=576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2120.0Y=544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1400.0Y=584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1436.0Y=580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1740.0Y=544.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=984.0Y=592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=8.0Y=592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2560.0Y=592.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1232.0Y=584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-440.0Y=576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-88.0Y=572.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1664.0Y=600.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1288.0Y=604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=940.0Y=620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1344.0Y=620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=464.0Y=608.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1576.0Y=588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2112.0Y=632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1500.0Y=632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1416.0Y=640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1860.0Y=644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2572.0Y=652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-136.0Y=664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1628.0Y=672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=68.0Y=660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=548.0Y=668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1776.0Y=696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1704.0Y=676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=164.0Y=708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1640.0Y=724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1396.0Y=716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1584.0Y=748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=400.0Y=736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1888.0Y=740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=628.0Y=756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2464.0Y=764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1792.0Y=760.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1756.0Y=776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1108.0Y=764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1688.0Y=772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=656.0Y=776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1644.0Y=784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2408.0Y=764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1824.0Y=772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1664.0Y=784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=516.0Y=768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-400.0Y=768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1384.0Y=768.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2356.0Y=772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1888.0Y=784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=252.0Y=780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=84.0Y=796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=712.0Y=812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-600.0Y=760.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1400.0Y=812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1916.0Y=820.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2148.0Y=792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1768.0Y=816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1448.0Y=832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1708.0Y=836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-272.0Y=780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1620.0Y=848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1652.0Y=836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=608.0Y=840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=868.0Y=856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-120.0Y=836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=344.0Y=848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1320.0Y=812.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-608.0Y=852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1384.0Y=856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-56.0Y=876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-436.0Y=888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1728.0Y=900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1832.0Y=884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1772.0Y=892.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1748.0Y=904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=452.0Y=904.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2512.0Y=912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1516.0Y=788.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=920.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2408.0Y=936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=656.0Y=936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=332.0Y=936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1672.0Y=948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1764.0Y=956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1744.0Y=968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1552.0Y=984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=64.0Y=968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1076.0Y=992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=736.0Y=988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1044.0Y=996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1356.0Y=1000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1348.0Y=952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1484.0Y=960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1736.0Y=1000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=1024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1888.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=1012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1840.0Y=1028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=780.0Y=1000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=256.0Y=1020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2532.0Y=1032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1752.0Y=1044.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1464.0Y=1036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1240.0Y=1056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=144.0Y=1020.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1788.0Y=1064.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1748.0Y=1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1136.0Y=1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1376.0Y=1068.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=1076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1276.0Y=1096.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1024.0Y=1104.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=916.0Y=1112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1472.0Y=1092.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1660.0Y=1076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1540.0Y=1080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1588.0Y=1120.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1784.0Y=1132.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1364.0Y=1136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1688.0Y=1144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1580.0Y=1156.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1548.0Y=1160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1508.0Y=1184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2012.0Y=1188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1652.0Y=1172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1452.0Y=1184.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1592.0Y=1196.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2344.0Y=1220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1984.0Y=1228.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2284.0Y=1244.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2052.0Y=1256.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1184.0Y=1208.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-992.0Y=1280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2000.0Y=1304.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1940.0Y=1328.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2484.0Y=1344.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1004.0Y=1372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1164.0Y=1396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2424.0Y=1400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-772.0Y=1388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-960.0Y=1416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2196.0Y=1444.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2012.0Y=1440.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2344.0Y=1452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2132.0Y=1464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-748.0Y=1472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2252.0Y=1484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-860.0Y=1468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1744.0Y=1484.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1140.0Y=1480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-540.0Y=1504.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=1508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-572.0Y=1548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2000.0Y=1548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1948.0Y=1552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-492.0Y=1536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2060.0Y=1512.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2412.0Y=1564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1840.0Y=1564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2208.0Y=1568.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2072.0Y=1584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1688.0Y=1584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1304.0Y=1580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1756.0Y=1560.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2224.0Y=1596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1540.0Y=1596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2144.0Y=1584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1032.0Y=1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-456.0Y=1620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-836.0Y=1616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1016.0Y=1632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1636.0Y=1640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1216.0Y=1632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-332.0Y=1640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2248.0Y=1652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2044.0Y=1644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2128.0Y=1656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2164.0Y=1648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-224.0Y=1660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-580.0Y=1664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1420.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1604.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-160.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2016.0Y=1684.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-520.0Y=1680.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2100.0Y=1688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1744.0Y=1692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1316.0Y=1668.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1992.0Y=1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-244.0Y=1704.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1504.0Y=1716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2128.0Y=1720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1896.0Y=1724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1644.0Y=1740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2052.0Y=1740.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1260.0Y=1736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.0Y=1728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1444.0Y=1728.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1148.0Y=1748.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2004.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=36.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1796.0Y=1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=140.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=88.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1956.0Y=1716.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2216.0Y=1776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1688.0Y=1784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=592.0Y=1780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=960.0Y=1772.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=1792.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-604.0Y=1744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2116.0Y=1764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-440.0Y=1756.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1612.0Y=1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2020.0Y=1808.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1920.0Y=1828.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2492.0Y=1832.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=896.0Y=1840.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-260.0Y=1804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=744.0Y=1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=588.0Y=1844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1112.0Y=1848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=304.0Y=1856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=100.0Y=1852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2332.0Y=1856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2096.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=228.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2280.0Y=1868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=472.0Y=1872.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1080.0Y=1884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1536.0Y=1884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2048.0Y=1880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1660.0Y=1864.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2216.0Y=1880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-944.0Y=1868.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1996.0Y=1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-276.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=388.0Y=1888.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1188.0Y=1876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2108.0Y=1908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1540.0Y=1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=612.0Y=1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-768.0Y=1896.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1912.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1868.0Y=1936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=472.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2048.0Y=1928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1528.0Y=1940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-556.0Y=1916.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1836.0Y=1944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1500.0Y=1964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1132.0Y=1960.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=536.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-648.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-504.0Y=1976.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-696.0Y=1980.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2036.0Y=1988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=612.0Y=1984.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1340.0Y=1992.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1968.0Y=2000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1860.0Y=2000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1680.0Y=2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1208.0Y=2000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1092.0Y=2008.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1732.0Y=2000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1596.0Y=2016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=760.0Y=1996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1648.0Y=2032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-672.0Y=2024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1468.0Y=2028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1500.0Y=2024.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=972.0Y=1972.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-516.0Y=2032.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1264.0Y=2040.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-140.0Y=2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1316.0Y=2048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1740.0Y=2016.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-840.0Y=2072.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=652.0Y=2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1320.0Y=2028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-992.0Y=2076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2240.0Y=2076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=868.0Y=2100.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-120.0Y=2108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-596.0Y=2108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-660.0Y=2112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1408.0Y=2080.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1788.0Y=2088.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1108.0Y=2108.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=96.0Y=2124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=20.0Y=2140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1016.0Y=2128.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=776.0Y=2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-432.0Y=2152.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-988.0Y=2148.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1272.0Y=2124.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-572.0Y=2160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1048.0Y=2144.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1188.0Y=2168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1032.0Y=2168.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=116.0Y=2188.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2232.0Y=2176.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=2192.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1492.0Y=2172.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-644.0Y=2216.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-604.0Y=2220.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1480.0Y=2224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=740.0Y=2204.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2248.0Y=2224.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1008.0Y=2212.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=804.0Y=2232.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-608.0Y=2284.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=936.0Y=2280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.0Y=2272.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-184.0Y=2292.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2148.0Y=2288.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-552.0Y=2280.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=856.0Y=2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-144.0Y=2308.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1308.0Y=2312.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1340.0Y=2264.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=32.0Y=2316.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-108.0Y=2320.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.0Y=2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-432.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1748.0Y=2300.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1292.0Y=2340.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-2088.0Y=2332.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=892.0Y=2348.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=20.0Y=2360.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-444.0Y=2356.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=2372.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=2380.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1124.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1276.0Y=2384.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=788.0Y=2392.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-84.0Y=2400.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1260.0Y=2408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1080.0Y=2408.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=316.0Y=2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-148.0Y=2388.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1520.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1404.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=856.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1024.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=64.0Y=2416.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1012.0Y=2412.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1816.0Y=2396.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-748.0Y=2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=808.0Y=2424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-836.0Y=2420.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=112.0Y=2428.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1012.0Y=2432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=696.0Y=2352.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=880.0Y=2432.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=540.0Y=2424.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1496.0Y=2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=896.0Y=2452.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=296.0Y=2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1048.0Y=2448.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=852.0Y=2464.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1596.0Y=2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1424.0Y=2472.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=800.0Y=2468.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-260.0Y=2456.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=868.0Y=2480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=924.0Y=2480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=940.0Y=2492.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.0Y=2480.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-976.0Y=2508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1652.0Y=2508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=968.0Y=2520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=912.0Y=2520.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=2508.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1692.0Y=2528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=988.0Y=2536.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=940.0Y=2548.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=20.0Y=2528.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1632.0Y=2552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=956.0Y=2564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=532.0Y=2564.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.0Y=2552.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1920.0Y=2556.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1616.0Y=2584.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1488.0Y=2580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1352.0Y=2588.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1840.0Y=2576.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=500.0Y=2596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1104.0Y=2596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1896.0Y=2596.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=32.0Y=2616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1860.0Y=2604.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1588.0Y=2620.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1952.0Y=2616.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=148.0Y=2580.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=812.0Y=2624.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1168.0Y=2628.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-908.0Y=2640.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2016.0Y=2632.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=16.0Y=2644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1136.0Y=2644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.0Y=2644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.0Y=2644.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1784.0Y=2636.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1332.0Y=2656.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1264.0Y=2652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=756.0Y=2652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-436.0Y=2660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1464.0Y=2664.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1196.0Y=2660.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=784.0Y=2676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=880.0Y=2676.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1844.0Y=2672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=552.0Y=2652.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1164.0Y=2688.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1444.0Y=2696.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=728.0Y=2692.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-896.0Y=2700.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-948.0Y=2672.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-396.0Y=2712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=848.0Y=2712.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=756.0Y=2720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1108.0Y=2720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1708.0Y=2708.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1424.0Y=2724.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-744.0Y=2648.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1004.0Y=2736.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=584.0Y=2720.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1844.0Y=2744.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=948.0Y=2752.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=840.0Y=2764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=700.0Y=2764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1596.0Y=2764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1760.0Y=2764.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=444.0Y=2776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=928.0Y=2780.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1632.0Y=2784.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=468.0Y=2796.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-856.0Y=2776.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=412.0Y=2816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1352.0Y=2804.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=904.0Y=2824.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-996.0Y=2816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1556.0Y=2816.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1000.0Y=2836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=436.0Y=2836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1756.0Y=2836.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=512.0Y=2844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1080.0Y=2844.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=980.0Y=2864.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1308.0Y=2852.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1636.0Y=2848.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1200.0Y=2856.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1324.0Y=2876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2056.0Y=2880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-912.0Y=2876.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=476.0Y=2880.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1876.0Y=2884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=384.0Y=2884.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2112.0Y=2900.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1392.0Y=2908.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1300.0Y=2912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1856.0Y=2912.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1696.0Y=2920.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=2928.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=832.0Y=2936.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=536.0Y=2932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-784.0Y=2932.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1280.0Y=2944.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1576.0Y=2940.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1336.0Y=2952.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1676.0Y=2964.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-572.0Y=2956.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=512.0Y=2968.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1036.0Y=2948.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=884.0Y=2996.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=840.0Y=3000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1728.0Y=3000.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-764.0Y=3004.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=2988.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=3012.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-816.0Y=3028.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1920.0Y=3036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.0Y=3048.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1968.0Y=3052.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-988.0Y=3056.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2020.0Y=3076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1428.0Y=3076.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=660.0Y=3036.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1628.0Y=3084.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=772.0Y=3096.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1472.0Y=3112.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1604.0Y=3136.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1784.0Y=3140.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1864.0Y=3160.0Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1384.0Y=3176.0Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_game_gtav.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-300.0Y=-2780.0Z=80.62907409667969.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-368.0Y=-2716.0Z=90.03410339355469.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-956.0Y=-928.0Z=62.66725540161133.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-272.0Y=-544.0Z=68.18321228027344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-368.0Y=-464.0Z=65.03630065917969.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-332.0Y=-400.0Z=72.12525939941406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-428.0Y=-396.0Z=66.64181518554688.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-320.0Y=-388.0Z=59.769615173339844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-712.0Y=-376.0Z=85.13311004638672.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-784.0Y=-344.0Z=67.9697265625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1020.0Y=-356.0Z=60.881797790527344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1416.0Y=-348.0Z=91.01664733886719.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1088.0Y=-320.0Z=62.38178634643555.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-588.0Y=-324.0Z=90.82169342041016.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-888.0Y=-320.0Z=60.30592346191406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-300.0Y=-304.0Z=68.4848403930664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1020.0Y=-296.0Z=86.50218963623047.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1116.0Y=-276.0Z=88.93126678466797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-728.0Y=-280.0Z=72.28339385986328.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1020.0Y=-260.0Z=62.43511962890625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-820.0Y=-260.0Z=78.57041931152344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-432.0Y=-252.0Z=76.88024139404297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-296.0Y=-248.0Z=68.25225067138672.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-412.0Y=-208.0Z=77.19280242919922.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1064.0Y=-180.0Z=78.37234497070312.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1044.0Y=-120.0Z=59.61659240722656.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-280.0Y=-120.0Z=58.0415153503418.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-368.0Y=-96.0Z=73.31693267822266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-240.0Y=-152.0Z=67.62374877929688.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1108.0Y=-96.0Z=69.78602600097656.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-844.0Y=-108.0Z=81.36266326904297.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-264.0Y=-68.0Z=55.338314056396484.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-344.0Y=-72.0Z=69.14248657226562.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1024.0Y=-56.0Z=80.76786804199219.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-920.0Y=-64.0Z=92.42252349853516.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-392.0Y=-40.0Z=73.0837173461914.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-240.0Y=-4.0Z=58.882503509521484.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1004.0Y=-8.0Z=98.56221008300781.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-808.0Y=40.0Z=65.57872772216797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-628.0Y=44.0Z=89.06620788574219.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-312.0Y=60.0Z=63.87718963623047.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-832.0Y=128.0Z=63.87102508544922.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-644.0Y=128.0Z=180.78477478027344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-536.0Y=156.0Z=96.39971160888672.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-596.0Y=148.0Z=210.67254638671875.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-448.0Y=288.0Z=86.4076919555664.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-816.0Y=304.0Z=94.83013153076172.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-148.0Y=328.0Z=62.844566345214844.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1056.0Y=320.0Z=78.26860046386719.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-616.0Y=348.0Z=66.82485961914062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-52.0Y=352.0Z=95.59820556640625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-348.0Y=400.0Z=76.05476379394531.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-152.0Y=416.0Z=64.99881744384766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-340.0Y=448.0Z=93.99378204345703.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-704.0Y=472.0Z=83.83609008789062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=440.0Y=496.0Z=107.62864685058594.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-432.0Y=488.0Z=56.5178337097168.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-332.0Y=504.0Z=92.45132446289062.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-604.0Y=560.0Z=60.84794235229492.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-420.0Y=576.0Z=85.97093200683594.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-92.0Y=612.0Z=58.404842376708984.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-616.0Y=680.0Z=70.58941650390625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-412.0Y=696.0Z=68.52732849121094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-612.0Y=716.0Z=70.85274505615234.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=56.0Y=704.0Z=81.14710235595703.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-772.0Y=776.0Z=86.73384094238281.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-376.0Y=780.0Z=68.3984375.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-224.0Y=792.0Z=62.55434036254883.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-612.0Y=812.0Z=100.57167053222656.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-584.0Y=840.0Z=101.08759307861328.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-240.0Y=852.0Z=90.6933822631836.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-352.0Y=856.0Z=84.69583129882812.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=576.0Y=868.0Z=103.53917694091797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=40.0Y=892.0Z=62.083213806152344.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-220.0Y=908.0Z=89.20973205566406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=560.0Y=920.0Z=115.2804183959961.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-96.0Y=1004.0Z=66.70365905761719.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-492.0Y=992.0Z=61.640525817871094.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-428.0Y=1020.0Z=82.9317626953125.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-760.0Y=1016.0Z=80.2428970336914.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=84.0Y=1056.0Z=72.50550079345703.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-16.0Y=1068.0Z=73.9239273071289.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-376.0Y=1164.0Z=59.22187805175781.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=4.0Y=1196.0Z=67.68890380859375.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.0Y=1208.0Z=71.01188659667969.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-196.0Y=1196.0Z=85.39334869384766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-364.0Y=1228.0Z=58.386940002441406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-160.0Y=1268.0Z=60.52482604980469.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-24.0Y=1292.0Z=70.9585952758789.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-224.0Y=1288.0Z=61.20265579223633.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-176.0Y=1348.0Z=65.64120483398438.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-480.0Y=1384.0Z=90.60686492919922.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-316.0Y=1388.0Z=66.61214447021484.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-120.0Y=1424.0Z=67.00953674316406.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-240.0Y=1444.0Z=63.37196731567383.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-540.0Y=1460.0Z=92.34598541259766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-148.0Y=1476.0Z=62.798004150390625.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-200.0Y=1472.0Z=62.57396697998047.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.0Y=1460.0Z=70.95384979248047.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-220.0Y=1492.0Z=60.75043487548828.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-104.0Y=1504.0Z=72.06941223144531.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-180.0Y=1508.0Z=65.08438110351562.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-340.0Y=1524.0Z=57.84837341308594.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-604.0Y=1524.0Z=72.6837387084961.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-144.0Y=1564.0Z=75.7570571899414.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-396.0Y=1568.0Z=61.82192611694336.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-432.0Y=1596.0Z=57.799564361572266.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-188.0Y=1612.0Z=73.23741912841797.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-516.0Y=1648.0Z=79.30615997314453.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-252.0Y=1688.0Z=69.05741119384766.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-284.0Y=1688.0Z=67.14071655273438.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-408.0Y=1780.0Z=57.30485153198242.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-356.0Y=1848.0Z=93.71517944335938.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-240.0Y=-748.0Z=65.14860534667969.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_gs_ecust.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=171.15267944335938Y=-47.95754623413086Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=186.88421630859375Y=-35.16781234741211Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=171.03070068359375Y=-35.24241256713867Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=172.51925659179688Y=-5.255241394042969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=153.096435546875Y=13.99801254272461Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=139.2166748046875Y=15.745677947998047Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=144.021484375Y=28.355056762695312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=140.63583374023438Y=45.29444122314453Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=121.22898864746094Y=76.0051040649414Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=115.00210571289062Y=85.64190673828125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=138.55429077148438Y=-26.056922912597656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=121.13159942626953Y=-26.107839584350586Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=137.55712890625Y=-8.933860778808594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=119.46577453613281Y=-7.530708312988281Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=82.92781066894531Y=-28.262161254882812Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=86.9166259765625Y=-7.321907043457031Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=120.43134307861328Y=12.764591217041016Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=108.02767944335938Y=12.48095703125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=121.85403442382812Y=21.75229263305664Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=107.51216125488281Y=22.000167846679688Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=122.27291870117188Y=30.786529541015625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=108.51927947998047Y=30.49188995361328Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=121.99946594238281Y=39.705657958984375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=109.31904602050781Y=39.80290222167969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=122.47590637207031Y=48.39372253417969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=108.26100158691406Y=48.733863830566406Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=122.31758117675781Y=56.62052917480469Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=109.61428833007812Y=57.28313446044922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=91.85169219970703Y=13.917987823486328Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=90.43458557128906Y=48.9715576171875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=90.43790435791016Y=57.50982666015625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=76.01480102539062Y=56.53468322753906Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=60.87005615234375Y=9.833976745605469Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=56.89203643798828Y=16.64305877685547Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=52.210655212402344Y=24.686996459960938Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.01341247558594Y=31.530776977539062Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=63.16606140136719Y=39.243927001953125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=63.08150100708008Y=29.482650756835938Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=27.08023452758789Y=-5.602519989013672Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=55.43901824951172Y=-10.990345001220703Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=39.44340515136719Y=-20.480051040649414Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=13.413280487060547Y=-50.065391540527344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=17.14840316772461Y=-58.40896224975586Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=25.195880889892578Y=-74.28863525390625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=33.34663391113281Y=-91.64524841308594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=48.789554595947266Y=-79.25923156738281Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=59.16109848022461Y=-82.58625793457031Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=63.5150146484375Y=-101.92595672607422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=64.04983520507812Y=-91.44949340820312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=78.42264556884766Y=-41.81779098510742Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=100.04588317871094Y=-40.07239532470703Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=128.88796997070312Y=-48.79493713378906Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=116.17826843261719Y=-94.30815124511719Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-16.615684509277344Y=-7.673946380615234Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-25.991127014160156Y=5.494781494140625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-38.393714904785156Y=-21.75360870361328Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=64.31182861328125Y=-40.82001876831055Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=15.811264038085938Y=33.93170166015625Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_gs_nwpu01.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-65.51205444335938Y=43.38063430786133Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.220958709716797Y=0.33190539479255676Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-11.356328010559082Y=28.864274978637695Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-57.194740295410156Y=53.24066162109375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=32.831085205078125Y=7.845455169677734Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-48.29462432861328Y=44.97136688232422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=27.782154083251953Y=11.027813911437988Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-0.7311501502990723Y=40.40327453613281Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-11.746687889099121Y=33.862552642822266Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-10.23727035522461Y=37.892696380615234Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-17.42425537109375Y=46.00354766845703Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-13.871870040893555Y=19.613473892211914Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.910022735595703Y=22.530593872070312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-33.59761047363281Y=28.691410064697266Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-29.06082534790039Y=27.996902465820312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-35.51143264770508Y=41.685333251953125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-45.67927551269531Y=46.233482360839844Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-57.56650161743164Y=39.476009368896484Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-61.41991424560547Y=24.013778686523438Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-49.98839569091797Y=20.046276092529297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-36.7265625Y=15.354761123657227Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-41.84900665283203Y=18.86812973022461Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-57.50001525878906Y=39.61296463012695Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-33.79204559326172Y=28.522247314453125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-29.187686920166016Y=28.094039916992188Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-28.984310150146484Y=32.5101203918457Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-45.12855529785156Y=46.03337860107422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-34.19896697998047Y=41.56903839111328Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-39.62102508544922Y=57.22455596923828Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-28.80640411376953Y=52.16668701171875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-54.71295928955078Y=60.9954948425293Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-9.953348159790039Y=37.96659469604492Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-17.283084869384766Y=45.94279479980469Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-13.859878540039062Y=19.930606842041016Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-19.557363510131836Y=22.120912551879883Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-15.0919771194458Y=24.82758903503418Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-18.333127975463867Y=17.370344161987305Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-27.99598503112793Y=11.917678833007812Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-19.756929397583008Y=8.3367280960083Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-0.7601451873779297Y=35.75843048095703Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3.7479820251464844Y=30.693572998046875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=15.328432083129883Y=28.19383430480957Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=11.237058639526367Y=24.538105010986328Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=18.9663028717041Y=20.60673713684082Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=25.86594581604004Y=22.827407836914062Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.933462142944336Y=15.582239151000977Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=37.38884735107422Y=19.892684936523438Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=35.71344757080078Y=15.544394493103027Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=19.827573776245117Y=12.224448204040527Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=9.293035507202148Y=14.80109977722168Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5.803466796875Y=7.594822883605957Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3.078306198120117Y=13.498235702514648Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=29.03775978088379Y=-0.14403915405273438Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=26.962419509887695Y=-5.247998237609863Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=13.63852310180664Y=-5.963479042053223Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=20.363683700561523Y=-9.243943214416504Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=26.866567611694336Y=-12.21329402923584Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=17.5143985748291Y=-14.154519081115723Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=9.414745330810547Y=-16.99761962890625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=15.452230453491211Y=-20.050697326660156Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1.258981704711914Y=-9.641499519348145Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3.1245975494384766Y=-1.036301612854004Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1.5449981689453125Y=-1.580735206604004Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=44.062015533447266Y=55.31599807739258Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=29.62140655517578Y=56.80186462402344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=21.69831085205078Y=43.01408386230469Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=13.45097827911377Y=50.03061294555664Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=9.950575828552246Y=45.760528564453125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=21.895753860473633Y=53.00971603393555Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=16.834741592407227Y=66.86382293701172Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5.712820053100586Y=62.3313102722168Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-4.412660598754883Y=55.649723052978516Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-20.349515914916992Y=62.576377868652344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-9.562081336975098Y=65.6812744140625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-0.7896394729614258Y=62.011497497558594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=2.6238553524017334Y=71.18132781982422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5.491724967956543Y=12.306190490722656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-16.824970245361328Y=21.004732131958008Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_gs_nwpu02.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-50.33761978149414Y=8.783727645874023Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-65.20237731933594Y=-17.792673110961914Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-49.43672561645508Y=-12.162076950073242Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-25.916934967041016Y=-23.67620849609375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-39.46123123168945Y=-30.87920379638672Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-53.927127838134766Y=-36.57300567626953Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-29.579586029052734Y=-45.52190399169922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-23.86374282836914Y=-56.00126647949219Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-39.64644241333008Y=-84.86373901367188Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-25.080493927001953Y=-77.1650390625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-7.193607330322266Y=-69.93753814697266Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-0.6153335571289062Y=-91.16864013671875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-25.14275360107422Y=-125.59614562988281Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-10.24267578125Y=-119.15709686279297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=5.783290863037109Y=-114.10913848876953Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-25.325576782226562Y=-141.89431762695312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-5.3783721923828125Y=-140.4781036376953Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=14.598480224609375Y=-134.8733673095703Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=24.67913818359375Y=-106.33210754394531Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=17.547828674316406Y=-78.82162475585938Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3.325145721435547Y=-14.186546325683594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=13.72662353515625Y=-11.016021728515625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=6.0871429443359375Y=0.30542755126953125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=6.204059600830078Y=0.9752578735351562Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-11.393901824951172Y=22.95291519165039Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=11.551624298095703Y=29.860637664794922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=21.123310089111328Y=34.1486930847168Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=31.499187469482422Y=-50.200626373291016Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=46.823768615722656Y=-39.304412841796875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=55.192134857177734Y=-66.10844421386719Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=71.53291320800781Y=-54.34027862548828Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=55.016868591308594Y=-98.61463165283203Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=79.73192596435547Y=-82.02250671386719Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=86.19947814941406Y=-97.18405151367188Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=46.134735107421875Y=-123.66243743896484Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=85.04542541503906Y=-112.28921508789062Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=55.14136505126953Y=-15.718193054199219Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=72.8749008178711Y=-18.227148056030273Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=62.3239631652832Y=-26.683147430419922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=30.350494384765625Y=14.692558288574219Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=21.79428482055664Y=11.694007873535156Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=47.23060989379883Y=37.72686004638672Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=90.16976928710938Y=-58.028533935546875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=81.01748657226562Y=-49.13365936279297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=94.24073028564453Y=-44.899295806884766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=61.66378402709961Y=48.59569549560547Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=82.32347869873047Y=41.69506072998047Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=96.7368392944336Y=54.580284118652344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=121.98231506347656Y=64.97611236572266Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=118.66402435302734Y=8.457117080688477Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=129.88966369628906Y=-13.4251708984375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=128.36068725585938Y=-2.112682342529297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=154.17376708984375Y=18.634775161743164Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=152.51449584960938Y=-3.684560775756836Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=141.246337890625Y=-3.635030746459961Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=98.50050354003906Y=-71.00563049316406Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=96.1689453125Y=-76.47732543945312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=149.5799560546875Y=-87.69927978515625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=161.85992431640625Y=-80.79727935791016Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=169.72531127929688Y=-75.26329040527344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=180.04689025878906Y=-78.4963150024414Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=188.68951416015625Y=-73.42864227294922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=169.16525268554688Y=-53.99848175048828Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=178.4918975830078Y=-52.72364044189453Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=191.072998046875Y=-49.06981658935547Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=200.2700958251953Y=-46.77033233642578Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=209.37521362304688Y=-43.581790924072266Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=220.5956573486328Y=-40.515838623046875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=247.35946655273438Y=-45.42041015625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=266.8572692871094Y=-38.460819244384766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=258.70257568359375Y=-44.408477783203125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=192.94146728515625Y=26.83736801147461Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=203.5487518310547Y=31.313533782958984Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=268.236328125Y=-14.79347038269043Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=277.4028625488281Y=-16.540729522705078Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=279.08941650390625Y=-36.3145637512207Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=294.693603515625Y=-21.077621459960938Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=302.81768798828125Y=-18.655534744262695Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=311.12579345703125Y=-19.053897857666016Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=295.2144470214844Y=-5.442454814910889Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=286.9368591308594Y=2.129685401916504Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=281.23468017578125Y=-0.07775115966796875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=274.1828918457031Y=29.16903305053711Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=278.16864013671875Y=14.790905952453613Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=246.530029296875Y=33.98161315917969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=256.00299072265625Y=38.304473876953125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=241.5019989013672Y=46.796226501464844Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=50.14883041381836Y=27.710594177246094Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_gs_sjtu01.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-192.14239501953125Y=55.65081787109375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-180.7803955078125Y=66.179443359375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-185.05044555664062Y=51.17756652832031Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-173.90646362304688Y=62.0705680847168Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-180.55511474609375Y=44.23710250854492Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-123.46284484863281Y=50.93632888793945Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-126.6915283203125Y=47.63045883178711Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-130.4922332763672Y=43.97993087768555Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-129.45220947265625Y=43.941532135009766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-126.33212280273438Y=43.8263053894043Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-129.49061584472656Y=42.901493072509766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-132.70147705078125Y=41.687007904052734Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-79.61150360107422Y=6.066824913024902Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-151.48548889160156Y=2.3056700229644775Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.01606750488281Y=-29.94277572631836Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-101.0887451171875Y=37.02033615112305Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-100.71328735351562Y=-57.723846435546875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-28.87006950378418Y=-11.928624153137207Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-44.7042236328125Y=-1.8875162601470947Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1.6098711490631104Y=-46.4699821472168Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-66.04312896728516Y=-52.922080993652344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.875492095947266Y=-55.71907043457031Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-117.99078369140625Y=89.08934783935547Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-148.06512451171875Y=52.468589782714844Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-163.14068603515625Y=77.08829498291016Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-184.8666534423828Y=62.71455383300781Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-61.8245735168457Y=103.67472839355469Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-42.02830123901367Y=122.395263671875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=41.142974853515625Y=127.4226303100586Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=56.73497009277344Y=123.41303253173828Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=6.760828018188477Y=29.878232955932617Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=103.38710021972656Y=76.16222381591797Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=81.15304565429688Y=103.94807434082031Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=107.66726684570312Y=45.159912109375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=63.650177001953125Y=6.470640182495117Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=66.71916961669922Y=-14.845619201660156Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=111.99092102050781Y=29.137847900390625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=88.7829360961914Y=3.5494346618652344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=10.459774017333984Y=-1.6152153015136719Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1.094259262084961Y=127.59598541259766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-145.00682067871094Y=14.954399108886719Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-143.88880920410156Y=33.08415222167969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-134.10897827148438Y=24.163204193115234Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-141.84930419921875Y=9.154563903808594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-125.96823120117188Y=19.834598541259766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-134.9712371826172Y=4.257087707519531Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-154.03021240234375Y=2.348377227783203Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-120.98931121826172Y=14.826683044433594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-116.22325897216797Y=8.8917236328125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-123.1201400756836Y=2.316608428955078Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-59.77629852294922Y=-24.60295867919922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-72.8965835571289Y=-32.950111389160156Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-94.20482635498047Y=-40.744422912597656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-82.19977569580078Y=-29.603797912597656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.36402893066406Y=-69.57425689697266Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-58.971717834472656Y=-74.4819107055664Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-49.930747985839844Y=-85.77061462402344Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-39.669342041015625Y=-76.10338592529297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-32.29212951660156Y=-86.30741119384766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=80.08068084716797Y=104.03815460205078Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=42.383262634277344Y=127.7234115600586Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=37.21028137207031Y=143.27685546875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.5615005493164Y=136.9772186279297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-21.061748504638672Y=69.87770080566406Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-73.63114929199219Y=88.46771240234375Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_gs_sjtu02.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-133.96804809570312Y=166.81712341308594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-155.8099822998047Y=160.93943786621094Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-163.6942138671875Y=144.8083038330078Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-172.3870391845703Y=127.37681579589844Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-181.58328247070312Y=109.22405242919922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-182.20675659179688Y=71.30361938476562Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-167.12689208984375Y=116.30813598632812Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-172.65611267089844Y=106.98656463623047Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-159.01571655273438Y=113.16468048095703Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-164.09149169921875Y=104.11824798583984Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-151.75164794921875Y=110.36482238769531Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-156.44244384765625Y=100.29296875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-143.12393188476562Y=106.64070892333984Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-148.52694702148438Y=95.6075439453125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-157.05889892578125Y=128.58438110351562Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-150.45596313476562Y=125.31037902832031Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-142.99288940429688Y=122.3028335571289Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-136.42369079589844Y=119.35506439208984Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-154.25267028808594Y=139.9578094482422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-143.14772033691406Y=134.10569763183594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-135.21153259277344Y=128.4840087890625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-146.4852752685547Y=158.14642333984375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-137.73068237304688Y=156.40798950195312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-130.1514892578125Y=151.6951141357422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-122.165283203125Y=147.8785858154297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-127.21812438964844Y=138.9103240966797Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-103.06001281738281Y=153.75408935546875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-87.2520751953125Y=145.68495178222656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-74.74748229980469Y=137.91908264160156Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-69.50354766845703Y=136.4624786376953Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-70.0974349975586Y=143.76712036132812Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-106.34906005859375Y=140.1971435546875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-111.64169311523438Y=130.89785766601562Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-100.49826049804688Y=135.6763458251953Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-105.12329864501953Y=126.15592956542969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-93.15204620361328Y=133.3732452392578Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-96.65686798095703Y=124.24263000488281Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-85.65306091308594Y=129.7481231689453Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-91.11014556884766Y=120.01274871826172Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-117.32330322265625Y=119.12448120117188Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-123.3490982055664Y=108.18592834472656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-127.01286315917969Y=98.8777084350586Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-114.79354095458984Y=106.81375122070312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-108.11556243896484Y=103.4906005859375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-101.00362396240234Y=101.11492919921875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-113.18348693847656Y=91.39383697509766Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-107.08737182617188Y=87.4625244140625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-123.30879974365234Y=82.22359466552734Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-145.73687744140625Y=59.99922180175781Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-134.26841735839844Y=58.20282745361328Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-127.8784408569336Y=49.86936950683594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-135.1556396484375Y=34.82025146484375Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-145.51832580566406Y=35.1191291809082Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-153.526123046875Y=43.97843933105469Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-140.10345458984375Y=46.51031494140625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-114.58096313476562Y=55.01557159423828Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-107.66870880126953Y=51.76499557495117Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-114.21208190917969Y=39.596309661865234Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-109.47125244140625Y=18.213905334472656Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-83.03108215332031Y=108.43683624267578Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-74.34388732910156Y=137.44503784179688Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-59.021385192871094Y=138.12628173828125Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-65.3186264038086Y=122.95280456542969Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-54.88840866088867Y=128.23782348632812Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-48.50136184692383Y=124.61058044433594Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-43.540733337402344Y=121.99642181396484Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-59.31375503540039Y=119.5923080444336Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-53.497535705566406Y=115.87383270263672Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-48.0821533203125Y=112.75865173339844Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-64.43460083007812Y=98.96353149414062Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-68.56771087646484Y=93.10446166992188Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-59.65610885620117Y=87.82011413574219Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-73.70463562011719Y=77.77691650390625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-70.58179473876953Y=61.75581359863281Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-83.23246765136719Y=37.25398254394531Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-89.6141357421875Y=25.39213752746582Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-91.43667602539062Y=10.278253555297852Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-18.05384635925293Y=119.58745574951172Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1.78887140750885Y=109.30828094482422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-17.899953842163086Y=97.89868927001953Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-14.233739852905273Y=64.55754852294922Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=1.614107370376587Y=58.13658905029297Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=8.36921215057373Y=40.09739303588867Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-19.313072204589844Y=39.983333587646484Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-21.84034538269043Y=-1.306158185005188Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-1.664358377456665Y=-15.576955795288086Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=14.099515914916992Y=-1.162763237953186Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=28.667699813842773Y=26.059049606323242Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=15.918729782104492Y=31.737995147705078Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=37.078365325927734Y=49.76873016357422Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=27.89318084716797Y=54.460662841796875Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=18.153413772583008Y=63.45450973510742Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=13.516819953918457Y=66.49193572998047Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=30.556615829467773Y=86.99077606201172Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=64.25566864013672Y=73.01886749267578Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=50.69089889526367Y=74.89529418945312Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=3.4359211921691895Y=84.2789306640625Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=0.8148900270462036Y=74.37368774414062Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-5.778038501739502Y=80.7439193725586Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-3.286346912384033Y=106.60750579833984Z=0.png\"}\n{\"type\": \"building\", \"color\": \"test\", \"size\": \"test\", \"shape\": \"test\", \"feature\": \"test\", \"filename\": \"X=-39.92634201049805Y=37.9413948059082Z=0.png\"}\n"
  },
  {
    "path": "scene_data/seg_map/env_ue_bigcity.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-150630.250Y=-101525.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-145220.875Y=-115355.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-146879.688Y=-296503.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-153997.094Y=-89318.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-154172.922Y=-103178.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=39723.871Y=-189796.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-145173.562Y=-195764.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-141967.828Y=-152349.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150630.297Y=-148951.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-147047.500Y=-324910.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-14760.067Y=-187781.094Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-147201.922Y=-301022.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-148646.016Y=-329244.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-133581.422Y=-249505.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-148427.688Y=-323571.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-147689.250Y=-317246.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-151492.609Y=-124759.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150630.219Y=-121375.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-152871.406Y=-41612.191Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-156147.703Y=-153696.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-149211.359Y=-21963.275Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-145035.828Y=-262014.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144754.688Y=-181592.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-146772.016Y=-55829.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-145150.797Y=-302911.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-142454.688Y=-180957.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-169282.625Y=-168366.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-141755.953Y=-185436.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-144176.109Y=-235038.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-145024.438Y=-170018.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-141406.828Y=-180834.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144720.375Y=-180699.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-144732.594Y=-177514.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-144754.672Y=-190631.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=-142861.062Y=-190207.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-140419.641Y=-185409.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=67070.633Y=-5767.402Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-142915.359Y=-115362.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-152851.797Y=-313036.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-144747.859Y=-176581.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-144754.688Y=-175783.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-143270.203Y=-253793.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-144515.359Y=-299746.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-150014.656Y=-313003.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two-tiered structure\", \"filename\": \"X=-139264.094Y=-53730.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-156147.688Y=-196096.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-142739.594Y=-207358.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-144840.016Y=-111699.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143957.562Y=-304525.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-146494.844Y=-315836.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-147817.391Y=-259150.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-148975.188Y=-322827.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-148016.922Y=-300272.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-149974.328Y=-322263.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"pointed top\", \"filename\": \"X=9224.341Y=-209386.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150762.578Y=-320372.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150743.109Y=-313767.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-145854.219Y=-329169.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-125720.836Y=-72904.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-142778.750Y=-295413.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevator on the side\", \"filename\": \"X=-140628.641Y=-51052.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-148911.609Y=-298412.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-142311.906Y=-305406.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-144735.062Y=-239879.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-154735.250Y=-317819.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-144662.812Y=-266556.250Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150552.344Y=-68586.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144217.594Y=-321668.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-143052.781Y=-245661.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-146590.984Y=-301702.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-155628.953Y=-289023.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-146420.781Y=-86360.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-146698.938Y=-329817.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-144914.750Y=-314498.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-146107.188Y=-326187.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-149666.656Y=-299183.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=121612.156Y=-133559.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-151888.438Y=-321580.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-144795.562Y=-18091.955Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-156044.328Y=-265766.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-146656.500Y=-319168.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-148027.219Y=-297702.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-144697.281Y=-186829.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-141353.859Y=-237677.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-141226.016Y=-34338.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-140531.969Y=-180744.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-140386.906Y=-319285.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=34013.984Y=-116255.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-148967.062Y=-124720.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-143570.672Y=-199308.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-138253.219Y=-93309.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-144809.125Y=-179682.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-141417.188Y=-111729.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-156147.734Y=-139043.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=95216.656Y=-197860.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=-143570.719Y=-148951.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143570.734Y=-143805.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-156147.734Y=-142388.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=-139233.516Y=-150389.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=-150630.266Y=-106250.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-149580.859Y=-327962.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-153829.891Y=-316934.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-139122.203Y=-38690.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows and decorative elements\", \"filename\": \"X=-143081.500Y=-306163.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-139641.406Y=-320101.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-148150.953Y=-83624.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-156147.688Y=-106977.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-158122.422Y=-188653.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=-156614.016Y=-112439.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-163765.156Y=-166977.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151096.594Y=-171722.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151096.656Y=-157498.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-164231.500Y=-160179.641Z=1320.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevator\", \"filename\": \"X=-142662.922Y=-322975.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-140070.891Y=-309673.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-161937.625Y=-283713.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"geometric\", \"feature\": \"modern design\", \"filename\": \"X=-144373.281Y=-125791.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-141465.594Y=-318467.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143601.469Y=-322342.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143978.422Y=-327158.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-153699.172Y=-39943.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"octagonal\", \"feature\": \"ornate\", \"filename\": \"X=-166982.219Y=-298904.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-149767.859Y=-188985.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-148094.766Y=-286444.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=-149336.406Y=-267675.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, and densely packed with windows\", \"filename\": \"X=-158721.516Y=-259882.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-154073.531Y=-323766.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-152749.156Y=-322271.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151684.562Y=-314659.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended in the air\", \"filename\": \"X=-141556.250Y=-42713.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open entrance\", \"filename\": \"X=-168019.969Y=-250684.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-162526.250Y=-186773.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-73100.000Y=-27300.000Z=20.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=83699.289Y=-308251.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-201923.797Y=-124171.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-151344.500Y=-320999.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"water tower on top\", \"filename\": \"X=-138253.219Y=-97165.344Z=394.586.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-148447.422Y=-37097.152Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-157883.125Y=-320987.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-125037.609Y=-223143.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-154679.188Y=-50421.059Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-153633.062Y=-312256.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-151157.578Y=-95108.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-167203.672Y=-304916.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=-145165.406Y=-44526.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-154615.984Y=-38144.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-152800.844Y=-325088.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-145158.984Y=-320492.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-165436.016Y=-225237.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-167496.516Y=-310610.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-159626.609Y=-275782.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-158742.156Y=-308133.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-169282.609Y=-116437.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-153071.344Y=-177937.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-163524.594Y=-43927.574Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143373.922Y=-104031.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, with many windows\", \"filename\": \"X=-151288.234Y=-253153.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-164152.094Y=-185623.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=-156967.312Y=-308838.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-157496.391Y=-314469.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed facade\", \"filename\": \"X=-162324.516Y=-46492.848Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-175514.703Y=-273025.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-156614.016Y=-116095.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-155130.547Y=-122752.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-156147.688Y=-199640.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-142739.578Y=-195776.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-156147.734Y=-133447.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"tall with many windows\", \"filename\": \"X=-169282.562Y=-151166.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-164231.484Y=-141565.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-162533.062Y=-188698.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=-150630.297Y=-134914.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-156147.703Y=-129901.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-150630.281Y=-131369.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-154711.562Y=-324321.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-145173.594Y=-207362.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-164667.266Y=-314201.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-151096.562Y=-114582.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-64495.160Y=-281928.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-156614.031Y=-170333.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-169282.594Y=-176272.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-162543.250Y=-185704.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-156614.062Y=-158970.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151096.625Y=-161055.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-152227.562Y=-307105.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-156867.500Y=-39964.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=-48004.664Y=56036.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-160155.812Y=-317727.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-172785.828Y=-87400.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-158790.125Y=-319216.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-155933.172Y=-305360.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-165624.438Y=-75248.023Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-161026.422Y=-310617.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-163001.422Y=-45154.449Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-159584.109Y=-41235.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-153461.531Y=-323051.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-163626.219Y=-68804.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-162489.578Y=-184694.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large open space\", \"filename\": \"X=-156147.688Y=-126105.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-160401.812Y=-41558.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"octagonal\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-154828.609Y=-223648.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-164627.531Y=-177611.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-167428.375Y=-48279.355Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-153771.406Y=-255971.781Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-153606.812Y=-305666.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-156177.375Y=-319229.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-154851.312Y=-310945.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-155070.672Y=-304633.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-164076.047Y=-42964.215Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-155836.438Y=-309929.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-155966.719Y=-39340.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-152707.312Y=-270981.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-162002.891Y=-204146.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-159500.375Y=-318363.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-160614.750Y=-310148.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-162143.328Y=-42586.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"symmetrical window arrangement\", \"filename\": \"X=-157848.047Y=-52035.676Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-163188.906Y=-312762.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-161756.281Y=-311240.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-158980.812Y=-313313.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-165463.672Y=-308503.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-158222.656Y=-44903.488Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-161452.828Y=-316305.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-162013.156Y=-35206.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-152371.094Y=-282168.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=117130.250Y=-105381.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-158631.391Y=-40797.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151096.578Y=-181313.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-160829.344Y=-317163.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-161832.531Y=-47356.012Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-155475.750Y=-43505.988Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-165744.297Y=-306193.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=179555.297Y=-134998.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-162489.562Y=-190716.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-162842.203Y=-314957.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-162101.984Y=-177655.953Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-163765.156Y=-181000.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-166237.469Y=-189985.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-176590.719Y=-233343.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=16155.067Y=-352616.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-164033.344Y=-313507.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-159306.172Y=-45720.441Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-179164.969Y=-91061.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-181004.438Y=-297087.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-157664.328Y=-40509.004Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=39952.516Y=-179929.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-175532.641Y=-207528.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-168109.172Y=-195953.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-157164.812Y=-103121.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-16166.330Y=-233541.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-193548.297Y=-112382.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151096.594Y=-110926.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-158122.438Y=-122759.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-169282.562Y=-132574.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-156614.031Y=-166977.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-169282.609Y=-171722.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-164821.875Y=-190195.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-162110.016Y=-315558.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-156827.906Y=-44337.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-156614.031Y=-181000.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple floors\", \"filename\": \"X=-156147.703Y=-150340.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-156725.141Y=-305892.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-165189.594Y=-185455.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-147599.250Y=-330778.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style\", \"filename\": \"X=-158277.203Y=-177655.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-169866.156Y=-85926.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-168265.438Y=-179619.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"broken top\", \"filename\": \"X=-162780.500Y=-198053.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-178013.844Y=-287511.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150257.844Y=-214174.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-158153.375Y=-307514.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-157886.734Y=-78753.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-168068.797Y=-58056.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-156147.703Y=-185109.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=-166237.469Y=-185503.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-172483.672Y=-243979.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"storefronts\", \"filename\": \"X=-154235.516Y=-303764.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-159865.250Y=-311980.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-171024.438Y=-184694.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=36549.051Y=-129691.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-161070.406Y=-42257.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-175278.656Y=-80630.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed facade\", \"filename\": \"X=-162517.938Y=-311933.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-169122.469Y=-302823.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-171098.375Y=-187709.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-171072.234Y=-185704.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=155816.703Y=-131352.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-165252.422Y=-197989.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-155130.547Y=-188694.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-172802.484Y=-304939.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"industrial structure\", \"filename\": \"X=-163679.469Y=-219227.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-174858.328Y=-304317.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-169130.828Y=-189977.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two clock towers\", \"filename\": \"X=-150630.297Y=-152307.906Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-163765.141Y=-151166.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-163323.969Y=-94113.531Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=47229.500Y=73269.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-163765.156Y=-111269.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-169282.594Y=-123583.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-151096.609Y=-168365.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-163765.156Y=-170333.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-210865.375Y=-176290.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=82271.109Y=-196096.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-169282.578Y=-104070.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-170166.219Y=-78046.609Z=313.059.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-175397.484Y=-212068.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-172020.531Y=-252097.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-166260.969Y=-239523.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-163765.109Y=-104070.531Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-95927.711Y=-221689.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-167112.312Y=-190183.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-175777.609Y=-189367.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-171257.312Y=-179616.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-169130.828Y=-185302.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-171024.469Y=-190716.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-171064.484Y=-189526.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-176573.328Y=-299103.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-175493.078Y=-183465.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-167888.844Y=-304119.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"circular\", \"feature\": \"glass windows\", \"filename\": \"X=-175391.656Y=-170018.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-168025.750Y=-190281.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-177137.359Y=-224851.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-176034.531Y=-52764.832Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-168074.875Y=-311103.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-163445.250Y=-297191.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-174319.953Y=-303784.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-173939.516Y=-233327.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-169827.844Y=-95605.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-169932.328Y=-307643.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-174884.203Y=-256776.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-180168.531Y=-251030.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-172139.844Y=-293810.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=73690.180Y=-221926.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-178936.016Y=-260818.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-164954.219Y=-83447.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=157340.094Y=-63874.324Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-189500.641Y=-233603.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-168510.859Y=-212907.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36366.832Y=-323600.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-181430.000Y=-275784.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-175868.297Y=-219555.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-169991.000Y=-219424.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-171065.469Y=-188536.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-182774.234Y=-237597.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-157370.672Y=-306745.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-169282.609Y=-112782.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-180660.219Y=-63951.770Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-176029.406Y=-106237.844Z=619.999.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-171171.875Y=-306761.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-175784.672Y=-236625.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=64869.391Y=-31416.527Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-182417.438Y=-132574.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-182417.453Y=-159304.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-176900.031Y=-157832.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-182417.453Y=-151993.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-50258.750Y=79351.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-171637.141Y=-50297.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=95142.930Y=-111146.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-193548.297Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-190034.906Y=-141565.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=134041.938Y=-300643.875Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"bank\", \"filename\": \"X=-177366.375Y=-116437.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-178383.562Y=-170009.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-165274.828Y=-294712.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-182021.469Y=-167993.281Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-179945.125Y=-289676.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-188526.484Y=-131701.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-190279.828Y=-109114.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-182883.781Y=-111269.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-186858.672Y=-180598.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-167467.203Y=-84715.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with a large black scoreboard\", \"filename\": \"X=-173725.891Y=-262870.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-174714.922Y=-289770.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-171256.375Y=-58264.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-175372.375Y=-284882.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"yellow\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191448.672Y=-210036.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-151410.750Y=-293415.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-191303.125Y=-213038.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-186899.953Y=-223661.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-181541.797Y=-217635.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-193059.547Y=-282974.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style\", \"filename\": \"X=-190034.891Y=-123583.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-191023.031Y=-170628.375Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-186695.859Y=-221547.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-181541.797Y=-225728.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-186905.109Y=-220634.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-182652.484Y=-225688.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-192714.828Y=-198224.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-191518.375Y=-131694.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-188526.484Y=-150340.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-190501.203Y=-153696.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-189312.391Y=-160543.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-179297.703Y=-106238.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-184386.547Y=-217566.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-190620.094Y=-225728.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-170043.156Y=-233591.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-193548.297Y=-109114.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-191518.359Y=-150390.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-182021.406Y=-114875.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-184546.953Y=-114925.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-179364.922Y=-183128.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large black opening\", \"filename\": \"X=-197217.359Y=-86420.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid windows\", \"filename\": \"X=86290.578Y=73497.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-192273.609Y=-213050.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191303.125Y=-203843.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-191588.203Y=-281508.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevator\", \"filename\": \"X=-188447.344Y=-193226.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated stage\", \"filename\": \"X=-187982.344Y=-290860.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-182995.219Y=-67639.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple windows\", \"filename\": \"X=-186830.438Y=-211609.531Z=1345.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-186682.250Y=-222422.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-177366.375Y=-121061.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-182883.844Y=-171407.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangle\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-190932.578Y=-286796.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-185829.578Y=-260441.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"green dome\", \"filename\": \"X=2351.681Y=-258596.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=144363.797Y=-101450.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-185720.406Y=-197557.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=134752.266Y=46149.762Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=-181816.406Y=-205763.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-190076.375Y=-194854.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-184739.219Y=-279079.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"skewered\", \"filename\": \"X=-14659.928Y=-364145.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-179341.109Y=-112747.414Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-186903.359Y=-219529.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-186581.516Y=-285219.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-190965.938Y=-280914.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131180.656Y=-113033.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-182147.672Y=-220634.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=-192273.609Y=-203895.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-188930.719Y=-176651.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-182061.734Y=-221970.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-184386.594Y=-225873.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-186000.594Y=-225754.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-183304.188Y=-210695.797Z=544.410.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-186547.797Y=-289485.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-185761.047Y=-288595.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-182098.172Y=-219529.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-190459.797Y=-280334.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-192623.500Y=-285316.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-188953.375Y=-271221.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-7709.456Y=-178590.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-190501.219Y=-135246.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-182883.781Y=-141565.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-188868.062Y=-289068.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-190755.625Y=-165429.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-177366.438Y=-166662.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191213.312Y=-273451.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=-189548.250Y=-271978.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-176900.031Y=-161388.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-194029.328Y=-256103.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-187168.391Y=-256984.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-187366.922Y=-290225.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"many windows\", \"filename\": \"X=-177366.359Y=-124407.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-176900.031Y=-148951.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-182304.688Y=-195204.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"broken corner\", \"filename\": \"X=-190279.828Y=-112382.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with a pointed top\", \"filename\": \"X=-185834.688Y=-106238.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-182883.797Y=-122446.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-183900.062Y=-182791.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-190465.094Y=-272706.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-191333.469Y=-205466.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-191618.750Y=-217578.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-182417.453Y=-162860.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-176900.000Y=-132574.281Z=394.586.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-185352.656Y=-206841.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-187454.000Y=-225728.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-203443.016Y=-94705.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-195675.047Y=-169302.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-181853.266Y=-291647.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with gold accents\", \"filename\": \"X=-188789.750Y=-283494.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-192476.016Y=-251903.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-184547.000Y=-168051.344Z=618.403.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-192567.125Y=-254549.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-195072.062Y=-270452.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-190465.734Y=-213035.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-190465.734Y=-203843.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-201828.359Y=-217627.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-183284.047Y=-213371.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191496.062Y=-209123.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-191376.734Y=-222729.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-200947.578Y=-223832.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191498.141Y=-207255.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-187052.609Y=-217635.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-185340.234Y=-91533.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-177366.359Y=-141565.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-109973.867Y=10865.390Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"columned base\", \"filename\": \"X=-186021.422Y=-286221.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-182652.453Y=-217653.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-199132.797Y=-273663.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-199615.094Y=-247501.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-194249.250Y=-217667.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195107.391Y=-257212.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191336.828Y=-211141.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-190620.047Y=-217635.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-200901.703Y=-222727.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-198088.625Y=-246050.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-189588.953Y=-288226.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-190680.906Y=-267465.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-181166.312Y=-198298.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=95079.031Y=-206283.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-190689.703Y=-96437.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-193139.797Y=-78921.109Z=819.946.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-192251.656Y=-282104.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-185271.172Y=-84485.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-194191.141Y=-213022.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-195374.141Y=-203879.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191343.484Y=-223834.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195410.953Y=-217634.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-188304.906Y=-247371.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-201286.859Y=-262491.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-55305.523Y=-3166.440Z=569.943.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-182566.188Y=-106238.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-182883.797Y=-125791.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-192276.250Y=-274513.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-199552.484Y=-252002.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-193206.766Y=-203653.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-195766.984Y=-213035.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-200843.828Y=-220314.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-193247.297Y=-225739.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-137204.781Y=-300296.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-202597.797Y=-150848.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-55785.902Y=-233313.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-184155.391Y=-193270.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-202491.094Y=-250403.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-195594.562Y=-269737.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-193195.078Y=-255339.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-197918.047Y=-99221.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195634.250Y=-208700.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-191150.094Y=-241352.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-191899.688Y=-225693.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-196477.297Y=-217635.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-192414.375Y=-217614.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-195426.203Y=-221816.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=-201845.375Y=-122390.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-206714.203Y=-134528.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open frame\", \"filename\": \"X=-189342.859Y=-259060.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-196436.062Y=-203843.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195614.766Y=-207200.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-206182.562Y=-170811.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-193206.766Y=-213079.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-103139.688Y=22174.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-202830.156Y=-217492.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-201839.250Y=-225727.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195448.141Y=-205466.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195682.094Y=-210036.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-194296.453Y=-262907.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-195743.312Y=-211141.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-194249.297Y=-225780.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=11353.965Y=35030.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-195552.344Y=-141565.109Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-196473.344Y=-269324.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195735.547Y=-206208.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-199274.406Y=-259091.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-200234.188Y=-225727.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-193442.391Y=-264731.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191783.766Y=-274005.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-196816.781Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-208933.781Y=-103468.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-183733.781Y=-293455.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-196018.672Y=-134914.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-196018.656Y=-153382.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended structure\", \"filename\": \"X=-196816.781Y=-112382.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-199089.672Y=-267700.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-197996.516Y=-266668.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-199433.391Y=-240254.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-195110.484Y=-263660.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-201880.516Y=-178880.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-211709.156Y=-159341.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"red awning\", \"filename\": \"X=-203279.781Y=-260428.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-196479.938Y=-265046.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-200448.312Y=-251253.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-191205.000Y=-260965.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-135352.062Y=-287033.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with many windows\", \"filename\": \"X=-195984.844Y=-248434.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-203353.766Y=-112382.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-200085.281Y=-112382.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-200085.281Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-195717.094Y=-219893.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-200992.500Y=-221814.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191790.922Y=-253814.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-212923.125Y=-181618.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-201006.438Y=-225687.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open structure\", \"filename\": \"X=-195163.984Y=-98695.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-193247.234Y=-217494.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-200223.281Y=-217635.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-195716.156Y=-222729.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-195549.016Y=-220941.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-202841.281Y=-225794.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195747.719Y=-264427.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-197823.359Y=-253798.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-200995.484Y=-217638.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195771.109Y=-225728.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=-203787.844Y=-121474.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-204892.922Y=-121363.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-192818.500Y=-170608.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-201894.219Y=-126296.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-203787.844Y=-125561.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-201883.281Y=-123261.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-201914.938Y=-121341.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-193857.219Y=-244059.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-196881.047Y=-150025.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-202543.062Y=-153064.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-202544.109Y=-148952.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-195725.453Y=-223834.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-191307.172Y=-220768.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended in air\", \"filename\": \"X=-215748.297Y=-194705.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-214521.531Y=-208700.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-209996.969Y=-203880.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-209684.938Y=-205466.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=-197319.703Y=-265851.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"black grid facade\", \"filename\": \"X=-200103.094Y=-101188.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-196816.781Y=-109114.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=116763.234Y=-203001.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-204892.922Y=-125788.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-201869.281Y=-125147.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-209397.312Y=-183638.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=-218841.844Y=-182451.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-210495.672Y=-249095.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-209868.219Y=-222422.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-214894.875Y=-217635.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=327.819Y=27661.133Z=618.403.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-215768.359Y=-233709.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-195552.312Y=-123583.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-71996.078Y=-326621.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-213898.359Y=-217632.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214473.406Y=-223470.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-206402.906Y=-254200.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-210052.641Y=-225732.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-197490.578Y=-164975.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-213898.359Y=-225720.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214457.984Y=-220634.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214992.484Y=-225728.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-210484.078Y=-122390.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-205285.594Y=-177995.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-210921.875Y=-139318.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=70057.273Y=-303567.625Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-209890.719Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-210429.094Y=-126296.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-194355.484Y=-131328.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-215828.875Y=-121692.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-213234.078Y=-131917.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Archway entrance\", \"filename\": \"X=-205806.344Y=-121582.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=-203353.750Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-214246.734Y=-205466.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-214745.172Y=-213035.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-205033.953Y=-223827.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-210468.844Y=-237605.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-212945.828Y=-225676.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-209825.391Y=-219529.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-204222.391Y=-217578.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-209798.359Y=-210036.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-214277.688Y=-203845.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-209439.500Y=-217635.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=88331.367Y=-91391.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-205319.797Y=-219694.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-209636.984Y=-220634.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-210465.094Y=-124171.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214394.344Y=-207200.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214437.844Y=-210036.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-212068.453Y=-213017.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-212098.703Y=-225723.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-203923.266Y=-182598.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=149803.391Y=-133447.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-206681.203Y=-121473.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-210429.094Y=-121104.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-206681.203Y=-125569.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-204698.703Y=-234755.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-208096.797Y=-121677.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-205111.750Y=-242936.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-206622.234Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-218958.766Y=-166471.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended section\", \"filename\": \"X=-214412.938Y=-177992.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218921.969Y=-150566.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222618.625Y=-221815.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-208998.531Y=-213035.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-214509.422Y=-206208.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-203076.406Y=-191476.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214305.031Y=-222422.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-203076.422Y=-197322.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-219049.562Y=-165423.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-211065.891Y=-213041.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-211065.891Y=-203885.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-212945.844Y=-217619.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-200546.047Y=-183639.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218826.219Y=-179670.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=152499.406Y=-30419.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-209137.078Y=-225728.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-205007.078Y=-220933.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-209698.297Y=-211141.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid windows\", \"filename\": \"X=-203076.406Y=-211609.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-194355.469Y=-150045.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-205799.609Y=-225724.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-211407.953Y=-217658.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-204457.625Y=-140169.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-210083.500Y=-190298.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-209941.719Y=-209123.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-214527.828Y=-211141.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-209704.000Y=-221547.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-205246.281Y=-221808.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-215151.609Y=-203843.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-209662.312Y=-223661.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-212944.828Y=-184679.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-209996.969Y=-213015.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-214405.031Y=-219529.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-211045.797Y=-225717.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-213198.578Y=-213067.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-209897.688Y=-207255.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-203076.422Y=-205763.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-208998.531Y=-203843.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-205794.141Y=-217631.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-204233.281Y=-225709.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-212068.453Y=-203717.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-211082.812Y=-199053.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"overhanging structure\", \"filename\": \"X=-211671.531Y=-194705.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"circular\", \"feature\": \"glass windows\", \"filename\": \"X=-204008.062Y=-256598.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-205806.344Y=-125749.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-215828.609Y=-123900.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-221182.766Y=-185477.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=4432.813Y=-288376.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-223115.484Y=-225728.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222004.781Y=-217606.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-223352.938Y=-162465.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-66202.281Y=58726.781Z=1345.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-221375.047Y=-213026.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218875.734Y=-167346.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-220316.219Y=-157832.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-220535.859Y=-190814.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-217914.062Y=-190841.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222466.531Y=-155500.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222246.312Y=-212979.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222610.766Y=-205736.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-214201.953Y=-190314.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-203896.594Y=-185659.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-219157.906Y=-185496.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-222388.500Y=-179615.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=-222364.938Y=-178623.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-220281.766Y=-185449.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222368.719Y=-223834.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222280.375Y=-162445.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-220077.156Y=-141692.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"water tower\", \"filename\": \"X=-220270.656Y=-225690.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-219314.312Y=-148701.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-212280.469Y=-167887.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=-223180.812Y=-213035.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-222450.922Y=-220941.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-219835.688Y=-171293.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218795.828Y=-164256.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218329.781Y=-213035.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-219058.000Y=-225680.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218153.562Y=-225728.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-221944.828Y=-148668.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218797.969Y=-208630.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222674.641Y=-185450.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222567.188Y=-167346.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"low-rise\", \"shape\": \"square\", \"feature\": \"open sides\", \"filename\": \"X=-221940.312Y=-197952.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=-219835.750Y=-162478.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-223086.766Y=-148673.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=20002.953Y=95960.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-219157.984Y=-176598.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218291.625Y=-157865.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-219055.219Y=-206841.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-213939.797Y=-153815.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"pyramid\", \"feature\": \"glass facade\", \"filename\": \"X=-222690.922Y=-194093.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-30565.994Y=-133447.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=-221182.859Y=-176615.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218906.547Y=-155249.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-218153.562Y=-217635.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-223180.781Y=-203843.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218867.672Y=-168259.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-221149.078Y=-157821.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222452.250Y=-164430.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218887.203Y=-183557.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-222280.297Y=-171236.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"box\", \"feature\": \"glass windows\", \"filename\": \"X=-218463.766Y=-198011.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222436.078Y=-208630.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-219030.281Y=-220768.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-221375.000Y=-203872.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218360.984Y=-162465.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-222517.188Y=-153460.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-56668.043Y=-352166.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-209390.141Y=-234130.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-220162.359Y=-203842.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with industrial elements\", \"filename\": \"X=-222317.484Y=-176630.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-219058.000Y=-217584.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218100.656Y=-185450.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-223352.906Y=-171258.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-221118.609Y=-171281.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222281.688Y=-219893.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-219034.625Y=-169364.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218824.953Y=-207755.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-218933.547Y=-222729.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-221944.719Y=-157904.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222546.359Y=-154507.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218815.875Y=-211044.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222580.984Y=-182451.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222571.141Y=-151671.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118215.602Y=-130348.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-219314.203Y=-157893.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218100.734Y=-176658.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-223356.641Y=-176658.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-219922.797Y=-212981.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218954.391Y=-153008.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222607.906Y=-169364.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-218291.672Y=-148673.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-222339.484Y=-183557.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222557.812Y=-168259.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-222561.984Y=-207755.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-222646.750Y=-222729.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222528.031Y=-206841.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-220597.219Y=-148652.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-223115.484Y=-217635.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-219220.344Y=-194093.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-218569.391Y=-203843.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-221118.656Y=-162438.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222625.484Y=-152585.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-220510.250Y=-217605.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-221765.188Y=-225675.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-27115.570Y=-93732.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-222619.438Y=-211044.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-220281.844Y=-176616.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-223086.672Y=-157865.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-218360.938Y=-171258.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"box\", \"feature\": \"modern\", \"filename\": \"X=121694.227Y=21604.947Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-81294.898Y=-298640.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=46643.969Y=-213312.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=3559.184Y=68391.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93818.344Y=-166742.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-4829.436Y=-293158.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=612.541Y=77854.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=101896.445Y=-101235.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=99566.172Y=-176546.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=93816.688Y=-149076.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"elevated base\", \"filename\": \"X=99728.945Y=-161006.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=100645.898Y=-171252.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=87894.375Y=-186576.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=99566.156Y=-153324.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=112428.188Y=-81732.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=95142.320Y=-125906.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=104300.391Y=72195.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-95895.047Y=-258705.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=95737.320Y=-180710.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=98467.070Y=-123923.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=9232.480Y=77933.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=93952.805Y=-92202.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=101937.539Y=-104260.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=114544.398Y=-105347.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=110928.406Y=-72145.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=94580.992Y=-186908.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=106972.219Y=3570.317Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=109759.156Y=-71942.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=112851.125Y=-71981.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=102372.875Y=-65863.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=102607.188Y=22092.299Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102547.656Y=-167118.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=89336.594Y=-141956.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"stripes\", \"filename\": \"X=110541.102Y=-170031.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102588.891Y=-168118.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=102715.820Y=-169344.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=89272.055Y=-150967.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=100765.602Y=-157498.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=102564.867Y=-153575.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58032.648Y=-380062.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=99545.828Y=-186539.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=111803.211Y=-162555.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93789.219Y=-151302.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102617.008Y=-177734.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=94992.938Y=8515.118Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102526.773Y=-171593.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=102540.086Y=-170617.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107958.055Y=-81747.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid of windows\", \"filename\": \"X=94263.414Y=-158970.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=114869.656Y=-77405.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=24667.072Y=-357069.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=110264.750Y=28822.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-904.175Y=-300781.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=8121.870Y=-256015.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=122013.438Y=-288512.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43865.672Y=-389954.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=8550.528Y=-273561.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=11963.032Y=-126693.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=106193.078Y=-24987.615Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"classical facade\", \"filename\": \"X=95648.961Y=63819.848Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=109778.172Y=-52105.395Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-9447.484Y=-250435.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-174407.000Y=-247485.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=10828.494Y=56051.660Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=94541.266Y=-23292.223Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=98823.875Y=-300327.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=20166.871Y=-205197.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two-tiered structure\", \"filename\": \"X=-139354.875Y=-241063.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=10436.697Y=-131558.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=109300.781Y=-67368.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=8073.694Y=-128684.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102517.969Y=-179788.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=114949.906Y=-97165.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=123256.836Y=-71908.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=107913.562Y=-208191.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=107963.023Y=-71948.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=113764.500Y=-157926.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=114283.617Y=-292450.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=98529.500Y=-316364.031Z=444.989.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107964.867Y=-148264.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=115427.367Y=-212974.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=107964.891Y=-71234.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=100671.250Y=-149167.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=109858.500Y=-153294.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=110819.617Y=-140691.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=109778.195Y=-144086.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-89153.500Y=-79055.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=110560.641Y=-81947.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=107926.719Y=-73855.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=114968.969Y=-149186.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=112751.867Y=-153290.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=114167.453Y=-153188.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=110560.641Y=-86965.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9361.439Y=-344986.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=107995.922Y=-76406.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=111877.023Y=-203763.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=110183.750Y=-63572.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=109759.133Y=-162481.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=107964.891Y=-203001.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=100765.602Y=-87353.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=111976.266Y=-71956.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107964.883Y=-209124.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=102729.102Y=-77121.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=110963.578Y=-153377.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=109858.500Y=-208573.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=109759.156Y=-77387.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=110983.523Y=-157816.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=113764.500Y=-162544.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107964.883Y=-77947.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-2972.468Y=91770.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=114949.891Y=-93309.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=124756.266Y=61098.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=9428.185Y=60552.520Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=11021.271Y=-101321.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=7440.396Y=65449.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=109596.023Y=-225884.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102564.883Y=-175852.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=145991.953Y=-232699.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=115427.367Y=-139357.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=109300.719Y=-96803.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184648.578Y=-100715.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large open space\", \"filename\": \"X=109778.188Y=-115809.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=113764.547Y=-72085.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=112022.680Y=25372.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=9112.143Y=55469.543Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=26562.662Y=-373459.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107964.867Y=-157387.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"slender\", \"feature\": \"tall\", \"filename\": \"X=-65663.023Y=-12226.760Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=13233.049Y=-113283.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=1634.267Y=-106654.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107964.867Y=-163346.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=102425.797Y=-68670.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=111976.266Y=-77329.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=104525.570Y=-48007.059Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=109300.719Y=-122903.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=11567.965Y=-325259.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"ornate facade\", \"filename\": \"X=114544.398Y=-142758.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=112751.867Y=-149242.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=114869.602Y=-157788.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=113744.578Y=-203748.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=113744.578Y=-208520.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21163.945Y=-194980.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=100978.781Y=63645.473Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=110060.398Y=65838.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=6448.649Y=-102327.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=9705.621Y=-238738.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=110963.602Y=-203765.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107994.484Y=-72754.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=10965.532Y=-103497.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=109319.438Y=36810.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=7278.422Y=-244932.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=4527.647Y=-101360.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=108665.086Y=-177367.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=112851.078Y=-87103.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=96817.055Y=-171443.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=104087.016Y=-270079.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=110963.602Y=-208556.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=109858.500Y=-149139.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=107756.297Y=-103121.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=102430.453Y=-62911.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=108059.984Y=-83747.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=107940.258Y=-150298.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=109570.336Y=-166662.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=115085.875Y=-196294.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=115464.359Y=-216367.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=108009.391Y=-158499.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=9971.164Y=-242405.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=114061.875Y=-224675.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=123596.328Y=-9370.861Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=3522.021Y=79008.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-222859.125Y=-190788.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=108023.266Y=-161145.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107929.516Y=-151202.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=107802.359Y=-160205.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16561.938Y=-208211.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107995.750Y=-206034.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=114869.602Y=-81737.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=107597.898Y=-63512.816Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=102778.219Y=-83497.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=116496.109Y=30507.773Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=110928.406Y=-77069.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=117724.414Y=-240336.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=117161.938Y=-2286.309Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=111877.016Y=-148980.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107936.930Y=-162331.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=108038.297Y=-159430.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=113764.500Y=-86964.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Archway\", \"filename\": \"X=107785.477Y=-74907.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=108015.773Y=-82877.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129335.328Y=-305350.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=123642.430Y=50792.824Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=22979.258Y=39775.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=4362.728Y=-384553.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=114949.930Y=-199663.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=111877.023Y=-208413.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies on each floor\", \"filename\": \"X=114287.266Y=-167589.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=114869.602Y=-162652.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107955.031Y=-86645.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107964.867Y=-87792.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=110963.578Y=-148910.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=7841.656Y=70530.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=116710.062Y=-72754.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open floor plan\", \"filename\": \"X=108595.852Y=-222917.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=112851.078Y=-158040.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=121552.406Y=-129439.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=115595.750Y=-275916.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=100671.250Y=-153386.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with a pointed top\", \"filename\": \"X=110124.016Y=-248765.312Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=127603.344Y=67720.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=7204.484Y=-352361.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=115427.367Y=-111147.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107906.797Y=-152344.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=113531.430Y=-184425.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=107975.820Y=-153548.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=111877.016Y=-153263.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=112851.125Y=-77123.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=113764.547Y=-77314.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=171250.344Y=-159195.000Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Archway entrance\", \"filename\": \"X=113764.500Y=-81928.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=22118.303Y=-233203.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=114949.906Y=-135246.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=115427.367Y=-101525.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-150630.219Y=-194630.062Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=109300.719Y=-92947.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=110183.742Y=-198583.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=116798.609Y=-71948.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=113799.734Y=-148897.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-82488.344Y=-2113.065Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107982.406Y=-204139.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=122805.297Y=-53772.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=105131.656Y=-42432.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=123285.008Y=-113325.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=110819.625Y=-103172.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=20592.105Y=-261351.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=114949.891Y=-131701.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=110819.609Y=-214337.469Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=108012.867Y=-207259.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=13843.207Y=-129348.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"skyscraper with a distinctive shadow\", \"filename\": \"X=99150.711Y=-287469.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two towers\", \"filename\": \"X=-159823.391Y=-207543.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=4224.996Y=61230.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=32670.426Y=-370426.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=14300.120Y=-330621.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=105187.711Y=34964.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=119740.148Y=73203.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped\", \"filename\": \"X=7607.016Y=-333236.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=116756.664Y=-158895.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=116740.109Y=-74907.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=124361.938Y=-77343.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116763.242Y=-77624.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116763.188Y=-163346.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=121391.469Y=-72183.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116704.531Y=-161145.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=126546.305Y=-168324.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=117130.250Y=-142702.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=129586.070Y=-162860.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116763.234Y=-153791.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=124361.938Y=-81640.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=125275.367Y=-87118.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=116811.047Y=-152344.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129571.133Y=-160969.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=123285.031Y=-196096.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116818.578Y=-207259.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=114869.656Y=-71985.383Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=113908.484Y=-63921.324Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=124361.938Y=-87130.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=116763.234Y=-208768.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=127198.086Y=-87008.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=116763.234Y=-148264.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=121304.312Y=-73370.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=123285.000Y=-144086.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124714.773Y=-113033.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=116761.148Y=-150298.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=116771.945Y=-204139.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-152722.266Y=-262411.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=17330.273Y=-73420.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-170979.656Y=-186773.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=125698.258Y=-81713.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=125275.367Y=-72025.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=125675.805Y=-223984.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=17330.256Y=-39738.426Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=122790.641Y=-106615.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=125378.469Y=-140773.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=112692.711Y=6485.060Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=119203.195Y=11279.761Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=117438.766Y=48835.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=108072.453Y=-260750.922Z=618.403.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=114195.359Y=-123925.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=113466.836Y=57480.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=128014.148Y=-6838.075Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-87330.000Y=-27980.018Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=8073.471Y=53738.855Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-15721.028Y=-330186.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-637.803Y=77364.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with many windows\", \"filename\": \"X=120701.320Y=-305046.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=120961.117Y=7829.631Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=8698.091Y=52426.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=3464.223Y=-25296.355Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=11556.012Y=-133253.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=176556.641Y=-77986.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=130410.523Y=69124.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=125310.328Y=47440.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=1258.919Y=-344630.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-91389.742Y=-139357.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=3891.383Y=-261855.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=129134.141Y=-176585.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=116763.266Y=-71234.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116768.273Y=-86645.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121315.719Y=-189519.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=121191.531Y=-65772.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=126150.219Y=-87002.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=125275.391Y=-185335.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116718.141Y=-73855.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=125275.367Y=-77120.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=123256.836Y=-96953.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116787.961Y=-85098.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=123256.836Y=-87158.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121212.867Y=-85016.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121379.125Y=-95576.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=127198.109Y=-185471.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=123284.953Y=-181313.656Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121341.109Y=-76016.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=121876.406Y=-102759.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open space in the middle\", \"filename\": \"X=124363.281Y=-65794.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=127198.086Y=-81999.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121363.219Y=-87903.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=125275.367Y=-96718.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=33419.305Y=-133859.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=127198.109Y=-190230.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121346.289Y=-93929.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=124361.938Y=-71937.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=116971.781Y=-63874.348Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"elevated structure\", \"filename\": \"X=122070.773Y=-126409.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=13813.347Y=79487.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=92711.648Y=-56949.402Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=117140.234Y=16523.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=16249.759Y=80550.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121411.938Y=-91934.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=129565.141Y=-284664.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=125407.555Y=-125418.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=172016.438Y=-96814.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=122086.305Y=-123786.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=116796.055Y=-149270.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=5826.666Y=74540.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=12657.246Y=-224626.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=122790.578Y=-217685.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-1891.484Y=49617.090Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=115427.305Y=-57556.934Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=145015.234Y=-39977.707Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=116872.633Y=-206034.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-42473.289Y=-134403.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=123284.961Y=-61917.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=117213.688Y=61132.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=70347.672Y=42102.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=114495.570Y=69745.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=110696.742Y=-266056.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121422.984Y=-77127.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=121363.242Y=-71168.773Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=121310.609Y=-87174.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116763.219Y=-81950.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=124361.938Y=-96773.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=123256.836Y=-91460.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=126150.242Y=-189966.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=126150.242Y=-185499.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=116867.406Y=-151202.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=125407.938Y=-121942.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=85045.891Y=-233272.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=123285.031Y=-199640.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=123285.008Y=-203486.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"broken windows\", \"filename\": \"X=-184344.328Y=-64795.402Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=97715.781Y=-337174.219Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=117678.102Y=-15117.392Z=618.404.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=142561.547Y=-139043.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open central courtyard\", \"filename\": \"X=124363.242Y=-177995.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style\", \"filename\": \"X=-143833.812Y=-215344.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=0.000Y=0.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=112971.461Y=-308984.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=156236.547Y=-190452.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121485.680Y=-75084.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=114949.938Y=-67730.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121395.461Y=-84146.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116921.727Y=-83747.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=124553.820Y=-102743.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=124361.938Y=-91551.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=121363.242Y=-190656.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open front\", \"filename\": \"X=107685.641Y=-170018.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116718.516Y=-76083.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=121405.789Y=-97049.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=143520.828Y=29993.420Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=122859.750Y=65095.996Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=142278.656Y=-9540.954Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=123256.836Y=-77337.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=121330.492Y=-74309.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=123256.836Y=-81943.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=124361.961Y=-185358.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=123256.859Y=-185382.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=135743.469Y=57772.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=124733.445Y=43640.395Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=68044.656Y=-54013.652Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=122101.633Y=-121778.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=117258.680Y=-123925.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=5629.383Y=58459.355Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=122504.031Y=-223984.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=116763.219Y=-87792.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=6876.465Y=-361196.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=131704.031Y=2259.541Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=4295.458Y=-263428.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=136220.266Y=-121665.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=130371.484Y=-134559.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=124320.750Y=2431.245Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121363.219Y=-82149.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121363.234Y=-184805.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with multiple windows\", \"filename\": \"X=130119.766Y=-299903.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=121421.641Y=-92815.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116763.219Y=-157387.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=7258.637Y=-216409.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=128389.391Y=62951.246Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=142606.844Y=33859.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=11694.997Y=-237724.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=132219.391Y=-247918.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=142931.766Y=-43052.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=27101.404Y=-122660.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130588.578Y=-72183.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130589.219Y=-77127.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130770.445Y=-93929.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-126432.797Y=-189983.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121402.625Y=-86146.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121421.375Y=-186412.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=124361.961Y=-190227.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=121191.477Y=-177969.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=120133.656Y=43585.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=128321.852Y=-233991.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=115427.320Y=-53701.082Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=130733.297Y=-103121.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=116610.180Y=66543.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=128190.781Y=-81752.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130561.586Y=-87903.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=139926.281Y=-76447.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"skewed\", \"feature\": \"tapered top\", \"filename\": \"X=143032.734Y=-132532.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"curved windows\", \"filename\": \"X=20171.912Y=-95569.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=125935.117Y=-239944.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=132478.156Y=65718.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"pointed roof\", \"filename\": \"X=137886.422Y=-53594.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=130904.203Y=-308444.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130561.586Y=-90910.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130561.633Y=-190656.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=130048.391Y=-179929.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=127370.969Y=-179935.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=136588.984Y=-217684.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=128639.773Y=-218017.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=139572.922Y=-106697.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130561.570Y=-71168.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=135838.109Y=-74299.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130582.336Y=-86476.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=130554.125Y=-92815.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130561.570Y=-78097.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Gray\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Large windows and open courtyard\", \"filename\": \"X=136588.969Y=-162526.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=123704.812Y=-170371.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=136859.562Y=-222007.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=135852.938Y=-225001.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130605.992Y=-73370.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=136588.969Y=-214140.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=142932.500Y=-207362.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=1667.063Y=-365506.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=136589.000Y=-195764.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=10832.118Y=-104497.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=12914.893Y=-106821.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=135345.984Y=-124666.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open sides\", \"filename\": \"X=126021.266Y=-130986.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=132314.141Y=56048.230Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=65281.379Y=7700.740Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=106553.750Y=-296389.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"concrete wall\", \"filename\": \"X=-122285.914Y=-337948.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=116750.523Y=-160205.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=132187.922Y=49330.113Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Windows missing\", \"filename\": \"X=116731.156Y=-162331.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=12184.625Y=84916.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=142279.766Y=7069.189Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=139857.469Y=32615.387Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=134989.922Y=-188653.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=128535.672Y=-296824.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=137083.406Y=-115814.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"concrete facade\", \"filename\": \"X=-91542.219Y=-222864.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=123147.438Y=-272069.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129134.156Y=-222966.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130593.703Y=-186719.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=127142.922Y=-97030.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=128567.320Y=-91640.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=127142.922Y=-71964.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=128190.773Y=-87112.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=101958.469Y=-73111.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=136282.547Y=-86626.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=135646.328Y=-92947.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=137083.375Y=-185109.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130596.641Y=-185680.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=135673.266Y=-62630.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=135161.594Y=-101185.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130695.133Y=-84578.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=128935.031Y=-77362.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130565.406Y=-75619.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=130607.406Y=-189519.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=5088.927Y=-47196.512Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=141169.359Y=-189023.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-182194.188Y=-223470.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=127142.922Y=-77145.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130502.047Y=-188519.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=141344.891Y=-85609.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=126588.281Y=61960.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=128219.922Y=-63469.773Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=130561.570Y=-81237.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=136005.094Y=-112514.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130596.945Y=-91934.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=137187.016Y=20929.488Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=135830.859Y=-77212.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=133257.500Y=-306091.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=9390.057Y=51174.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=136395.281Y=-298290.562Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=129876.414Y=5669.481Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=6615.916Y=43439.879Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=52756.836Y=-358792.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=135199.531Y=52118.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=124009.781Y=-252672.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=52824.207Y=-78102.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=26563.285Y=-139232.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=135610.812Y=-303738.062Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=129673.109Y=64257.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=1621.833Y=-329514.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=25201.441Y=-116253.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-83350.125Y=-113790.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=141851.312Y=-24455.996Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-1776.678Y=82543.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=140317.438Y=-299031.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=17360.871Y=78424.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-71846.258Y=-207102.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=175676.422Y=-57414.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=5430.996Y=-358270.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=127412.742Y=44933.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and slender with numerous windows\", \"filename\": \"X=137964.125Y=-301384.719Z=619.999.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=136588.984Y=-158970.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=138161.672Y=-188639.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=135198.094Y=-102596.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=44111.297Y=-158345.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=136545.828Y=-96803.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=140447.781Y=-106523.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=136788.125Y=-106640.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=138157.344Y=-106480.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=129333.945Y=55087.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=1145.292Y=-355448.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=139095.844Y=22728.543Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=129297.023Y=-126175.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=5499.487Y=75211.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines and setbacks\", \"filename\": \"X=137179.688Y=-292843.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130532.227Y=-95823.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-52272.938Y=-75680.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"octagonal\", \"feature\": \"grid of windows\", \"filename\": \"X=126847.055Y=-30697.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=22594.273Y=-211967.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=130618.484Y=52623.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=128935.031Y=-71983.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129134.156Y=-67368.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=138800.922Y=-225003.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129956.375Y=40246.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=130897.336Y=-63512.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-129326.242Y=-176631.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=128935.031Y=-86915.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=128935.047Y=-81947.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=136233.250Y=-84081.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=128935.031Y=-96879.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=129718.094Y=-168366.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"unique window pattern\", \"filename\": \"X=-121512.367Y=-311124.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=138161.656Y=-152047.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129134.180Y=-207362.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=129057.859Y=-158239.844Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=129134.203Y=-194630.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"ornate facade\", \"filename\": \"X=142438.141Y=-216218.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=142438.156Y=-196096.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=135072.797Y=-104026.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=180468.734Y=-125796.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=129134.156Y=-225960.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=142970.000Y=-46669.121Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=110094.570Y=74480.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130589.273Y=-187720.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=137083.344Y=-177969.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture\", \"filename\": \"X=142438.125Y=-212674.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=142932.516Y=-203818.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=142932.516Y=-179929.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and columns\", \"filename\": \"X=137654.328Y=-124609.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=137064.281Y=-43542.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=127766.453Y=-302256.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=142080.391Y=19102.254Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=127460.414Y=-242907.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=3568.257Y=-283371.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=126021.266Y=-135081.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=129268.703Y=-122698.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-107627.438Y=-259066.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=131688.625Y=-302997.188Z=544.984.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=5118.471Y=69270.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=13405.946Y=92075.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=127561.523Y=-103077.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=136588.953Y=-168051.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=137083.344Y=-181313.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=130565.719Y=-97049.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=137114.250Y=-47659.582Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=11581.934Y=56584.973Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=9304.756Y=-128064.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=139734.125Y=42284.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=143153.594Y=46002.098Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=143455.172Y=-291230.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=140667.359Y=-223983.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=114654.125Y=-187607.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=139782.594Y=17948.668Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=135161.625Y=-107330.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=139176.859Y=-112556.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=136858.453Y=-277889.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=141488.188Y=49143.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=126673.766Y=37529.277Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=16326.074Y=64136.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=135992.781Y=-65737.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=130778.562Y=17724.955Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=128639.844Y=-106977.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25262.217Y=-105761.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=12713.541Y=-126272.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=11692.904Y=-75776.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=5089.077Y=-63512.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=134826.375Y=-295196.406Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=6282.390Y=-93363.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=142018.312Y=-112256.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=11706.212Y=-44033.496Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-208335.969Y=-131146.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=138748.562Y=-295937.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=10574.344Y=61322.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=142932.547Y=-185441.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=11487.894Y=-245382.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=144359.922Y=-100479.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=136282.547Y=-81902.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=142466.312Y=-106818.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=144302.156Y=-105590.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=90523.734Y=17286.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=151378.000Y=-286358.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"tapered\", \"filename\": \"X=125125.625Y=-24371.121Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=138834.844Y=-19641.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=142561.562Y=-142388.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=135183.016Y=-105875.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=149012.719Y=-54277.043Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=80471.836Y=-138761.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=132311.969Y=-19830.006Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=126078.672Y=-1018.886Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=135149.375Y=-39468.449Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=142438.141Y=-168366.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=158136.500Y=-239802.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=149150.000Y=-159418.094Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-28964.279Y=-153382.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=142932.516Y=-176585.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=137083.344Y=-208829.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=142438.156Y=-199640.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=136588.984Y=-199308.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=153451.172Y=-101492.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=144399.688Y=-104332.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=25426.801Y=-135348.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=128219.945Y=-140487.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=156236.547Y=-144086.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=131367.953Y=37599.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=132473.078Y=-297549.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=138332.766Y=-39529.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=150881.703Y=-166662.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=128639.781Y=-214473.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"many windows\", \"filename\": \"X=136588.953Y=-171407.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=128567.359Y=-189943.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=137083.344Y=-205284.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=151958.641Y=-57695.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=130561.625Y=-184805.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=142335.188Y=-39939.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=7389.664Y=-341217.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=137458.406Y=-141946.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=146038.375Y=37403.551Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=148017.875Y=-278924.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=146237.469Y=-3497.605Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style\", \"filename\": \"X=147255.828Y=-256122.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=111204.664Y=-222814.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=14594.471Y=74015.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=148959.906Y=-218509.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=144525.703Y=-103342.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=152416.469Y=-114472.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=100618.219Y=-93538.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=154099.125Y=-92371.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=144342.047Y=-106449.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=153333.766Y=-107035.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=141361.203Y=-101538.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"wooden facade\", \"filename\": \"X=138669.188Y=53869.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=156236.547Y=-140741.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=142061.500Y=-56784.832Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=152296.234Y=-39478.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"tapered top\", \"filename\": \"X=139590.594Y=-283251.438Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=148739.484Y=-21728.162Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=156115.062Y=-274802.500Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=143469.312Y=11693.124Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=10095.240Y=49694.645Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=36507.930Y=77407.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=14969.480Y=66641.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=109300.719Y=-134914.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=138601.203Y=48110.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=155521.719Y=20363.520Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=154960.344Y=-1818.735Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=149309.906Y=34717.332Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-117110.453Y=-121550.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-2089.792Y=-276431.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended structure\", \"filename\": \"X=148833.344Y=-17649.982Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=12830.255Y=-91352.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=14926.831Y=56737.012Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=146160.672Y=25505.277Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=151524.406Y=-15028.972Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=154656.875Y=834.964Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=152078.312Y=-73736.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=145025.531Y=-23424.627Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=142932.516Y=-52466.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=148945.375Y=-213413.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=150586.453Y=-212869.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176556.656Y=-71653.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=151330.734Y=-212874.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=150635.578Y=-224327.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=148862.109Y=-55568.934Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=152872.062Y=-52480.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=148929.047Y=-215990.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-17575.959Y=-293628.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=154999.219Y=-159418.094Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=16038.002Y=54561.754Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=70160.180Y=-148922.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=15808.274Y=-106797.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=19825.359Y=-105723.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=157432.594Y=-251576.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=152150.344Y=-122966.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=18867.881Y=75436.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=142438.141Y=-161055.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=150387.328Y=-139357.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=160412.516Y=-260667.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open top\", \"filename\": \"X=150387.344Y=-181000.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=150881.719Y=-196969.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=150586.453Y=-217639.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-43322.664Y=-261881.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=142018.281Y=-150064.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=143846.750Y=-188985.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=150374.344Y=-177643.000Z=618.776.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=150387.344Y=-185441.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=149472.906Y=-122909.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=152323.438Y=-217473.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=4612.284Y=-73686.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=148959.922Y=-57400.152Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=152872.062Y=-57437.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=149698.469Y=-151455.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=156730.891Y=-170333.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=129134.219Y=-198174.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=163063.031Y=-130442.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=148715.375Y=22074.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=148976.141Y=-214560.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated sections\", \"filename\": \"X=148920.859Y=-217554.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=141101.875Y=-293583.938Z=544.984.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"elevated structure\", \"filename\": \"X=-37857.961Y=-345033.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=23222.109Y=-136480.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=132444.344Y=14227.876Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=151232.984Y=28065.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=142009.047Y=-18610.441Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=4014.385Y=-247157.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=160191.047Y=-26560.705Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture\", \"filename\": \"X=148771.234Y=-66134.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"pyramidal\", \"feature\": \"glass facade\", \"filename\": \"X=148084.000Y=-15241.174Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=148950.188Y=-52770.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=547.981Y=68364.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=156274.781Y=-67730.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=151445.562Y=-208.464Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-145240.359Y=-275839.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=164857.641Y=-241710.500Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=52835.344Y=-62434.105Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=151330.734Y=-217644.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=142466.312Y=-101332.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=163907.031Y=-32202.619Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=160677.500Y=-19729.160Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=157130.516Y=-84812.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=144695.688Y=-56322.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=16434.609Y=-269073.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-107392.141Y=-26415.012Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=158167.719Y=-216296.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=18585.812Y=-20847.711Z=620.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open entrance\", \"filename\": \"X=156775.219Y=-225960.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=164170.641Y=-217159.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=144408.062Y=-102596.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162758.328Y=-61599.035Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=145285.734Y=-273562.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=160804.719Y=-257399.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=16728.623Y=-132743.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=141239.766Y=-95175.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=158171.000Y=-56682.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=22400.412Y=68381.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=163196.266Y=1287.999Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=145491.312Y=9671.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=156164.047Y=-57485.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=152127.031Y=4588.981Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=155547.688Y=-151455.359Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=164764.109Y=-3217.856Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=153902.625Y=-252142.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=142438.109Y=-157498.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=14860.661Y=52584.301Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162759.391Y=-110413.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=163007.266Y=-134560.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=161557.234Y=-17650.082Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162758.312Y=-68298.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=158509.234Y=-222966.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=16623.711Y=75063.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=158189.094Y=-54687.965Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"tapered top\", \"filename\": \"X=4791.072Y=5862.338Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=150387.312Y=-204952.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=155159.594Y=-212954.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=163399.125Y=-189695.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=156236.531Y=-179616.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=150387.344Y=-142702.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37004.035Y=-369419.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade\", \"filename\": \"X=156236.531Y=-186908.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=163627.938Y=-210264.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=155876.141Y=-222941.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=151399.344Y=-66145.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=158786.188Y=-20335.822Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cylindrical\", \"feature\": \"glass windows\", \"filename\": \"X=160894.453Y=530.572Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=158131.406Y=-52770.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=157454.828Y=-18983.066Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=159156.359Y=-11233.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=16734.584Y=53077.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=163520.016Y=-123793.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=5125.736Y=-262950.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=161589.156Y=-247593.781Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=156236.500Y=-176272.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=158494.109Y=-131369.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=150881.719Y=-170018.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with intricate detailing\", \"filename\": \"X=-107741.820Y=-86443.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=167001.406Y=-77145.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=154246.156Y=-217740.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=160254.609Y=-14007.396Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=160804.719Y=-254130.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=165757.391Y=-115487.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=157017.484Y=-122910.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=168216.594Y=-86897.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=166137.562Y=-15675.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=161779.094Y=-19305.332Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=152628.203Y=-83097.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=157180.359Y=-82267.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162763.234Y=-105705.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=157259.688Y=-106938.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=168688.828Y=-50729.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=155427.922Y=-255149.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=157164.797Y=-20818.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=208.032Y=69155.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162634.594Y=-93778.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162764.453Y=-95624.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=162856.312Y=-5583.310Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=18017.373Y=-101551.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=16856.135Y=-106524.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=73775.055Y=-87372.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=164857.641Y=-235173.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=167344.219Y=-208197.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=153371.297Y=-217644.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=154739.625Y=-52504.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=164651.922Y=-62030.754Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=158158.297Y=-212974.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"grid-like facade\", \"filename\": \"X=166130.500Y=-53291.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156264.672Y=-213037.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156531.781Y=-52291.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=153746.922Y=-57609.160Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=158773.969Y=12182.284Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=150881.734Y=-129901.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=164247.781Y=-72394.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern architecture\", \"filename\": \"X=153374.172Y=-223983.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=21062.924Y=88774.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=160734.203Y=3633.910Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=161031.328Y=-16737.135Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Archway\", \"filename\": \"X=155268.828Y=-12461.229Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=162114.891Y=-3185.274Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=153410.969Y=-6679.603Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162744.359Y=-19005.383Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=168928.969Y=-21268.887Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=15330.319Y=-18158.082Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=13987.094Y=51864.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=53818.219Y=-360866.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=149988.000Y=-261484.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=160999.078Y=-15645.819Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=152523.219Y=-22572.580Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=161284.109Y=-250862.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=161589.156Y=-244325.312Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=160012.266Y=-12924.176Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=157176.656Y=-17989.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=151605.500Y=-19728.744Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=170532.266Y=-38101.168Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=152189.094Y=-244497.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=166941.938Y=-18052.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=14252.262Y=-133983.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=164857.641Y=-238442.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=161589.156Y=-234519.812Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=156719.312Y=-234508.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-23023.330Y=-47654.191Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=156953.203Y=-258156.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-115249.258Y=-79085.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-5522.292Y=-327115.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=167338.594Y=-11162.847Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162748.234Y=-67296.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=164653.781Y=-111245.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162758.297Y=-97709.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162758.281Y=-90813.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=158478.281Y=-112515.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=157300.984Y=-104031.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=162721.328Y=-112613.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156923.047Y=-16970.232Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=158508.578Y=-73324.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=158158.328Y=-51674.371Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162775.000Y=-92214.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162817.250Y=-96814.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=157608.125Y=-94595.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=162567.422Y=-77145.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=165757.031Y=-62090.832Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=162758.281Y=-107328.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162773.922Y=-114327.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162611.516Y=-94637.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=158212.953Y=-53759.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162709.188Y=-113553.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=164384.812Y=-97029.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=162754.766Y=-62720.824Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=164384.812Y=-91505.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156336.891Y=-12166.358Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=155873.703Y=-73267.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21997.660Y=-121968.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=156730.906Y=-134914.422Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=152975.188Y=-133390.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=164886.594Y=-11969.354Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-49882.312Y=-150559.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=158158.344Y=-58397.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-90234.352Y=-306124.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, modern, glass-covered\", \"filename\": \"X=169126.750Y=-187036.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=6421.271Y=-107288.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-144936.016Y=-178499.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=163496.750Y=-121786.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162758.500Y=-115678.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=163478.719Y=-83584.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"geometric\", \"feature\": \"modern design\", \"filename\": \"X=55242.469Y=-170413.750Z=64.787.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=164652.297Y=-115559.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=163496.812Y=-86608.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=164126.375Y=-205147.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=161150.109Y=-44723.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=156730.906Y=-196969.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=163388.703Y=-185390.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"many windows\", \"filename\": \"X=177612.016Y=-159304.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=153198.297Y=-212994.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162768.109Y=-64284.387Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=167053.391Y=-224025.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=164185.688Y=-171407.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=178450.188Y=-134977.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=167298.625Y=-152070.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-15699.438Y=-337321.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=165758.891Y=-111162.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=164384.812Y=-101363.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=157180.359Y=-86992.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=157633.516Y=-91681.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=166670.453Y=-67377.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=153845.703Y=-123929.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=157790.844Y=-11633.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=156585.219Y=-5648.226Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with a clock\", \"filename\": \"X=161589.156Y=-237788.328Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=161589.156Y=-241056.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=149456.922Y=-239135.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=91105.336Y=31809.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=5150.239Y=-312663.562Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=179555.297Y=-101346.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=171978.062Y=-112616.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=154206.562Y=-63818.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=164651.922Y=-67512.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=89764.258Y=-292723.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=163480.547Y=-126415.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=24817.555Y=73891.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=162780.609Y=-63514.965Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=160735.797Y=-14811.494Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=176556.609Y=-101072.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=14403.676Y=67687.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=22429.980Y=58314.379Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=56319.598Y=27527.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=154794.781Y=-57758.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=151635.641Y=-77554.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176556.609Y=-121558.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-2049.184Y=-362413.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=15871.845Y=81316.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=178450.219Y=-125881.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176556.625Y=-116259.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156286.406Y=-15296.771Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=165757.031Y=-67415.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=163499.156Y=-223983.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=171957.703Y=-110415.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=26902.197Y=-134605.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=178089.969Y=-151993.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=160111.094Y=-10901.021Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=15567.130Y=55432.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-169748.906Y=-141565.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=163520.562Y=-81267.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176560.531Y=-133346.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=170034.844Y=-139043.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=176556.594Y=-107503.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=165754.047Y=-101580.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=177612.016Y=-162860.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=177329.609Y=-178325.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=178450.219Y=-101438.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=168044.484Y=-96706.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=171996.047Y=-95624.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=17750.682Y=-257318.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=22158.600Y=-204153.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=167376.422Y=-82922.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=164047.797Y=-197243.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=150853.547Y=-57746.059Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=171984.828Y=-67056.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=178450.250Y=-115407.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=25203.170Y=-110910.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=18503.059Y=-111143.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176608.516Y=-76706.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=163387.453Y=-50853.746Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156264.672Y=-217514.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=171442.906Y=-23483.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=156568.594Y=-16165.527Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=170063.016Y=-101410.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=176556.609Y=-110537.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=166176.922Y=-91720.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=83032.906Y=-162409.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=5218.027Y=-327699.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=164050.672Y=-179082.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"dome\", \"filename\": \"X=171691.766Y=-209066.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=172012.469Y=-63514.965Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=167169.625Y=-106721.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=176566.719Y=-132484.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=164185.703Y=-168051.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=170034.859Y=-166662.312Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=130655.008Y=-74309.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=165754.047Y=-106503.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=179555.344Y=-111177.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=177027.938Y=-92736.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176662.531Y=-88152.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=170912.688Y=-30171.334Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=162892.141Y=-65472.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=171956.672Y=-61599.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=43503.383Y=40408.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=168485.703Y=-196037.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-74429.242Y=5015.960Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=171923.625Y=-115262.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=170330.578Y=-115240.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=175633.172Y=-44284.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-6997.220Y=-23331.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=167911.344Y=-179053.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=167911.344Y=-160032.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=171161.047Y=-179975.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=171218.109Y=-162839.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated entrance\", \"filename\": \"X=178890.984Y=-83783.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=172931.703Y=-27833.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=169120.812Y=-219981.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=164185.734Y=-143773.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=171800.969Y=-75675.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176585.969Y=-134249.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=170330.125Y=-67575.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176496.797Y=-73042.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=167416.328Y=-130989.422Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136370.172Y=-275696.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=171956.625Y=-90813.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=166670.891Y=-115359.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=169619.812Y=-56214.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=168957.906Y=-101533.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=180391.062Y=-88480.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171868.500Y=-93778.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=170063.016Y=-91605.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171927.984Y=-62720.832Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=171766.578Y=-72723.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=170663.750Y=-122706.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=167621.594Y=-101383.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171956.625Y=-97709.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=167720.172Y=-111329.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=176575.141Y=-103481.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=171756.609Y=-78482.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=168957.906Y=-106829.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171956.609Y=-107328.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=176610.000Y=-111927.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=177778.922Y=-96745.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=170063.016Y=-97018.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=168538.016Y=-62368.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171985.281Y=-102444.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176588.422Y=-106136.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=180513.594Y=-92736.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=178450.266Y=-77374.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=176486.094Y=-113474.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=179555.297Y=-106799.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=176608.062Y=-82391.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=172066.875Y=-65472.773Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=11591.998Y=-365358.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-15310.141Y=-236544.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=170330.141Y=-62075.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevator shaft\", \"filename\": \"X=11689.074Y=-39284.168Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=168982.984Y=-24293.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=170521.234Y=-202298.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=17276.578Y=-44487.656Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=12353.467Y=-67368.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=12978.270Y=79199.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-4481.931Y=-308581.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=22949.742Y=67511.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=59702.797Y=-338046.438Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=172739.781Y=-53288.988Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=18471.439Y=-91713.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176556.578Y=-135425.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=27100.867Y=-125147.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176650.250Y=-104610.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=177728.656Y=-64112.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176586.719Y=-122626.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171985.453Y=-105325.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines and setbacks\", \"filename\": \"X=170034.875Y=-142388.750Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=178450.219Y=-106862.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=168538.375Y=-115540.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=164185.734Y=-140427.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171956.625Y=-100694.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176556.594Y=-126039.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=176580.891Y=-124416.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=171290.391Y=-82922.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=168656.875Y=-72418.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176283.516Y=-140741.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171766.516Y=-134560.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=177727.266Y=-67421.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176556.594Y=-129896.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=178450.219Y=-122177.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"yellow\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=172067.141Y=-103483.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=176572.734Y=-115013.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=178450.188Y=-130345.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=166803.031Y=-121911.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=170063.016Y=-106845.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=176635.891Y=-85199.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=179555.344Y=-77308.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=169964.188Y=-111436.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=178450.266Y=-71982.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative columns\", \"filename\": \"X=-99238.820Y=-288923.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=168044.484Y=-106500.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=168957.906Y=-91668.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=172008.938Y=-94637.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=44395.508Y=-95498.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-1738.578Y=-385268.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=19877.350Y=-254388.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=178384.000Y=-169154.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=164050.703Y=-160091.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=168538.016Y=-67323.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=171956.672Y=-68298.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=168957.906Y=-96955.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=171707.688Y=-152128.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large overhang\", \"filename\": \"X=178180.156Y=-144086.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-64939.379Y=-88999.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=170034.859Y=-170018.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=176570.750Y=-131404.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=171803.719Y=-130442.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171995.016Y=-111430.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=172015.328Y=-104234.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=170660.266Y=-126181.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=179555.344Y=-115473.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=172012.828Y=-106350.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=179555.297Y=-125783.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=176570.719Y=-74183.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=179555.344Y=-71991.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=4781.163Y=-13452.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated entrance\", \"filename\": \"X=172077.234Y=-86933.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=16655.418Y=63365.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184655.000Y=-129896.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=166802.656Y=-125425.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176540.141Y=-102433.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=171956.719Y=-116215.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=179555.297Y=-122266.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"curved windows\", \"filename\": \"X=185076.719Y=-142702.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=184608.812Y=-131404.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=178450.250Y=-111350.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=176516.266Y=-123378.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=176614.141Y=-75370.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=172101.453Y=-64284.387Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=11146.828Y=78347.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25981.422Y=71513.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=14019.984Y=-106771.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated stage\", \"filename\": \"X=25203.635Y=-107643.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=182391.438Y=-135014.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=181343.578Y=-134878.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=182391.453Y=-101301.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=184668.500Y=-102075.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=184685.250Y=-124685.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=181343.578Y=-122112.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=184622.578Y=-111683.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=181968.562Y=-125594.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184675.984Y=-72700.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=181343.641Y=-72090.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=166176.922Y=-96738.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=181343.594Y=-101431.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=182391.500Y=-115454.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=180468.719Y=-130151.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=181343.578Y=-130383.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=184692.109Y=-113669.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184650.125Y=-107503.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=184665.031Y=-115452.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184691.625Y=-77084.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=18005.145Y=77067.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=14577.167Y=43596.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=182040.547Y=-66371.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-293.797Y=-265711.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=184778.094Y=-74183.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184680.750Y=-104982.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=180468.781Y=-115303.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184635.688Y=-71311.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=184652.688Y=-110537.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=144637.500Y=42953.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184653.953Y=-116260.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple windows\", \"filename\": \"X=183010.469Y=-149951.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-29114.930Y=-111163.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=178089.969Y=-148636.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=179157.062Y=-140795.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=184339.250Y=-88478.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=184095.609Y=-97129.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=184147.562Y=-91788.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=181273.516Y=-96790.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-23072.188Y=-158002.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=183479.328Y=-139357.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=180468.734Y=-106514.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=180468.781Y=-72063.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=182880.719Y=-83727.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=184149.188Y=-94813.383Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=182585.734Y=-130130.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=182585.281Y=-111162.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184845.844Y=-103481.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=181343.641Y=-115321.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=184682.781Y=-123378.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184687.406Y=-135306.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=180468.750Y=-121990.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=181343.625Y=-111460.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=181968.609Y=-77130.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=181968.562Y=-106537.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184679.625Y=-106509.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=184655.000Y=-126307.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=184654.984Y=-121558.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=179555.297Y=-130108.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184649.219Y=-134249.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=182391.500Y=-71958.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=184690.812Y=-75748.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=14849.284Y=-95206.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-185362.812Y=-203670.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=15426.744Y=74393.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=180468.734Y=-101513.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated balconies\", \"filename\": \"X=19765.637Y=-102496.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=180468.719Y=-134878.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19333.936Y=-205568.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=21229.053Y=-16961.758Z=618.404.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=30036.898Y=-339039.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=17400.936Y=70519.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=23431.311Y=-205622.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=21718.125Y=69811.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19035.145Y=65300.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=13473.031Y=-348153.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-13552.276Y=-49527.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=3967.348Y=-249606.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=27020.195Y=-81540.555Z=1320.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=4612.284Y=-77541.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-43396.770Y=47471.590Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=14056.027Y=-51328.723Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25224.195Y=-106812.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25249.215Y=-104788.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=11790.297Y=-95208.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=15808.273Y=-101416.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=25125.049Y=-103661.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-57550.207Y=-131368.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=6197.977Y=96405.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=23331.889Y=26906.092Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=105241.578Y=-312923.312Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"black windows\", \"filename\": \"X=12669.426Y=-240223.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=19803.633Y=-100589.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=17631.939Y=-114370.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=24286.891Y=-211134.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=21311.168Y=66795.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=26456.451Y=70714.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-80052.586Y=-122518.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=20192.764Y=-207272.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=15295.440Y=-264079.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two towers with a central courtyard\", \"filename\": \"X=22061.984Y=-343102.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=1161.382Y=-339282.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=4319.748Y=68825.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=6552.364Y=-103348.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"With a large open space in the center\", \"filename\": \"X=25422.980Y=-210560.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=15595.062Y=69617.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=30956.074Y=-322178.062Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=16815.719Y=-198002.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=14590.470Y=79985.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=18988.580Y=-200795.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed facade\", \"filename\": \"X=25192.551Y=-115377.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=17877.592Y=-16858.199Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43835.086Y=-216375.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=20455.145Y=-208378.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=15144.563Y=-131425.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=1798.459Y=78545.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=26190.662Y=75042.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=218.684Y=-101580.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=23810.043Y=-206669.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=7515.234Y=54739.723Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=18787.363Y=-344771.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=27170.465Y=-233081.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=18523.898Y=-205997.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=18169.479Y=70938.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=15659.274Y=65215.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=24915.871Y=-209004.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-7404.587Y=-255443.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=30228.467Y=-243312.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with a spire\", \"filename\": \"X=30301.443Y=-200239.578Z=65.008.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-31985.139Y=-80613.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19045.137Y=71243.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=12939.566Y=31906.783Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=45607.641Y=-71728.844Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"parking garage\", \"filename\": \"X=10664.091Y=-182583.828Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=4506.148Y=-190525.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=15396.828Y=-133433.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=20229.057Y=65900.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=21022.701Y=-320963.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=27765.322Y=29165.033Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=13571.364Y=-210733.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=21026.230Y=-204734.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=8761.433Y=-260052.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=24564.979Y=35222.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=10939.933Y=-377598.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94833.633Y=-254145.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25601.352Y=-246122.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=44785.051Y=-347390.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=25021.117Y=-2952.051Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=20884.314Y=-209185.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=16672.180Y=79863.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=95748.344Y=36858.895Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=26564.715Y=-349348.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=23796.527Y=1814.904Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=21834.518Y=-212393.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=26434.256Y=-210050.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=30524.961Y=39334.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=18491.680Y=-362553.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=25672.158Y=72509.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-116696.969Y=-360155.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=31588.721Y=-101382.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=17302.312Y=-77431.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=26624.793Y=-8478.692Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=11052.264Y=-102697.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25215.148Y=-114503.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=14019.986Y=-101425.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25143.684Y=-112371.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=40438.078Y=-223987.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-127307.719Y=-176557.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=20181.340Y=-114431.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated top floors\", \"filename\": \"X=34070.492Y=-101435.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=16856.137Y=-101368.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=29538.672Y=-101546.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=14933.421Y=-101473.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19821.348Y=-106996.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Windows\", \"filename\": \"X=25178.105Y=-113551.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=18017.373Y=-106802.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19700.631Y=-104497.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=23418.588Y=-211579.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=130377.500Y=-265169.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=26575.967Y=-250975.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=26974.926Y=69781.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-141755.953Y=-190161.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=23294.785Y=66748.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical facade with modern addition\", \"filename\": \"X=23658.889Y=-374432.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=16613.244Y=70053.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=39852.000Y=-298279.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83050.625Y=-19251.121Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=21451.562Y=-210294.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=18484.031Y=76297.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, and symmetrical\", \"filename\": \"X=26542.221Y=-63512.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramidal\", \"feature\": \"pyramidal roof\", \"filename\": \"X=65669.016Y=-236390.172Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=14614.473Y=-130455.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=37018.836Y=-350776.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=13811.380Y=68947.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11604.932Y=-197858.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=54352.172Y=-40555.793Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=20312.918Y=72600.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=27097.260Y=-101350.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=32197.369Y=-65773.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=27020.197Y=-77541.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=19664.756Y=-86503.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=32030.682Y=-111427.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=34013.914Y=-107643.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31404.398Y=-115174.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=24996.160Y=-95208.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=3823.657Y=74245.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=13302.746Y=-126019.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=20166.299Y=-237370.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=33060.512Y=-40019.090Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-92037.234Y=-71996.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=38130.434Y=74309.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=31326.746Y=64082.652Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=22863.047Y=-350179.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=30947.900Y=17449.611Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129134.219Y=-113325.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=30286.047Y=-204835.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=81553.938Y=1667.599Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=44344.984Y=-3374.376Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=30970.643Y=-92911.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-26639.057Y=34183.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=34003.562Y=-105761.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=34067.816Y=-104788.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=27020.188Y=-91352.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=33968.980Y=-106812.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=39307.414Y=-315495.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=30293.480Y=-223251.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=47538.746Y=29244.158Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=29388.965Y=-381399.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=27414.447Y=68795.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=42652.848Y=-29974.799Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=28564.004Y=70384.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, and symmetrical\", \"filename\": \"X=51373.211Y=-245766.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=4783.424Y=-353612.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-857.588Y=58190.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=29382.064Y=-12246.513Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=29990.637Y=-106696.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=34240.707Y=42972.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=10647.790Y=-6281.198Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=30163.318Y=71196.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=28200.414Y=-115513.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-76554.766Y=-200445.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=23109.934Y=-265671.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=27406.766Y=75674.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=171.593Y=-18563.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=40305.484Y=-40926.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=2633.844Y=80971.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31038.012Y=-111365.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=31038.500Y=-106817.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=17410.346Y=-338718.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31988.238Y=72061.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=39516.570Y=75760.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-8824.127Y=-225001.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=33110.512Y=45220.223Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=31476.488Y=-248026.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=23164.539Y=-203632.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=25188.406Y=-224927.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=27816.400Y=-194145.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=2927.243Y=-354605.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=34837.363Y=29944.723Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-44433.141Y=35029.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-80724.562Y=-268213.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=28223.895Y=-316815.875Z=394.586.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=32675.336Y=-75946.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=37178.426Y=76083.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=34001.453Y=-114428.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=33986.352Y=-112374.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=33786.191Y=67136.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=35545.625Y=-379654.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=37793.664Y=74928.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=33631.719Y=-241615.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=10336.467Y=-362906.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=39387.938Y=-93245.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=29348.697Y=70868.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=34519.590Y=-213816.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=29115.783Y=-106518.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-188950.984Y=-170656.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=123224.562Y=-150274.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=19664.760Y=-56737.238Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=26542.225Y=-67368.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=54346.172Y=-307892.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=33739.980Y=72794.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=36752.629Y=-1362.053Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=22730.865Y=-188401.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=35623.188Y=-344733.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=36413.117Y=-369692.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=28062.717Y=-85370.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=27020.193Y=-73686.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-13902.088Y=7934.427Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall clock tower\", \"filename\": \"X=-137285.359Y=-217547.391Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=29593.152Y=76761.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-200385.547Y=-248149.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"box\", \"feature\": \"open center\", \"filename\": \"X=28350.953Y=43601.348Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=32769.582Y=-177281.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical facade\", \"filename\": \"X=82271.094Y=-188653.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=28368.502Y=-372488.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=34013.910Y=-100584.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=32675.324Y=-86992.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=23702.324Y=-252439.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, with a grid-like facade\", \"filename\": \"X=47453.480Y=-294196.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=31789.875Y=76641.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=40639.027Y=-7061.084Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=20990.225Y=71384.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=24066.438Y=-352657.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=39662.922Y=89076.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=34220.160Y=-103661.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=27096.836Y=-111141.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=27095.416Y=-115230.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=29115.344Y=-111180.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=40154.668Y=-96941.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=33453.207Y=-123635.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=27097.256Y=-106809.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=28202.350Y=-101323.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=33997.418Y=-111262.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=32675.320Y=-96803.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=34474.414Y=-177262.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=4527.648Y=-106856.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=3422.556Y=-101519.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=34015.016Y=-110377.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=35662.645Y=53157.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=32015.756Y=-10904.581Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"balconies\", \"filename\": \"X=33220.625Y=-168897.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=84200.703Y=-170703.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=33893.617Y=86902.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=22727.711Y=-247586.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=33106.836Y=33341.078Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=5858.025Y=64615.832Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=26827.887Y=-24536.652Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=7668.835Y=82873.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=29180.328Y=41874.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=32297.006Y=-5588.556Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=28919.568Y=-357320.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=34562.273Y=93748.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=12600.021Y=-265453.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=38428.320Y=-373712.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=10854.068Y=75109.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=35404.266Y=-328979.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical facade\", \"filename\": \"X=30223.057Y=50387.582Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=7632.456Y=-268455.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=45178.539Y=-199445.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=16036.703Y=-22143.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=12156.860Y=78799.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-187483.016Y=-269720.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=28622.021Y=-235930.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=32290.078Y=75881.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=31306.477Y=-384144.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=36258.105Y=-233416.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large open entrance\", \"filename\": \"X=40997.035Y=44401.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=124196.188Y=15622.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=35311.105Y=79842.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=29319.109Y=-762.116Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=32838.219Y=74585.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=60403.656Y=35772.238Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=4110.920Y=-347625.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=31263.098Y=77655.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=35248.375Y=-10919.833Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=37040.574Y=80619.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=35789.266Y=68221.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=8346.017Y=65695.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open floor plan\", \"filename\": \"X=39952.504Y=-63512.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"open center\", \"filename\": \"X=43583.613Y=-105164.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=50909.410Y=62891.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=45201.711Y=-170383.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=39591.859Y=-375880.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=23635.869Y=37023.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=38653.723Y=-112781.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elaborate facade\", \"filename\": \"X=47414.023Y=-115589.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=38252.621Y=-102873.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=47412.523Y=-114153.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=43400.922Y=-111214.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=45607.637Y=-61917.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=44326.168Y=38678.410Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=3545.392Y=-261235.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=36022.160Y=78383.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=41107.512Y=19876.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=2662.265Y=68128.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=11951.474Y=50828.559Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=7217.239Y=59514.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=53283.430Y=-20917.096Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=25203.635Y=-101065.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=14039.797Y=-81936.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6679.320Y=65015.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=28604.834Y=76267.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31007.076Y=71552.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=38979.824Y=81259.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=98005.133Y=-13869.597Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=35800.664Y=-188203.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=45645.098Y=-204598.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=36824.121Y=37005.535Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=32267.127Y=-302187.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=41256.988Y=76790.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=38662.508Y=-111725.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=43583.617Y=-75606.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-19635.713Y=26751.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=38247.828Y=-73324.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style, setbacks, ornate detailing\", \"filename\": \"X=32372.016Y=-180941.578Z=64.996.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=40430.469Y=-171722.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=28062.713Y=-95249.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=41.355Y=-345420.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-10593.685Y=-329732.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101641.922Y=-132610.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=43667.863Y=79045.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=41472.992Y=-168310.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=25188.410Y=-220821.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=47424.195Y=-110590.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=47469.559Y=-111725.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=47529.465Y=-159304.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=29042.293Y=-7246.915Z=620.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=51994.566Y=60831.676Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=45495.363Y=-365075.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-30039.512Y=-124109.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=39952.504Y=-77180.383Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=38613.914Y=-110590.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=50596.102Y=52786.199Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=9516.544Y=-348923.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=20080.039Y=10425.507Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=42956.047Y=80692.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=46074.285Y=-142336.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=48019.793Y=61784.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=44480.477Y=90008.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=38406.434Y=-168366.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=44461.043Y=72824.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"castle-like structure on top\", \"filename\": \"X=40836.688Y=-102903.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=48365.348Y=-368825.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=38136.426Y=-334341.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=42713.988Y=77441.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=41530.129Y=71144.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=51970.824Y=77420.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=11778.670Y=62860.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=49560.391Y=-14590.190Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=45607.641Y=-101417.938Z=544.410.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=33672.688Y=-286152.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=47369.336Y=-112781.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=35119.020Y=-23851.832Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=41421.758Y=83382.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=1567.949Y=-264702.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=42209.379Y=81961.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=29723.186Y=-54271.941Z=65.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-57861.969Y=-161053.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=5072.043Y=75966.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=42526.055Y=-115484.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=40507.547Y=-115450.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=45607.664Y=-181313.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"grid-like facade\", \"filename\": \"X=50414.398Y=6572.187Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"geometric\", \"feature\": \"angled facade\", \"filename\": \"X=38876.488Y=48563.230Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=167416.328Y=-135079.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=42526.066Y=-111171.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=30970.648Y=-83179.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=40836.406Y=-73337.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=1775.990Y=-390707.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=50360.320Y=86801.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=47649.070Y=-373433.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=53321.688Y=8100.286Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=47138.031Y=-40353.934Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=13739.195Y=-270446.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=51335.199Y=90697.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=43466.457Y=-186863.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with a large central opening\", \"filename\": \"X=40557.988Y=-161449.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=36110.926Y=-161238.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=33756.660Y=-17232.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6869.864Y=-287010.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=51904.289Y=50227.926Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=48274.656Y=-12013.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=41811.168Y=-365822.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=168896.609Y=-15671.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=40420.867Y=76419.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=41612.629Y=-111361.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-163765.156Y=-114925.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=49421.203Y=82545.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=47058.797Y=-43322.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=45394.828Y=-327551.312Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=44448.781Y=-111381.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=43582.598Y=50287.035Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11791.579Y=-203989.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=135863.578Y=40286.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=55311.266Y=-187691.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=44413.387Y=77511.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=11011.707Y=-106996.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=6460.518Y=-105505.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=46326.629Y=-19150.002Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and imposing\", \"filename\": \"X=44162.242Y=-161069.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"high-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed facade\", \"filename\": \"X=32393.543Y=-346378.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=43473.320Y=27200.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=46085.621Y=-91695.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=46650.184Y=-75584.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open structure\", \"filename\": \"X=39952.512Y=-67368.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=43583.613Y=-83453.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=47348.613Y=-186873.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=39777.629Y=-185742.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=43400.934Y=-115262.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=39713.684Y=-198563.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"slender\", \"feature\": \"tapered top\", \"filename\": \"X=43241.113Y=-268694.719Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=42178.039Y=68509.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=50383.480Y=37865.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=39952.520Y=-176585.625Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=25200.814Y=-102460.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=42396.953Y=-10511.207Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=4839.624Y=-186547.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=5089.074Y=-67368.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=3947.906Y=-84601.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162577.828Y=-103928.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=14023.581Y=-56955.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"tall with many windows\", \"filename\": \"X=5365.414Y=-171503.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=41343.406Y=-151856.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=44398.078Y=-362946.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-26288.559Y=-234542.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=35985.785Y=-216997.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"classical architectural elements\", \"filename\": \"X=45607.648Y=-65772.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"tapered top\", \"filename\": \"X=23330.750Y=-304865.719Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=44013.520Y=-41027.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=51075.629Y=12280.629Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=45696.805Y=70528.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=45607.668Y=-177969.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=46650.172Y=-105128.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=41612.617Y=-115485.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=45623.977Y=-111138.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=39952.496Y=-106647.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and slender with a grid of windows\", \"filename\": \"X=53764.141Y=-291088.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=45306.984Y=-162860.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=46085.609Y=-166977.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-622.875Y=14700.526Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=50061.930Y=-238657.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=49730.285Y=54494.652Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=9743.712Y=27670.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=45683.562Y=60461.434Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=49401.484Y=39784.121Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=46102.906Y=-6824.501Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=40730.734Y=-363675.062Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=31907.432Y=-207476.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=14116.007Y=58103.004Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=51812.523Y=-340146.812Z=695.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=28572.326Y=-29869.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=45666.020Y=-253263.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=4612.280Y=-181313.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=39848.562Y=46639.926Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=43067.863Y=79905.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=10933.600Y=64430.152Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=45549.523Y=22118.943Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=38340.148Y=56687.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=36823.723Y=-238883.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=54855.684Y=-366983.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-105939.523Y=-9389.168Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=39929.195Y=81975.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=45677.391Y=36142.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=43854.305Y=10012.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=51335.418Y=-28053.918Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"pointed top\", \"filename\": \"X=45756.605Y=46020.309Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6131.199Y=69716.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=4612.288Y=-54062.598Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=11960.029Y=-28872.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=124421.984Y=-236968.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid windows\", \"filename\": \"X=12353.469Y=-63512.793Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=39374.238Y=-21683.682Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=48114.277Y=57961.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=49336.121Y=-260636.062Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=28059.982Y=3966.901Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=2703.957Y=74009.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=3118.449Y=-264039.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=42074.070Y=77114.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=49126.984Y=-23719.650Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=54204.262Y=-249828.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=7882.415Y=-318025.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-1564.623Y=76749.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=49599.715Y=15166.515Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=48262.941Y=42027.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with arched windows\", \"filename\": \"X=44063.371Y=78123.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=38554.652Y=33609.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=37681.605Y=54212.098Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=109778.188Y=-55961.262Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched bridge\", \"filename\": \"X=44501.332Y=-46487.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arches\", \"filename\": \"X=62013.074Y=-152307.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=55688.207Y=-214456.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=52459.160Y=-207362.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=54644.445Y=-153696.922Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=58649.297Y=-67524.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=5089.016Y=-41726.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"pointed arches\", \"filename\": \"X=58472.484Y=-356830.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52823.297Y=-66303.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=56736.352Y=-77232.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=57735.863Y=-67583.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52824.199Y=-68325.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-19982.697Y=71769.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=52824.207Y=-71141.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52955.770Y=-75002.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-130447.305Y=-150782.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=55445.422Y=-62315.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=61729.418Y=-75002.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=61648.035Y=-78102.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=57312.977Y=-62364.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=56861.012Y=-67451.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-49388.820Y=-293815.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=4402.779Y=-270100.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=45302.301Y=-17215.043Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=58794.773Y=-112474.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=3507.205Y=-168559.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=50136.836Y=-361606.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=60649.250Y=42695.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52812.242Y=-77395.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=54116.285Y=-81580.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"tall columns\", \"filename\": \"X=54122.438Y=-83492.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=87652.797Y=-47038.996Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=55050.836Y=-140413.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=52459.164Y=-158970.578Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=60306.355Y=-196969.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-74915.281Y=-318324.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=34623.023Y=-246392.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=5416.457Y=-350143.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=93270.594Y=72888.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, with many windows\", \"filename\": \"X=52834.984Y=-323760.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=54831.293Y=-47210.363Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=58398.262Y=80336.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=58679.902Y=-39484.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=60306.355Y=-148951.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=55805.457Y=-25866.834Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=13542.047Y=73646.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=57784.215Y=-71855.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=63740.246Y=13970.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=59754.387Y=-67323.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=59896.953Y=-43531.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=62962.535Y=-362118.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-383.907Y=70265.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=55688.219Y=-150343.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=60306.348Y=-213006.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=47790.297Y=-170333.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Black\", \"size\": \"high-rise\", \"shape\": \"Cuboid\", \"feature\": \"Grid-like facade\", \"filename\": \"X=60973.195Y=-187691.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=54311.645Y=56022.730Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=59754.387Y=-62100.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=1103.547Y=-257163.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=58918.719Y=46091.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=55050.848Y=-207336.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"many windows\", \"filename\": \"X=52459.156Y=-140427.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=63333.355Y=563.907Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=61648.012Y=-67811.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=61648.023Y=-71141.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=61504.062Y=-179753.312Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=50907.199Y=-17179.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=12268.728Y=61842.285Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=59201.629Y=18319.432Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=9861.699Y=-358430.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=64658.895Y=37940.383Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=50457.547Y=-26317.566Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \" Missing middle floors\", \"filename\": \"X=61665.992Y=-62434.105Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, multiple stacked sections\", \"filename\": \"X=59421.379Y=-216559.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=59841.039Y=-71891.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=52766.656Y=-72074.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=61639.234Y=56176.184Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-3486.420Y=-259446.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"curved\", \"feature\": \"striped facade\", \"filename\": \"X=64587.883Y=-270400.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=64911.230Y=-15969.299Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=61619.031Y=-65441.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52816.930Y=-65223.480Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Floating structure with large windows\", \"filename\": \"X=-35922.578Y=-190452.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated sections\", \"filename\": \"X=60871.605Y=-205284.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=66012.758Y=82592.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=10021.951Y=76474.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=57987.781Y=87081.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"skyscraper with a distinctive angular top\", \"filename\": \"X=60917.406Y=-354815.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"parking structure\", \"filename\": \"X=67030.766Y=-254048.156Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=4856.407Y=64147.441Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=52819.094Y=-67457.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=61806.145Y=-74142.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=52282.734Y=-47207.129Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=60403.418Y=-115799.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=59701.152Y=-88884.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"grid-like facade\", \"filename\": \"X=53947.352Y=45329.121Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=55416.078Y=53761.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=61799.461Y=77791.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=153753.719Y=-268875.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=62013.062Y=-216550.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=53600.641Y=-55961.262Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=58784.016Y=-142401.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=54827.734Y=-96590.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=38756.234Y=-56002.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=61648.012Y=-61428.918Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=54717.828Y=-71974.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-56616.133Y=-286685.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=60650.812Y=-133830.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=54165.883Y=-162526.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52617.980Y=-214473.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=61739.430Y=-39464.543Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=52796.242Y=-76293.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=62013.062Y=-53701.051Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=54147.395Y=-86527.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan and brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-11900.909Y=-314500.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=52824.188Y=-61428.926Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=61854.250Y=-142388.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=59127.699Y=64045.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=2509.121Y=-106595.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=63107.457Y=-106647.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"asymmetrical\", \"feature\": \"angled facade\", \"filename\": \"X=56432.184Y=51860.918Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=24846.035Y=-385279.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=67030.766Y=-254048.156Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=95807.547Y=-223577.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=4612.290Y=-177969.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"skyscraper\", \"filename\": \"X=57801.336Y=-161067.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=54165.895Y=-203818.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=67346.453Y=-350765.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=55842.145Y=-179753.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=57172.145Y=-14121.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=11384.954Y=73693.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=107948.984Y=-149270.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=53510.969Y=-102893.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=44084.961Y=-190560.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=67039.305Y=4250.616Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=54717.828Y=-77369.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=55822.918Y=-77114.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=49982.109Y=64549.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=61688.062Y=-63458.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=59421.379Y=-53724.488Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=52808.484Y=-63458.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=52618.617Y=-64361.477Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=52757.887Y=-74142.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=82659.641Y=-222067.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=55813.148Y=-67362.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=54644.422Y=-52105.395Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=54635.047Y=76157.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=61646.910Y=-76612.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=46151.891Y=-144167.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and slender with numerous windows\", \"filename\": \"X=64902.227Y=-312844.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=13405.954Y=98112.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=43731.902Y=-371186.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=54240.730Y=-44209.996Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=59421.395Y=-152301.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=12228.510Y=72053.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=14023.573Y=-86687.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=71004.508Y=59314.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=128489.672Y=-55844.074Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=76860.516Y=74754.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=54650.133Y=-9172.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=54648.645Y=-23587.873Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=61575.426Y=4014.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-1087.081Y=-349249.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=75352.469Y=62625.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"many windows\", \"filename\": \"X=-120166.945Y=-60840.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=74405.625Y=50533.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=6435.780Y=-101395.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-113360.352Y=-233652.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=12914.895Y=-101382.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=54189.539Y=-272087.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=10969.055Y=-105723.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=65940.594Y=47490.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=63685.387Y=-24232.387Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"slender\", \"feature\": \"tapered top\", \"filename\": \"X=75207.461Y=-331287.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=55961.836Y=-11870.223Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=6037.052Y=-114185.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=54759.625Y=16077.311Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=60329.387Y=58788.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6498.917Y=-104280.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=2985.977Y=79970.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=75060.547Y=-235718.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=55677.867Y=41932.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=108223.508Y=15256.423Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=60453.352Y=-319878.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=73178.234Y=-158023.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=75071.859Y=-154080.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=71035.031Y=-153284.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=75019.523Y=-157786.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=72082.898Y=-148973.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=66198.609Y=-158016.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=75063.734Y=-152339.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=68055.016Y=-158032.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=71159.703Y=-162550.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=75109.797Y=-161006.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=71994.797Y=-209939.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=68068.281Y=-196969.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=2885.164Y=63845.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=76947.727Y=42831.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=71757.383Y=-65260.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=64115.309Y=61148.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=70412.891Y=-31337.580Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=70472.250Y=-46321.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=74705.680Y=-130492.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=70437.758Y=-130491.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=19155.822Y=-297089.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large central opening\", \"filename\": \"X=59306.816Y=11711.225Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=55793.273Y=354.331Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7761.103Y=-175909.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=61930.535Y=-356954.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=66947.727Y=-42212.590Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=72750.039Y=-224896.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=73251.609Y=-187781.109Z=1345.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=181792.609Y=-160129.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=78017.953Y=79419.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=10380.295Y=-298028.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=61348.121Y=-112507.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"tapered top\", \"filename\": \"X=77111.438Y=-295643.312Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-7703.683Y=-208211.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=72671.023Y=-53180.770Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=72671.008Y=-57212.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=73264.867Y=-153143.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=73730.148Y=-166977.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=73730.148Y=-170333.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with large windows\", \"filename\": \"X=68068.250Y=-168366.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-2361.097Y=-100939.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=73730.164Y=-196969.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=71035.031Y=-149198.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=69246.750Y=-153316.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130694.555Y=-28841.004Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=78608.383Y=55069.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=58490.812Y=-332757.938Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=156730.906Y=-166977.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern office building with large windows\", \"filename\": \"X=73226.477Y=-39999.629Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-91833.008Y=-249768.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=13525.830Y=-355992.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=70284.852Y=-157975.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=75071.844Y=-148264.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=72082.898Y=-153437.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=15159.268Y=-250933.281Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=69921.727Y=17091.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=11818.094Y=72901.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open facade\", \"filename\": \"X=80237.438Y=23712.449Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=73046.812Y=53201.695Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=67965.109Y=-299809.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=66937.086Y=-39464.730Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=68141.656Y=-153325.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-16739.184Y=-18339.471Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=73178.234Y=-162619.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=71159.703Y=-157736.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=-182417.453Y=-148636.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"unique architectural design with a large cut-out in the center\", \"filename\": \"X=7819.291Y=-366220.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-72783.344Y=31890.082Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143350.328Y=-301214.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=115427.359Y=-114445.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=70057.891Y=-42212.641Z=620.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=59821.277Y=-122609.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-4471.022Y=86078.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=76844.922Y=58452.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=66965.078Y=-141587.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=75086.711Y=-153291.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=74249.945Y=-92729.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=69236.992Y=-162459.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=72073.141Y=-157768.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=68503.883Y=-223920.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=59827.789Y=-139043.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=66966.766Y=-138846.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=66220.695Y=-151158.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-3885.116Y=34864.512Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=86219.625Y=-153456.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55002.820Y=-125809.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=7195.638Y=55385.414Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=74364.273Y=-214248.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=64091.750Y=51004.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=6385.106Y=-352766.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=77298.703Y=-500.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=66392.031Y=-204284.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=66190.883Y=-159012.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=75089.258Y=-159012.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=68053.562Y=-58024.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=68141.656Y=-149135.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=72073.141Y=-162541.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=8289.993Y=59842.848Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=67351.500Y=-46335.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-95777.758Y=-299088.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open atrium\", \"filename\": \"X=72077.852Y=-60799.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=75143.062Y=-224974.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=10408.493Y=-253863.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=66792.914Y=32786.980Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=68188.461Y=52122.145Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=78773.109Y=-277253.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=66194.289Y=-152686.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=70160.180Y=-153256.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=72048.078Y=-4653.316Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=70057.945Y=-39408.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=75079.172Y=-150047.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=75071.844Y=-163141.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"cylindrical\", \"feature\": \"tapered top\", \"filename\": \"X=-155600.031Y=-254086.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=74396.969Y=-217974.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=68068.250Y=-171722.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=73730.148Y=-178792.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=68068.266Y=-178792.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-140830.047Y=-131297.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=73765.781Y=-84133.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-144373.266Y=-122446.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-3077.277Y=-341475.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=71910.250Y=-342653.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=69732.461Y=23607.127Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=80046.008Y=-23218.775Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=126323.227Y=-91519.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=67459.031Y=-215164.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=1018.994Y=67497.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=72005.875Y=55244.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=7127.585Y=-233278.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=75059.359Y=-140212.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=70284.852Y=-162440.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=66922.891Y=-144016.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=71308.742Y=-139421.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=68913.531Y=-244160.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=75202.164Y=-151158.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=75071.961Y=-149115.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=69236.992Y=-157698.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=66269.516Y=-160200.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=75045.773Y=-143847.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=66248.031Y=-149196.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=66248.016Y=-163141.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=66191.883Y=-161999.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=71196.086Y=-215205.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=3932.012Y=48707.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=11114.959Y=-258017.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=76052.859Y=-24709.879Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=69246.750Y=-148919.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, grid-like facade\", \"filename\": \"X=68442.500Y=-316491.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=66447.750Y=-136010.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=68319.641Y=63225.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=68527.930Y=-24188.846Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=2075.786Y=-344308.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=10428.426Y=-279961.281Z=1345.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=6564.890Y=88884.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated on stilts\", \"filename\": \"X=66832.055Y=57188.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=77598.984Y=28939.557Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=140024.891Y=-101328.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=70290.156Y=-1203.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=74176.961Y=19260.102Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two stacked cubes\", \"filename\": \"X=84255.109Y=29557.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=71670.000Y=82937.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=1576.543Y=73278.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=88414.383Y=-76297.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-115529.188Y=-233637.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-89291.758Y=-292547.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=88803.188Y=-72465.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-36889.695Y=-217666.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=72865.867Y=37113.562Z=870.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=494.147Y=-187189.797Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=86647.914Y=-222950.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-10540.723Y=-124713.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=73918.781Y=-348160.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=90430.781Y=49052.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9654.718Y=-139508.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=65469.008Y=21455.131Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=67608.484Y=-241455.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=80471.844Y=-153965.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=80428.992Y=-148937.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=87450.016Y=-139595.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=87324.711Y=-153119.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=80313.250Y=-150967.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=86219.625Y=-125556.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=87324.711Y=-122020.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=87324.711Y=-125701.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=89218.336Y=-121426.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=80495.789Y=-140855.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=83470.547Y=-143717.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"arched windows\", \"filename\": \"X=-215828.906Y=-125791.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-82393.727Y=-60661.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=89273.750Y=-139803.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=80394.039Y=-141956.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Large windows\", \"filename\": \"X=80425.102Y=-143755.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=83529.281Y=-39479.504Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=82365.453Y=-143673.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=83490.906Y=-179987.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96434.633Y=-196969.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=79629.391Y=-223984.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=80601.102Y=-163623.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=17905.096Y=65083.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-2195.548Y=60733.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=12039.668Y=-345220.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"classical architectural elements\", \"filename\": \"X=84200.711Y=-167044.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=7516.663Y=-247853.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=93451.453Y=-3318.863Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=80916.891Y=-179929.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=74294.938Y=-21259.754Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=84431.336Y=-149181.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=72506.570Y=-46708.246Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=82271.094Y=-185109.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=87894.375Y=-198174.172Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=82933.367Y=-206338.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-91418.336Y=-354867.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=82302.219Y=-43568.309Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"overhanging top floor\", \"filename\": \"X=82674.742Y=-131411.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=84383.984Y=-143676.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=87360.141Y=-234227.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=82659.656Y=-225045.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=80606.289Y=-214476.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=80413.359Y=-139803.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-8023.167Y=-289437.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=85424.250Y=-273880.375Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=12543.791Y=57131.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-119771.570Y=-352286.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple windows\", \"filename\": \"X=66332.102Y=-208697.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=83595.898Y=-158053.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=79248.453Y=63804.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-103514.234Y=-180147.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=80471.836Y=-121426.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=85306.188Y=-153436.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=80512.203Y=-142762.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-1042.432Y=71534.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=4807.359Y=36469.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=84383.984Y=-139301.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=86219.625Y=-148926.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=89431.711Y=-177969.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=80606.312Y=-159306.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=117010.672Y=-224721.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=82925.000Y=-114425.000Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=81795.828Y=-176585.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=88506.664Y=-169216.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=7152.191Y=32756.982Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=77444.352Y=10622.208Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=80528.500Y=-39471.418Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=80613.125Y=-322110.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=85406.461Y=-133454.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=83383.469Y=-149023.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=86219.617Y=-122131.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=83438.633Y=-153352.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=82745.773Y=48401.910Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=142932.500Y=-153382.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=82459.969Y=-233647.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-18575.635Y=-32111.436Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=83470.547Y=-139286.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=85306.188Y=-149048.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=89221.812Y=-123537.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=86674.414Y=-143503.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=86940.055Y=51141.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=81795.836Y=-134914.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"tapered top\", \"filename\": \"X=73139.398Y=-266063.062Z=569.943.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=12592.246Y=-284208.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, with a grid-like facade\", \"filename\": \"X=96465.773Y=-282076.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=82390.773Y=-125584.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=84883.297Y=-122042.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=85258.836Y=-143661.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=7978.328Y=-122293.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=79498.141Y=75895.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=89218.344Y=-138761.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=32675.338Y=-72090.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=85405.977Y=49732.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=3044.435Y=-345903.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-222473.000Y=-209677.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=82240.164Y=-149174.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=99982.711Y=27288.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"box\", \"feature\": \"glass windows\", \"filename\": \"X=79927.383Y=52462.004Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=3484.595Y=62590.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=81117.172Y=60783.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=96730.008Y=-153259.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=80471.836Y=-126493.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=80468.039Y=-124485.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=80475.961Y=-149939.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=87324.711Y=-148959.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-219822.266Y=-122200.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=87402.453Y=-83392.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"octagonal\", \"feature\": \"ornate detailing\", \"filename\": \"X=87139.344Y=-86468.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=86306.695Y=-139335.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in the center\", \"filename\": \"X=95617.641Y=-190452.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=88455.727Y=-133447.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=3187.976Y=-260435.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=97826.008Y=-49607.801Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=97777.867Y=-153436.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=87897.445Y=-40454.707Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=99566.172Y=-180933.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass facade\", \"filename\": \"X=97777.883Y=-181013.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=95186.812Y=-195188.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=82271.109Y=-199640.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=172068.188Y=-113556.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=83369.484Y=-327328.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=88331.352Y=-97165.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=88372.000Y=-94297.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=83771.102Y=-105322.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=88236.805Y=-101164.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"water pool in front\", \"filename\": \"X=88218.133Y=-104031.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=78943.523Y=-73615.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=125285.812Y=66332.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=88556.617Y=-171652.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=86382.445Y=-177964.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines and ornate detailing\", \"filename\": \"X=86229.562Y=-214476.859Z=444.990.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=87292.008Y=-162409.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=80207.180Y=-68955.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=102566.477Y=-151724.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=83383.469Y=-125763.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=98229.836Y=-148974.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=85306.188Y=-125886.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=80467.578Y=-122425.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=89194.414Y=-152536.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=85258.836Y=-139643.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=102564.867Y=-148115.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=95711.961Y=-167158.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-58049.852Y=-278547.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=80100.766Y=-131369.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=89164.414Y=-142762.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=87419.062Y=-129901.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=96362.281Y=-149163.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"elevated structure\", \"filename\": \"X=94480.094Y=-76797.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=86142.703Y=-287971.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=89594.664Y=-86537.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=92139.250Y=-303733.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=87826.203Y=-240525.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=92708.492Y=44523.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=102502.992Y=12341.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevator shaft\", \"filename\": \"X=19675.178Y=-51110.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=51071.570Y=-265461.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"tapered\", \"feature\": \"stepped back design\", \"filename\": \"X=-86400.078Y=28739.191Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=62529.449Y=30634.982Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=395.974Y=55647.191Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple floors\", \"filename\": \"X=83668.891Y=-346545.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=84358.578Y=-312386.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=70622.656Y=-112958.688Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=393.312Y=72581.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=84864.961Y=45915.590Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=99151.211Y=-55629.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=96856.617Y=-264794.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=11499.569Y=-135407.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=85365.258Y=79443.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=99017.562Y=-77163.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=86382.820Y=-6920.513Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93761.742Y=-170617.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=99653.203Y=-171219.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=98605.344Y=-171281.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93842.836Y=-152205.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=93818.336Y=-148115.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93764.297Y=-153067.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-33398.445Y=20901.736Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93811.133Y=-168118.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=102119.812Y=-186576.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=96362.289Y=-176883.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=99566.156Y=-148904.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=102584.617Y=-149076.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=107756.297Y=-140741.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=98153.383Y=-166996.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=93789.469Y=-176782.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=98652.734Y=-181015.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=100671.258Y=-176792.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=89218.336Y=-149040.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=2201.482Y=52103.691Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=96379.422Y=-93570.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open floor plan\", \"filename\": \"X=93994.195Y=-95009.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=96942.414Y=-98290.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=95737.305Y=-153123.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=4943.203Y=59870.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=101126.578Y=-98290.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=100003.836Y=-67334.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with multiple levels\", \"filename\": \"X=95789.117Y=-67334.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=95142.344Y=-86992.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with arches\", \"filename\": \"X=101284.617Y=-190075.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple floors\", \"filename\": \"X=107888.891Y=-327858.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=86882.852Y=24351.037Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=101516.344Y=-123923.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=100765.688Y=-115807.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=100171.555Y=-167250.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-112967.117Y=-336297.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=95142.328Y=-122901.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=98905.641Y=-106547.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=4022.846Y=-354139.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=107756.320Y=-214293.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-87672.828Y=-110043.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=94427.609Y=-73772.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=99630.453Y=-195251.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=10006.678Y=66187.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Grid-like facade\", \"filename\": \"X=84981.430Y=14462.546Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=93818.367Y=-181786.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=122790.562Y=-57556.934Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=97940.594Y=-238204.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=102778.219Y=-161055.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=93753.828Y=-178607.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102564.867Y=-166212.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=87894.367Y=-190120.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=111441.445Y=-5201.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with multiple levels\", \"filename\": \"X=95202.023Y=-62583.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"skyscraper with large windows\", \"filename\": \"X=-190279.828Y=-115651.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=104154.422Y=76714.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=6206.643Y=73623.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=99728.953Y=-83513.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=88556.633Y=-206338.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=93767.547Y=-150277.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=93828.805Y=-171593.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=137083.344Y=-148636.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=95142.344Y=-162526.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=100671.258Y=-181047.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=89218.344Y=-153965.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93808.078Y=-177656.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=98652.719Y=-153413.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=90832.852Y=-258775.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=94346.219Y=-102546.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=7470.773Y=-239903.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=93026.453Y=-268859.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=8424.824Y=30180.605Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=93810.477Y=-180900.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93818.344Y=-175852.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=93818.344Y=-154015.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=91995.352Y=-341440.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=87870.688Y=-44027.910Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=89990.023Y=-244771.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=103643.031Y=40627.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=104713.797Y=-13233.113Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=93991.312Y=-97961.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=96837.406Y=-158929.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=94475.234Y=-71456.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=89218.344Y=-143965.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=97730.492Y=-171469.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-6128.759Y=97063.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=88125.719Y=-334690.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=95711.961Y=-171350.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=8659.446Y=-343662.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=87834.688Y=-158091.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=96730.016Y=-180831.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=79883.719Y=-339179.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=95194.102Y=-106556.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=89862.250Y=-82682.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=11666.052Y=-341074.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=99440.844Y=-62607.801Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=10750.200Y=-127110.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=95698.742Y=-234205.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=97630.250Y=-186964.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=102520.609Y=-150277.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=88563.320Y=-166468.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=93744.422Y=-169344.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=87419.102Y=-181313.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=87894.375Y=-194630.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=6938.619Y=-253717.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=96817.055Y=-166939.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=93447.281Y=-83136.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=95455.352Y=21986.682Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-97802.922Y=-333442.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"circular\", \"feature\": \"tall with large windows\", \"filename\": \"X=-86958.766Y=-123856.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-101918.500Y=-322147.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96450.602Y=-74212.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-100158.727Y=-316688.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-97579.086Y=-312656.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-89478.727Y=-327998.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-108446.188Y=-369248.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-95174.602Y=-67154.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-42473.305Y=-140427.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108823.875Y=-298335.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-90518.883Y=-300255.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-128079.625Y=-277496.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-100961.625Y=-364530.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-88800.992Y=-318798.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96915.156Y=-313474.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-102511.258Y=-375174.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-100219.070Y=-323576.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-91275.062Y=-76710.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-106820.164Y=-296446.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-97577.688Y=-224530.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-95895.023Y=-130792.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base with arches\", \"filename\": \"X=-109203.188Y=-286577.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-70497.172Y=-217303.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-109179.812Y=-279924.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107596.070Y=-302676.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-103449.180Y=-74426.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=35307.355Y=-367523.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=-103581.688Y=-134803.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-108569.359Y=-131412.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113252.641Y=-123003.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-107694.852Y=-139673.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-105600.180Y=-121529.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101680.375Y=-185956.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101688.062Y=-190843.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-102002.359Y=-113797.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-95685.367Y=-178338.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101726.219Y=-186794.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108597.305Y=-132995.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-104686.789Y=-130153.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-101741.648Y=-189691.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101671.750Y=-123419.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-40206.512Y=-65876.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-103581.688Y=-130223.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=-91389.734Y=-181000.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-109987.453Y=-367934.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-89356.125Y=-341413.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-96448.172Y=-235521.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-8902.379Y=-321876.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107076.797Y=-370488.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-94556.484Y=-99723.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-111371.031Y=-294764.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-95498.781Y=-328495.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-115096.852Y=-121534.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-109965.531Y=-247327.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass facade\", \"filename\": \"X=-102069.797Y=-244923.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-105302.492Y=-242577.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-95842.141Y=-261383.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-28446.301Y=-317541.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110225.648Y=-299966.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=155850.359Y=-14276.487Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-108075.297Y=-45800.523Z=618.404.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-101351.648Y=-315352.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-101134.742Y=-84401.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-106273.523Y=-303995.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-88741.898Y=-79870.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"arched windows\", \"filename\": \"X=-104925.344Y=-345249.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with many windows\", \"filename\": \"X=-15952.792Y=-44032.859Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-414.379Y=-32094.807Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-104500.875Y=-361244.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-106447.289Y=-369993.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-107258.156Y=-196933.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-89500.000Y=-332511.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-102591.867Y=-151166.875Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"stepped design\", \"filename\": \"X=-107297.172Y=-151166.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-106429.734Y=-159023.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-107297.141Y=-199605.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-114500.094Y=-194333.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113254.688Y=-204370.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-117812.406Y=-215810.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-102082.461Y=-291613.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-90847.195Y=-235315.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-101211.945Y=-301788.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-98700.352Y=-308373.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7712.237Y=-177228.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-1028.897Y=-57556.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-7904.297Y=-56737.145Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=601.257Y=-90804.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-10759.815Y=-176632.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-100977.477Y=-370963.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-109034.570Y=-362858.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113171.242Y=-208193.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-107680.250Y=-221942.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=-102554.727Y=-160481.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-99173.148Y=-322682.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-102591.867Y=-199308.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-113236.570Y=-205281.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-1618.504Y=-348279.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-102702.219Y=-305968.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-90806.703Y=-345442.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, with a flat roof\", \"filename\": \"X=-106451.742Y=18252.746Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-105594.625Y=-366396.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-110941.000Y=-354380.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-104302.664Y=-287237.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-10335.068Y=-387583.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-15205.599Y=-387705.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-115831.484Y=8032.319Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-206206.938Y=-150541.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-97688.609Y=-321028.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101087.227Y=-324416.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-101688.062Y=-184849.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-104686.742Y=-125799.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108598.648Y=-126337.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-104686.789Y=-135024.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-106700.414Y=-130341.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7761.096Y=-212681.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-103463.867Y=-306728.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-106641.758Y=-316337.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108445.742Y=-224751.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-115548.828Y=-130228.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-95292.078Y=-378906.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-111433.273Y=-273359.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-14300.260Y=97326.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-112254.125Y=-295485.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical facade\", \"filename\": \"X=-112930.594Y=-348060.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-137332.422Y=-258336.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-104920.828Y=-368475.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-2707.759Y=87064.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate facade\", \"filename\": \"X=-112877.828Y=-296136.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-101880.375Y=-376106.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=1520.855Y=66522.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Uniform windows and doors\", \"filename\": \"X=-109547.398Y=-352850.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113198.633Y=-181739.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-115322.227Y=-180871.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-106899.367Y=-289918.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramidal\", \"feature\": \"tapered top\", \"filename\": \"X=-112875.125Y=-3238.500Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113565.867Y=-336855.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108480.227Y=-358389.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-114715.883Y=-309718.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-108339.219Y=-104642.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-106700.391Y=-121492.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-115774.133Y=-125783.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113171.328Y=-134565.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113140.328Y=-131611.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-10936.919Y=-77408.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113860.367Y=-363705.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110095.531Y=-327892.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated water tank\", \"filename\": \"X=-101407.914Y=-66965.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-111511.023Y=-334829.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7998.257Y=74984.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-96843.570Y=-149735.344Z=569.943.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-114680.031Y=-317957.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110807.688Y=-360717.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-109965.531Y=-252034.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-114628.562Y=-252078.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-102069.797Y=-254336.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-107627.438Y=-249652.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-113579.258Y=-263263.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-14941.805Y=-121919.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-105682.156Y=-369206.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-108598.633Y=-190843.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-104263.602Y=-185516.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-108628.492Y=-122221.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated balconies\", \"filename\": \"X=-103581.641Y=-125554.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108570.359Y=-130590.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-114115.281Y=-141248.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-101167.109Y=-344603.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-115934.555Y=-359418.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-104950.906Y=-325602.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101903.766Y=-305255.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105742.773Y=-324831.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-109004.773Y=-308018.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-9017.446Y=-328894.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"twin towers\", \"feature\": \"grid-like facade\", \"filename\": \"X=-110558.969Y=-8941.012Z=544.984.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-113234.609Y=-316489.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"arched entrance\", \"filename\": \"X=-107488.914Y=-297176.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118298.422Y=-223182.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-106101.633Y=-295786.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-10397.114Y=-341073.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108158.328Y=-338003.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-101667.859Y=-365167.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108365.016Y=-351680.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=134989.891Y=-151993.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-104017.508Y=-233688.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-101358.578Y=17729.166Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-111258.094Y=16453.408Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-108598.648Y=-120667.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105600.219Y=-135020.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-109389.547Y=-368628.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-111379.039Y=-266678.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105848.305Y=-329136.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113980.289Y=-317264.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-110097.242Y=-319857.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-101688.031Y=-120667.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-102989.562Y=-139043.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=-107694.836Y=-181000.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113159.375Y=-180405.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-102286.992Y=-294552.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-109387.906Y=-292567.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-106320.438Y=-343829.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-111314.430Y=-366542.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-114260.594Y=-346653.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-107647.719Y=-371127.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-108867.734Y=-312237.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-28663.264Y=-381457.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-102989.547Y=-203521.672Z=569.943.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-102539.953Y=-197304.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113107.102Y=-215810.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107297.156Y=-212674.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-102591.867Y=-157800.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107053.258Y=-336938.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-105908.570Y=-318346.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=160412.516Y=-263936.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7761.096Y=-202964.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-100634.930Y=-295715.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-10759.810Y=-139311.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7761.095Y=-135671.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-11673.245Y=-153413.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-15238.346Y=-225960.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-11673.245Y=-143755.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7682.884Y=-215409.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-7777.304Y=-180690.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-11673.251Y=-176700.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-102653.539Y=-300375.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-112290.477Y=-254403.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107627.438Y=-244946.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-105302.492Y=-247284.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-105302.492Y=-261404.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=218.685Y=-106528.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-107297.156Y=-216218.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=-103844.625Y=-223184.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-104263.602Y=-189960.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-115774.180Y=-208277.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113174.977Y=-206151.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-115322.219Y=-134839.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"boxy\", \"feature\": \"open center\", \"filename\": \"X=-107734.469Y=-141881.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113254.297Y=-177840.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-111286.195Y=-326442.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-113667.102Y=-148951.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=144068.469Y=-224447.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-108598.633Y=-184849.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108213.609Y=-364039.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-106653.430Y=-309907.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-105020.227Y=-328533.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-94262.344Y=-100599.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-1028.896Y=-72090.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-115271.805Y=-318685.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-90712.914Y=-62690.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-105320.336Y=-297585.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108795.164Y=-341328.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-112342.039Y=-365353.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"geometric patterned windows\", \"filename\": \"X=-115723.266Y=-98272.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-32513.785Y=77915.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-43606.062Y=-84039.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-104022.781Y=-367502.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113198.648Y=-126622.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105599.930Y=-185428.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108616.234Y=-189691.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-101667.648Y=-131109.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113198.633Y=-129603.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113198.648Y=-120804.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-106475.070Y=-134725.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open arches\", \"filename\": \"X=-103856.984Y=-142393.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101736.641Y=-134498.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-106705.008Y=-190185.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-107751.156Y=-178409.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-103581.656Y=-121365.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-101700.883Y=-124483.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113175.828Y=-133496.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108546.156Y=-188081.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-104686.742Y=-121438.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-101664.578Y=-121919.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-109264.383Y=-339107.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-104074.000Y=-299154.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-109532.836Y=-312846.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-104941.359Y=-354823.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-108544.680Y=-125307.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107110.719Y=-323670.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large open space underneath\", \"filename\": \"X=-107349.742Y=-232552.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-103942.953Y=-279148.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108102.273Y=-297733.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113310.242Y=-289689.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-106441.516Y=-235259.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=151874.828Y=-2821.290Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open entrance\", \"filename\": \"X=-7760.157Y=-83779.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-102592.453Y=-292273.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-105297.953Y=-360037.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-104502.258Y=-314214.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113880.969Y=-310365.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-103228.078Y=-346610.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-107803.078Y=-357758.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines and setbacks\", \"filename\": \"X=143801.219Y=-249342.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-116654.336Y=-246057.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-103447.836Y=1795.593Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-111703.086Y=-355059.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-103834.719Y=-340535.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105276.453Y=-315084.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-96466.000Y=-160785.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-109188.367Y=-301453.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-109380.445Y=-56006.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, with a clock tower\", \"filename\": \"X=-97445.867Y=-9983.973Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=72053.914Y=-205526.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-104431.273Y=-339895.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-101383.336Y=-283865.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-105197.203Y=-339365.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-114431.875Y=-364342.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-101397.445Y=-304438.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110356.445Y=-293518.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-115842.359Y=-356147.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-104146.031Y=-307400.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-106451.734Y=21521.230Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107294.164Y=-330557.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-103226.859Y=-362498.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-103977.945Y=-293662.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-100775.281Y=-275981.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-115848.852Y=-84516.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated on stilts\", \"filename\": \"X=-106562.453Y=-283936.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110874.984Y=-350017.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-101358.016Y=-290938.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=-105247.922Y=-14089.326Z=620.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110140.891Y=-340069.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-109680.844Y=-362171.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108720.773Y=-329372.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-113150.484Y=-310872.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113714.625Y=-296888.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-115333.703Y=-350401.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-105903.055Y=-338510.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-111956.227Y=-304741.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-109965.531Y=-256741.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-112290.477Y=-249696.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113278.812Y=-363161.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-110656.883Y=-340538.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113840.531Y=-357246.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-127027.078Y=-283361.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-103273.195Y=-292916.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107872.094Y=-317631.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-104571.852Y=-108697.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-8348.970Y=63179.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-115848.648Y=12560.129Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110930.922Y=-300615.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107943.062Y=-331272.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-114988.672Y=-344880.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-112213.109Y=-335651.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-160276.922Y=-46249.426Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=-101506.547Y=-60681.965Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101688.070Y=-129658.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-102970.055Y=-206193.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=14122.421Y=-243192.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-21932.496Y=-364421.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-112700.234Y=-311713.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade\", \"filename\": \"X=-42510.168Y=-90776.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-114102.438Y=-187781.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-102591.867Y=-213006.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-114059.461Y=-139357.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-110247.484Y=-353550.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=-102591.883Y=-216550.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108590.906Y=-134498.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105600.180Y=-125734.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-108618.508Y=-123804.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-114500.094Y=-199640.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-101651.172Y=-133674.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108598.633Y=-135528.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-113023.508Y=-178916.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113150.586Y=-207201.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=29990.203Y=-111146.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113181.578Y=-124446.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-108563.133Y=-186301.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-12225.419Y=-346379.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113198.656Y=-176096.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-101688.070Y=-135528.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-101696.828Y=-125307.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-115973.008Y=-319356.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-107694.836Y=-208161.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-108585.695Y=-121400.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-114656.961Y=-170073.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-102989.547Y=-208829.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-112622.438Y=-362266.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-122344.859Y=-325639.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-112691.891Y=-290553.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-10003.041Y=89086.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-108982.617Y=-235246.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-102976.188Y=-225957.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-110750.180Y=-334185.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-116404.820Y=-259433.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9954.079Y=-341873.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-118528.836Y=-141549.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-113171.633Y=-352254.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-119544.922Y=-302807.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110825.977Y=-305828.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-102132.672Y=-34958.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-103824.547Y=-313540.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-109832.719Y=-306956.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-130407.547Y=-153382.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113198.633Y=-135501.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-64270.086Y=-327412.062Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-106705.008Y=-185393.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113099.344Y=-132420.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119666.422Y=-257299.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"arched windows\", \"filename\": \"X=-102320.258Y=-345678.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-107066.141Y=-357002.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-115186.891Y=-358576.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-102591.867Y=-194965.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-107297.141Y=-194297.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113198.617Y=-203262.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-111758.938Y=-361636.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-122306.602Y=-343657.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-125871.328Y=-284734.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-105302.492Y=-251990.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-115548.844Y=-176751.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16510.000Y=-285353.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-115236.984Y=-315390.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-115231.516Y=-308830.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123010.812Y=-326276.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-123241.797Y=-321321.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, with a clock tower\", \"filename\": \"X=-111482.680Y=-19464.115Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-120649.953Y=-303829.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=2086.233Y=-101367.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-124428.359Y=-299953.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-114319.008Y=-351508.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-117785.133Y=-113761.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108072.023Y=-308648.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated entrance\", \"filename\": \"X=-107213.461Y=-317018.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-108201.242Y=-311314.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-126325.305Y=-283896.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-110370.523Y=-53820.574Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows\", \"filename\": \"X=-108598.648Y=-129658.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-105599.930Y=-190194.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-108903.172Y=-352338.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-117953.836Y=-235182.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113221.531Y=-130751.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-113201.078Y=-177030.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-102981.492Y=-168659.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-113197.852Y=-121718.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-115774.180Y=-203864.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-128895.688Y=-301606.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117198.000Y=-332520.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall skyscraper with multiple floors\", \"filename\": \"X=-45067.660Y=-20633.115Z=50.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117079.844Y=-293459.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-119728.859Y=-289285.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123606.859Y=-326883.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-101763.453Y=-359121.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-111138.867Y=-320856.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cylindrical\", \"feature\": \"glass windows\", \"filename\": \"X=-179476.312Y=-73244.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7941.399Y=-323957.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-125838.016Y=-298548.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-121248.281Y=-281674.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-120103.812Y=-122592.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120109.203Y=-129603.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120057.031Y=-180920.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=35231.059Y=41058.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-121945.016Y=-302446.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-107381.102Y=-310635.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-116212.547Y=-265908.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-1319.805Y=-258321.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-102069.797Y=-249629.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118653.953Y=-331120.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-186551.469Y=-191330.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-9654.717Y=-130191.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7793.950Y=-142172.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=86506.234Y=42562.527Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-10759.809Y=-153447.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-1910.627Y=-170601.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-10759.814Y=-180980.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124460.938Y=-319828.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13248.590Y=-320290.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-124659.938Y=-340277.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124602.562Y=-307823.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124622.727Y=-258322.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-127222.070Y=-350213.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-128221.117Y=-116223.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120059.742Y=-125634.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-116197.070Y=-134999.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-117110.500Y=-208553.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-120109.219Y=-209129.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-124990.891Y=-171652.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with multiple skylights\", \"filename\": \"X=-120109.227Y=-181739.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-122046.094Y=-285245.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-137994.031Y=-334379.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-3318.867Y=-323733.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123060.000Y=-306239.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=-116649.594Y=-299903.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-126022.312Y=-323394.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-118744.773Y=-319418.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-122144.070Y=-311812.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-118261.734Y=-287873.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-121325.500Y=-356693.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-123523.781Y=-353325.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-118259.047Y=-258706.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-107694.836Y=-203818.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-9564.919Y=-204025.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=680.742Y=-206927.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"square\", \"feature\": \"glass facade\", \"filename\": \"X=-120493.383Y=-235215.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Gray\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Large archway\", \"filename\": \"X=-45863.824Y=3119.937Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-113294.188Y=-323090.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-122446.305Y=-61991.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-116795.594Y=-326628.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118215.555Y=-125791.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124709.156Y=-120536.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with ornate detailing\", \"filename\": \"X=-120090.445Y=-131037.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120111.766Y=-133496.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with large windows\", \"filename\": \"X=-120109.227Y=-176383.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-126859.391Y=-185546.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130026.773Y=-208793.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"curved edges\", \"filename\": \"X=-125521.438Y=-204153.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-117686.195Y=-347459.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-114785.898Y=-295384.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119227.641Y=-262271.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119128.211Y=-291415.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"flat roof with parapet\", \"filename\": \"X=-114068.789Y=-289210.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113480.375Y=-223199.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-197296.078Y=-268584.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-121343.422Y=-351142.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119687.414Y=-358294.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-114059.461Y=-143456.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-115579.922Y=-271812.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118902.281Y=-328704.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"many windows\", \"filename\": \"X=-163765.156Y=-123583.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=138868.031Y=-260967.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124709.227Y=-181868.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-127686.453Y=-236547.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-124927.211Y=-294495.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"octagonal\", \"feature\": \"ornate detailing\", \"filename\": \"X=-1757.606Y=-379675.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=1737.176Y=-305965.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-122946.531Y=-292555.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-118229.953Y=-301192.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-120624.703Y=-350460.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-129488.914Y=-289722.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-130024.977Y=-197005.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-121038.328Y=-270879.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-118215.602Y=-203775.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-125501.711Y=-197375.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-125349.961Y=-257486.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-120066.922Y=-177840.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-114047.789Y=-152297.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=-126407.484Y=-176785.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-119050.672Y=-311413.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-127312.062Y=-303476.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-114967.484Y=-304548.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-117451.062Y=-320660.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-130478.039Y=-246402.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open facade\", \"filename\": \"X=-117542.648Y=-360947.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows and a central entrance\", \"filename\": \"X=-124269.094Y=-354035.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-115283.859Y=-287571.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-122839.891Y=-315202.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-105743.484Y=-304757.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117110.523Y=-180998.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124709.156Y=-126571.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124709.227Y=-176055.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124709.156Y=-116422.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-116728.688Y=-320003.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-120077.891Y=-124035.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-126855.320Y=-215789.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-117996.781Y=-31730.432Z=444.990.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124685.891Y=-124414.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-119145.609Y=-197304.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-125521.445Y=-134578.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118215.602Y=-134998.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120153.406Y=-207564.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117110.523Y=-176536.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124737.414Y=-188832.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-120173.062Y=-132420.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-120109.164Y=-126622.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120050.852Y=-205701.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118002.930Y=-59748.332Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-127307.656Y=-190239.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-115803.102Y=-305302.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-120082.555Y=-323422.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118292.148Y=-312217.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"blue\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-118833.109Y=-232481.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-115898.391Y=-278578.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118521.836Y=-359346.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118270.297Y=-292120.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-122764.367Y=-352585.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-127910.531Y=-297519.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123499.562Y=-313232.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"flat roof\", \"filename\": \"X=-129384.555Y=-251974.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-122889.148Y=-71474.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-124161.953Y=-253617.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-125006.211Y=-305507.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16567.648Y=-206186.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16578.576Y=-138520.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-118730.992Y=-274964.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117468.727Y=-327209.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-119009.117Y=-288730.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-122745.945Y=-312583.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-124105.141Y=-287209.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118221.422Y=-353590.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-117083.727Y=-355172.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=27955.473Y=-275047.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-120229.625Y=-317567.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16412.621Y=-83955.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118099.008Y=-298600.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-135646.688Y=-31364.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-122769.773Y=-355283.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-125782.445Y=-295151.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-115861.008Y=-308126.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-124798.609Y=-314483.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-112691.734Y=-283441.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-9644.488Y=65744.469Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-9391.609Y=-342827.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-3184.037Y=-346952.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-116926.820Y=-346694.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117712.375Y=-331748.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-123796.031Y=-320399.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two towers with a bridge connecting them\", \"filename\": \"X=39039.879Y=24941.613Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-20066.545Y=-275326.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123189.305Y=-252648.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-115905.727Y=-328475.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118855.922Y=-302078.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119403.641Y=-318743.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118444.320Y=-348229.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-22712.992Y=-320057.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-118025.438Y=-69003.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117157.328Y=-286717.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-128950.805Y=-278340.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-129218.164Y=-2885.476Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-118127.438Y=-327955.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-123602.953Y=-306742.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-121772.023Y=-295295.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-120014.344Y=-263043.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-121565.430Y=-255265.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-155896.016Y=-316322.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=-132279.375Y=-39007.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-116172.836Y=-285782.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-126973.000Y=-276465.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-21922.234Y=-389053.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with arches\", \"filename\": \"X=-125948.023Y=-318456.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119858.461Y=-283011.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-121991.820Y=-351768.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=-116117.578Y=-294498.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-121200.891Y=-324554.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-22413.812Y=-63168.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-127327.195Y=-36484.535Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-124640.016Y=-324828.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123170.016Y=-259398.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-128507.422Y=-282026.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-120188.711Y=-296598.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Large windows\", \"filename\": \"X=-125302.930Y=-254765.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120744.547Y=-310500.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119103.992Y=-353181.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-115826.023Y=-333792.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124161.070Y=-293795.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-120203.953Y=-329958.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124749.023Y=-178292.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-124367.055Y=-347408.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-3980.799Y=77601.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-126407.438Y=-111332.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118420.398Y=-261648.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119632.117Y=-329374.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-114537.344Y=-335316.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124709.156Y=-110488.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-120133.609Y=-121718.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-102605.773Y=-236376.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124713.844Y=-123313.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-127307.680Y=-111150.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118215.555Y=-121453.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120109.156Y=-120804.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-120174.852Y=-178916.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-126884.828Y=-180980.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124711.477Y=-177006.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-118215.602Y=-180923.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-62552.156Y=-208123.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-127707.859Y=-121361.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124665.945Y=-111555.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-118807.719Y=-187781.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124731.688Y=-181086.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124523.719Y=-187670.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-116197.086Y=-181022.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-126361.047Y=-295991.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-119623.672Y=-297270.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-119248.375Y=-167294.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=-7761.096Y=-154135.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123503.781Y=-293049.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-114727.930Y=-324487.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open structure with arches\", \"filename\": \"X=-126650.742Y=-256109.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-122965.719Y=-272837.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118215.602Y=-208547.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16625.092Y=-134801.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-16681.293Y=-132874.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-14353.880Y=-222966.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16597.912Y=-149520.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-16627.826Y=-179563.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16578.582Y=-181742.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-120109.195Y=-203262.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-130026.734Y=-194333.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-125521.414Y=-194630.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-117110.500Y=-203716.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"unique window design\", \"filename\": \"X=-107694.852Y=-143773.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-130015.062Y=-206121.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-10632.646Y=-340212.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-117110.500Y=-130166.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-125362.172Y=-277997.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-134359.609Y=-66592.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-116161.570Y=-299340.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-120941.234Y=-295868.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-122685.031Y=-301623.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-123474.172Y=-15578.282Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123335.367Y=-286408.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-130407.531Y=-107057.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-126540.320Y=-345235.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-128221.117Y=-111084.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-124382.688Y=-93790.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-125640.414Y=-308828.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-120534.359Y=-290069.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-118771.445Y=-107774.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-119248.367Y=-171407.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-124709.180Y=-184825.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-117576.773Y=-313392.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern office building with large windows\", \"filename\": \"X=-143121.297Y=-157498.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-141971.828Y=-331997.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-4003.783Y=81536.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade\", \"filename\": \"X=-126884.789Y=-115971.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-127564.914Y=-250098.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22814.670Y=-199204.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-23526.088Y=-216550.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-126855.297Y=-158091.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-131363.719Y=-163623.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-125474.477Y=-206493.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-124677.781Y=-179903.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124668.820Y=-115234.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124709.156Y=-190115.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched openings\", \"filename\": \"X=-128543.602Y=-267755.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-121286.031Y=-284290.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-129234.305Y=-318993.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-135230.047Y=-309228.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-72121.500Y=-114679.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-116317.062Y=-306062.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-129336.109Y=-348030.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130864.758Y=-314155.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120082.289Y=-179984.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=-124600.805Y=-114318.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-118215.602Y=-176754.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117402.477Y=-307087.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-133925.453Y=-52406.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-122521.922Y=-254658.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-128473.266Y=-320869.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-114118.430Y=-236364.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-132968.891Y=-345216.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-116197.023Y=-121369.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-117110.453Y=-125719.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-138262.531Y=-310588.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-113710.562Y=-303395.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-113985.422Y=-269284.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-137814.766Y=-307416.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136666.750Y=-185819.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131219.859Y=-181868.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-143316.547Y=-106938.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-124659.781Y=-121914.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131272.062Y=-177006.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-142544.688Y=-16932.377Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-136619.828Y=-184866.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-117628.711Y=-292770.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-119188.109Y=-269035.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-133378.844Y=-60925.855Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-140962.188Y=-123554.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-154068.578Y=-39109.488Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-136831.328Y=-312012.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-127205.094Y=-296745.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended structure\", \"filename\": \"X=-130064.281Y=-272068.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35045.598Y=-380567.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-7934.739Y=-310341.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-137285.031Y=-214003.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-141884.438Y=-263698.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-128221.094Y=-190211.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131219.797Y=-190667.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131219.781Y=-126571.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-131161.328Y=-177944.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-136619.797Y=-190631.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-117654.883Y=-354261.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-120928.836Y=-255920.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-140712.031Y=-222918.594Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-120590.367Y=-283753.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-141579.828Y=-176637.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-136200.922Y=-168051.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-128621.305Y=-125717.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-12217.030Y=71225.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture\", \"filename\": \"X=-130026.719Y=-129937.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-127686.930Y=-338527.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131753.391Y=-301520.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7076.128Y=-325781.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120107.031Y=-204370.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-130026.719Y=-199640.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-120109.219Y=-134985.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-119205.359Y=-194965.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-119205.375Y=-199308.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-125521.414Y=-199714.719Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-125521.461Y=-208496.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-6352.626Y=-240582.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-138264.375Y=-269423.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-140874.750Y=-337258.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-133205.766Y=-311217.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-132632.469Y=-312044.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-134556.781Y=-330075.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-428.367Y=-263581.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-140836.734Y=-304034.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-129326.195Y=-111385.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-126801.969Y=-162409.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-136350.078Y=-150340.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-120764.281Y=-242045.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-1408.236Y=-346096.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-121782.680Y=-24300.176Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130140.062Y=-307786.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-133875.703Y=-310410.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130115.586Y=-279627.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-71828.477Y=-278011.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-141665.938Y=-296518.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131167.609Y=-179555.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131219.828Y=-184825.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131236.406Y=-181086.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"suspended section\", \"filename\": \"X=-138634.953Y=-168091.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131169.562Y=-121508.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131219.828Y=-116422.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131292.156Y=-114318.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-129326.195Y=-116205.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136470.844Y=-187974.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131235.188Y=-124414.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131168.938Y=-122906.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-138707.297Y=-190172.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-129044.188Y=-121500.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-125887.305Y=-105039.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-131215.922Y=-115234.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136668.672Y=-189185.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-129502.219Y=-166503.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-138513.453Y=-181010.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-129521.531Y=-125535.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136619.844Y=-181244.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-131219.828Y=-110488.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"geometric\", \"feature\": \"angled facade\", \"filename\": \"X=-129496.180Y=-170148.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-136573.234Y=-186829.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131266.391Y=-111555.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-126529.625Y=-152680.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=5320.852Y=-275206.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-137803.828Y=-161388.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-129309.961Y=-259921.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-132196.047Y=-292430.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-134440.500Y=-314558.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-6045.287Y=-262189.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-141072.969Y=-324206.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-135910.234Y=-298996.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-137277.672Y=-340108.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-123714.039Y=-45017.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130132.742Y=-313401.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-7005.775Y=-336722.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-133791.875Y=-296945.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130890.023Y=-280424.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-132556.844Y=-275130.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-129625.172Y=-308563.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-1169.331Y=-315883.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"solar panels on roof\", \"filename\": \"X=-130122.828Y=-342403.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136482.281Y=-299602.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-130711.391Y=-307120.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-129844.883Y=-299454.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-135591.359Y=-272096.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-138639.531Y=-61596.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-137444.781Y=-311335.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-128248.094Y=-18010.738Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-1567.313Y=72583.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-136555.047Y=-294126.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-128766.781Y=-309268.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-27627.246Y=-292073.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-137336.750Y=-42217.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-9451.750Y=32028.170Z=569.943.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-140940.766Y=-297274.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-1693.279Y=-84601.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-132644.906Y=-284326.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-140208.953Y=-330170.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-132248.672Y=-333429.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-135818.891Y=-313119.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-127226.891Y=-310432.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-137579.938Y=-293030.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-4622.151Y=-317642.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-135430.000Y=-298351.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136176.875Y=-265955.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-197318.625Y=-235791.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"square\", \"feature\": \"glass facade\", \"filename\": \"X=110.984Y=-114406.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-132526.891Y=-301941.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"cylindrical\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-117761.875Y=-260840.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Historic architecture\", \"filename\": \"X=-136832.844Y=-306529.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-3005.301Y=82011.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-123320.273Y=-52966.512Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-16578.574Y=-144501.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-13595.959Y=-143644.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-552.107Y=-61917.113Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-196881.047Y=-131369.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-139163.531Y=-24462.074Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21125.799Y=-198007.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-20844.307Y=-216550.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-134440.922Y=-309739.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131792.484Y=-315018.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-139618.109Y=-298481.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13963.681Y=-130414.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14588.656Y=-143555.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=130613.406Y=-82795.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-133451.031Y=-316638.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-139604.281Y=-254963.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51798.988Y=-282144.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-134191.750Y=-273495.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=1727.165Y=-235018.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-139967.672Y=-190001.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-133259.375Y=-302856.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-140191.641Y=-303389.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-128221.141Y=-181016.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-137803.828Y=-171407.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-138919.812Y=-185544.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=147092.234Y=13275.499Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-131275.547Y=-186186.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-129326.242Y=-181080.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-136619.828Y=-176246.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-142141.000Y=-170034.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=20684.965Y=-337050.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131213.875Y=-187670.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-128100.258Y=-317918.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-132758.234Y=-315951.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-134916.297Y=-244263.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-12553.027Y=-335860.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-140113.891Y=-297685.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-138253.250Y=-194297.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-138253.266Y=-208829.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"concrete construction\", \"filename\": \"X=-129842.359Y=-331108.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-137272.969Y=-205284.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-129435.453Y=-223141.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-134694.891Y=-255694.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-128666.227Y=-47540.855Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-127078.477Y=-316841.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-130226.625Y=-319958.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-133197.125Y=-296301.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-135068.156Y=-20257.291Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-143121.312Y=-161055.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-138253.250Y=-153696.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136654.422Y=-179682.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131094.438Y=-300774.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-143570.672Y=-92947.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143570.672Y=-96803.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-137803.828Y=-157832.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-140156.422Y=-205281.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-139061.031Y=-308747.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-141294.125Y=-271932.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-140474.078Y=-323611.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-131062.461Y=-9015.950Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open facade\", \"filename\": \"X=-136645.422Y=-178499.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-139618.531Y=-180963.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-151007.375Y=-85145.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13366.846Y=70076.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-128221.141Y=-176820.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-130257.141Y=-103857.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-131166.094Y=-189834.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-141326.594Y=-290903.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-137921.906Y=-301025.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131165.797Y=-188832.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-8184.963Y=10831.081Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-132308.906Y=-305903.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-152128.875Y=-46884.785Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-150077.547Y=-27508.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"two connected towers\", \"filename\": \"X=-1948.826Y=-14388.940Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144401.859Y=-152307.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-144381.781Y=-35934.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-143260.016Y=-292824.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-141511.078Y=-252024.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"overhanging top floor\", \"filename\": \"X=-147997.203Y=-245666.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-141452.344Y=-304672.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-131219.844Y=-176055.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"yellow\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-129326.172Y=-190186.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21694.633Y=-131369.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-16616.459Y=-141191.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-27389.041Y=-214458.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-16596.865Y=-207159.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=182651.078Y=-142722.797Z=870.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-148481.219Y=-317983.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"shuttered windows\", \"filename\": \"X=-137565.703Y=-328357.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-15328.309Y=-309358.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-14312.700Y=-345334.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-128221.094Y=-185352.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-135912.156Y=-294775.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-131360.625Y=-215789.891Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-125860.703Y=-7650.697Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=82324.961Y=-47637.824Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-134025.703Y=-303633.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-127653.359Y=-321614.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-139349.578Y=-291464.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-149404.734Y=-312281.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144734.547Y=-189185.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-133311.484Y=-268821.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-141781.000Y=-324855.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-150630.250Y=-185441.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-133606.500Y=-23125.965Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-150758.031Y=-49575.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-140114.250Y=-215345.250Z=1320.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-8531.914Y=-344558.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-143835.344Y=-132559.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-138851.250Y=-299277.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144752.750Y=-187974.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-125891.758Y=-336720.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-134567.781Y=-297662.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131219.812Y=-120536.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-139741.406Y=-322940.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-135290.000Y=-49728.277Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"circular\", \"feature\": \"modern design\", \"filename\": \"X=-140442.484Y=-279594.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-142808.500Y=-292098.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-13458.547Y=-324610.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-147514.938Y=-237109.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144786.969Y=-185818.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=135673.734Y=-30227.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-130347.484Y=-299997.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-139485.688Y=-250069.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-146971.875Y=-40090.020Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-136609.766Y=-177514.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-142861.062Y=-185468.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-140842.531Y=-190032.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-132186.125Y=-262850.062Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-196946.797Y=-101145.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=38079.234Y=81015.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-138566.094Y=-85107.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-150630.328Y=-143773.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=97977.523Y=32516.434Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-139618.531Y=-176780.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-140316.672Y=-289850.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=-143944.266Y=-293557.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-150079.438Y=-177969.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14934.226Y=73281.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"towers with large windows\", \"filename\": \"X=-137762.188Y=-15998.768Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-147128.922Y=-316612.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-143680.344Y=-333703.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-154074.781Y=-205280.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-144706.844Y=-294240.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-142158.969Y=-291622.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-144754.688Y=-184866.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-152293.438Y=-188957.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-150635.797Y=-103705.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7192.021Y=90487.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-12005.010Y=-305885.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-21094.664Y=95544.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-145766.672Y=-242216.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-18510.021Y=-346016.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-138513.453Y=-176665.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-175607.188Y=-201977.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-131360.578Y=-159306.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=88236.812Y=-106938.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=20110.775Y=-277928.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-11673.244Y=-217614.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-8545.327Y=-382762.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-16556.543Y=-203858.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-16542.988Y=-332839.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-21735.346Y=-281765.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=1480.029Y=-276541.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-21008.637Y=-140741.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-15810.050Y=-341674.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"pyramidal\", \"feature\": \"stepped\", \"filename\": \"X=16665.449Y=-314584.000Z=819.946.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-11315.639Y=-369548.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-24038.709Y=-320786.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21233.393Y=-196008.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16578.582Y=-212494.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21178.568Y=-163351.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-14774.762Y=-180759.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-16572.787Y=-337847.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-28469.104Y=-129901.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and slender\", \"filename\": \"X=-22949.311Y=-185510.594Z=64.996.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16578.566Y=-154135.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-23372.607Y=-315464.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-22546.775Y=70502.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-14190.907Y=-124971.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-17387.408Y=-338322.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-17143.855Y=-284436.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-13414.291Y=-83779.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-14353.887Y=-390680.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11892.635Y=-223983.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-24193.467Y=-194531.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=809.472Y=-353192.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-20623.674Y=3340.337Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-11010.727Y=-233874.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-20207.838Y=-311844.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-20592.904Y=-332669.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-14765.107Y=-344324.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7345.848Y=83924.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-6303.795Y=69685.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-18317.963Y=-338797.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-17602.775Y=-326722.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16783.213Y=-340443.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16578.576Y=-129552.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-23871.414Y=-296755.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-18138.918Y=-288140.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-10244.607Y=-44487.031Z=544.410.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21728.854Y=-330000.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21490.043Y=-58775.988Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=569.178Y=-265265.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=8346.446Y=40043.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=-14913.241Y=-321255.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-21101.293Y=88698.586Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-23277.947Y=-238807.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-2464.467Y=-346522.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-15944.666Y=-321723.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-16021.549Y=-308471.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=128567.359Y=-185388.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-20133.287Y=-289368.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13595.959Y=-135025.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-12096.133Y=-139383.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14684.961Y=-208492.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-13963.688Y=-176883.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-27389.051Y=-133417.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-22877.484Y=-328295.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-15238.347Y=-199308.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-16578.582Y=-209069.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-22610.375Y=-134914.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-22610.365Y=-213006.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-20401.645Y=-334696.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-13963.683Y=-212904.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-28392.236Y=-157708.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16636.100Y=-142172.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-13540.796Y=-153341.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-24376.422Y=-131382.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-16605.387Y=-130546.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-16944.107Y=-195764.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-13963.681Y=-217729.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-25202.436Y=-179610.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16612.496Y=-180690.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16539.850Y=-214882.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-28469.098Y=-218017.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-12096.132Y=-213083.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade with vertical lines\", \"filename\": \"X=-16600.957Y=-152996.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21008.629Y=-151993.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16542.631Y=-140352.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-43814.508Y=-384045.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13579.869Y=-208576.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-13595.966Y=-181065.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13963.681Y=-139556.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=107991.797Y=-84854.617Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-23105.549Y=-203486.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Large windows and a central courtyard\", \"filename\": \"X=-16729.871Y=-151102.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=128639.766Y=-171722.406Z=618.404.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16531.256Y=-131673.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-22283.000Y=-309536.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-22025.477Y=-179616.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=5274.246Y=-30328.363Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-19785.908Y=-280836.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14955.954Y=-343396.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-22814.670Y=-194526.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-26074.312Y=-223983.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-21920.379Y=-391523.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11332.757Y=77446.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-11467.825Y=-282793.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=66280.930Y=-161192.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=75383.781Y=78116.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22557.658Y=-343323.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-19434.436Y=-312950.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-21334.051Y=-244586.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-7266.553Y=-389611.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-15894.778Y=-287192.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-14370.503Y=-331623.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21048.855Y=-281221.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-9026.080Y=87218.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14650.051Y=76000.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-20722.697Y=-310866.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-14669.952Y=-311029.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-28014.615Y=-287754.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-17440.129Y=-317272.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22393.168Y=-285611.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Gray\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Grid-like facade\", \"filename\": \"X=-22224.574Y=58259.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-21938.092Y=-324774.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-15558.955Y=-342722.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-29492.096Y=-270262.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=16812.016Y=-255348.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16269.844Y=-329226.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22262.025Y=-317988.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-63320.098Y=-72784.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-22851.141Y=-325207.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-18468.732Y=-280135.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=13834.982Y=58768.348Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-21477.131Y=-287884.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-18272.967Y=-281984.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows and entrance\", \"filename\": \"X=-1166.337Y=-262654.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-19851.523Y=-308281.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-24325.479Y=-300587.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-27724.963Y=10156.454Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-25620.514Y=-114412.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-23538.863Y=-282685.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-194191.141Y=-203811.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-14249.731Y=71799.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-19930.180Y=67517.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-22383.473Y=-329158.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-12644.076Y=-296436.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-14916.396Y=-310173.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7745.988Y=-132874.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14636.190Y=-336802.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=237.641Y=-361248.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-18699.852Y=-323126.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-21345.590Y=-6535.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-18575.141Y=-314608.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-20916.994Y=-239555.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-22610.355Y=-222007.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16576.346Y=-216360.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-3564.157Y=-274446.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-23715.256Y=-325706.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-137272.969Y=-197841.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-23114.879Y=-233094.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with many windows\", \"filename\": \"X=-19522.514Y=-301014.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-13540.796Y=-148895.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16615.006Y=-143376.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-24435.627Y=-283175.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-13391.048Y=-280296.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30016.793Y=90859.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-23354.010Y=77768.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-15492.678Y=-332233.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-24193.467Y=-199341.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-23105.547Y=-207030.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-14891.731Y=-160718.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-12666.433Y=-203792.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-24177.279Y=-157756.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21194.711Y=-158028.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21154.486Y=-197010.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-16627.008Y=-205059.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-21217.045Y=-162259.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-21178.566Y=-157207.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21163.240Y=-160290.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13579.867Y=-203749.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-21288.779Y=-380741.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-25200.980Y=-364421.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-15932.151Y=-378842.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22849.127Y=-316786.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-70032.242Y=21713.551Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21124.467Y=-331699.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"air conditioning units\", \"filename\": \"X=-26148.566Y=-57460.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"boxy\", \"feature\": \"open center\", \"filename\": \"X=-21452.162Y=-56108.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-17654.719Y=-283196.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-21475.527Y=-53188.355Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-23967.186Y=-314785.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16537.230Y=-133899.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-17149.594Y=-328174.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-15094.452Y=-286757.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-19271.912Y=-288916.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-15904.810Y=-39283.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21061.352Y=-289797.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-16944.123Y=-390631.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-49213.496Y=-185374.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-21904.773Y=-386863.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=17994.648Y=-67730.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-20890.230Y=-324307.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=7195.638Y=55385.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=40734.914Y=-378121.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-21965.908Y=-124920.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16542.170Y=-177228.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-12713.987Y=52192.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16702.832Y=-306692.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Historic architecture\", \"filename\": \"X=-21295.420Y=-319286.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21416.641Y=-330823.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-19225.219Y=-295979.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-22645.461Y=-66995.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-24183.117Y=79667.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-3823.208Y=53408.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-13988.617Y=-347294.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=180468.781Y=-77157.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16578.580Y=-217927.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"pyramidal\", \"feature\": \"tapered top\", \"filename\": \"X=61688.680Y=-284104.188Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-3779.403Y=86733.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=14933.420Y=-106506.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=162960.516Y=-153763.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-18103.385Y=-307444.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21178.570Y=-200108.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-21160.600Y=-199017.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-163765.156Y=-132574.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=82443.055Y=-249069.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-24110.045Y=-313832.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-23101.406Y=-327384.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-18288.863Y=-325345.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=-16136.542Y=75512.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-24244.188Y=-189301.875Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16774.357Y=-287647.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-24946.174Y=-39409.199Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-40361.609Y=-40123.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-24177.277Y=-162581.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-23072.186Y=-162645.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-18517.240Y=-333683.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-24185.582Y=-140728.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-36336.293Y=-152307.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16578.582Y=-175909.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-14353.900Y=-195741.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-25609.051Y=-194760.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-20803.754Y=-233150.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-23671.482Y=78644.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-2457.290Y=-103923.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-18135.250Y=-317676.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-20844.305Y=-225001.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-16610.027Y=-213598.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21872.701Y=-286845.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-2692.591Y=-114427.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade with decorative elements\", \"filename\": \"X=-8646.498Y=-95677.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35046.996Y=-364497.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-24829.162Y=-29561.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-18067.541Y=82646.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-2336.464Y=-102327.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-20966.854Y=-298179.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-13981.982Y=-315532.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-18176.178Y=-315906.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-30325.666Y=-390292.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-14848.877Y=-316024.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-15830.673Y=-380556.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-21200.527Y=-161114.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-21172.932Y=-159226.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-22904.807Y=77032.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"many windows\", \"filename\": \"X=-36336.293Y=-148951.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"many windows\", \"filename\": \"X=-36814.562Y=-140741.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-16578.580Y=-202964.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14684.959Y=-203962.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched opening\", \"filename\": \"X=-12548.104Y=-180812.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-17066.891Y=-300215.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16578.576Y=-135671.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-22966.742Y=-284375.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40833.305Y=-369452.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-13502.707Y=-114439.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-29517.248Y=-315347.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-31510.480Y=78380.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13057.436Y=-346820.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-28778.227Y=88261.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-36733.172Y=-322762.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-25727.613Y=-243851.656Z=544.410.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-33239.766Y=-15386.979Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-36190.746Y=-316415.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-26148.566Y=-52707.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-21776.035Y=-295743.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-30396.066Y=-163351.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30396.070Y=-157066.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-32536.826Y=74127.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-24619.367Y=75806.383Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-35040.367Y=-363392.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16709.678Y=-178590.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-30177.869Y=-313589.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-35974.281Y=51253.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37246.480Y=82699.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-21178.568Y=-193853.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-26177.768Y=-109667.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-38908.445Y=70194.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-9654.725Y=-180734.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass facade\", \"filename\": \"X=-30773.834Y=-243903.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-33512.230Y=-317048.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16785.381Y=-65772.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-26958.264Y=-162616.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-107649.414Y=-205821.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated center section\", \"filename\": \"X=-23526.082Y=-225016.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-23058.541Y=-43530.645Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-63303.605Y=-354850.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white and black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-28516.275Y=-106548.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-140966.797Y=-14081.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-30656.477Y=-239603.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"pointed top\", \"filename\": \"X=-16608.596Y=-2620.732Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-33844.828Y=-331993.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34939.918Y=-388912.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-30325.674Y=-387016.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-30368.691Y=-194980.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large open space below\", \"filename\": \"X=-15860.810Y=-298046.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-28205.305Y=65071.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-38688.305Y=37956.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-29710.721Y=-352918.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-24838.156Y=-310838.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"abandoned\", \"filename\": \"X=-30998.064Y=-313976.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-25012.443Y=-123910.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30360.703Y=-161435.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-25185.709Y=81288.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=-28048.551Y=-179898.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-34630.520Y=-235301.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-5072.764Y=79485.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36814.562Y=-144086.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-25814.379Y=-311332.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-114507.266Y=-197005.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-12548.098Y=-134689.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-38011.578Y=-194925.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-28964.291Y=-204952.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34890.078Y=87571.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-36252.727Y=71483.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13001.704Y=-326061.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-32902.453Y=-318239.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-22680.033Y=-282227.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-75390.055Y=-380825.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-33869.309Y=82083.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-28356.836Y=74202.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-14382.197Y=-291973.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-23105.529Y=-144086.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-30730.328Y=-179929.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-36299.992Y=86853.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-24694.471Y=80466.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"cylindrical\", \"feature\": \"grid of windows\", \"filename\": \"X=-40388.719Y=59917.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-35667.750Y=-324484.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-34643.883Y=62844.488Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-111462.016Y=-291540.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-25965.566Y=-157862.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-42749.035Y=-252140.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-28912.854Y=-316279.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-12666.433Y=-208461.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-12548.098Y=-130286.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30396.051Y=-199422.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-37959.281Y=-199338.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=153421.656Y=-104470.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-27609.371Y=-312257.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34996.051Y=-387007.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-39331.090Y=-387793.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-28278.107Y=-336362.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-27013.432Y=-158017.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-35903.668Y=80397.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-26899.398Y=-320351.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-29294.910Y=89323.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34996.059Y=-359628.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-197913.656Y=-158682.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=162758.281Y=-100963.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-29879.998Y=-142702.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-27397.340Y=-194552.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34996.074Y=-212339.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-31469.711Y=-108011.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-26566.998Y=74925.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=-29251.270Y=-225960.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34971.422Y=-223536.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-27833.838Y=86449.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-1598.202Y=-261849.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-34789.250Y=-170018.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-36870.211Y=-316999.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-32406.645Y=-324834.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-24071.104Y=-275725.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-33604.512Y=-315304.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-25598.656Y=82188.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-34789.918Y=-345049.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-44308.070Y=-234280.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended structure\", \"filename\": \"X=-24185.592Y=-152035.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-35003.664Y=-370567.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-36985.973Y=-360251.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-33611.695Y=94855.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-8822.266Y=76803.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-29676.225Y=90197.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-25182.947Y=66602.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-24573.490Y=-312828.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-31230.230Y=83692.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-25329.453Y=45764.262Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34996.062Y=-391517.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-26483.904Y=-194659.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-30445.568Y=-158028.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30396.045Y=-193853.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=4439.956Y=-348440.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-27397.340Y=-199429.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-38756.355Y=-205931.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-28759.955Y=-162534.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-25090.715Y=-157761.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-36089.789Y=-160129.328Z=65.008.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=142438.141Y=-171722.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-27060.596Y=-239603.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-31457.129Y=-321098.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-29375.332Y=-168022.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-23105.527Y=-148636.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=2875.134Y=-343864.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-28403.029Y=-243889.281Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-29446.836Y=-313157.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-28726.346Y=-329927.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-30229.443Y=54180.684Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-7584.978Y=74153.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-32431.592Y=82746.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-29622.402Y=-100513.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-33900.816Y=76429.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34219.914Y=-100850.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-29127.230Y=73702.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-29456.777Y=-323445.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-33637.164Y=-107958.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-28184.830Y=87316.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35660.684Y=-95142.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-77332.188Y=-329192.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=120365.781Y=-245520.750Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-25639.645Y=75291.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35466.785Y=-98401.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-38338.930Y=85104.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-27338.836Y=-319073.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34390.012Y=-326022.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-33457.434Y=88063.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-27558.264Y=74562.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-25287.617Y=-321321.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9715.044Y=-320564.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40379.219Y=73702.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall skyscraper with multiple sections\", \"filename\": \"X=-49583.312Y=-10472.432Z=819.945.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=33789.176Y=-352421.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34504.422Y=77650.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34277.637Y=-315672.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-3766.807Y=93313.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37017.277Y=-321924.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22524.473Y=76155.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-31885.113Y=-112022.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-30726.270Y=-324028.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-36511.781Y=81603.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-30624.988Y=-101061.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-31258.969Y=-98866.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-32762.186Y=-95782.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-30524.420Y=-304221.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=66793.555Y=-62760.277Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-37716.637Y=83707.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-29836.170Y=-234583.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-31430.236Y=71617.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-200886.578Y=-90745.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36014.027Y=-97439.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-42988.684Y=-176272.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37994.762Y=-355832.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines\", \"filename\": \"X=-40546.484Y=27275.537Z=444.989.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34994.422Y=-368838.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-34703.066Y=-302802.250Z=870.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-30058.242Y=84335.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35008.289Y=-379324.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35048.090Y=-354632.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-36871.047Y=-383231.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36985.977Y=-225724.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-28963.959Y=-357797.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36889.668Y=-351059.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34933.285Y=-372894.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34996.066Y=-218532.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-105302.492Y=-256697.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34943.098Y=-217666.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37994.762Y=-391318.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34973.348Y=-365398.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-34939.781Y=-351530.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=57121.027Y=-275972.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-34996.051Y=-221541.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34996.047Y=-350418.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-72023.742Y=-97737.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-29884.531Y=-371824.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34985.109Y=-384027.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34983.855Y=-222563.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35046.074Y=-216375.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35015.621Y=-224587.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-23610.711Y=-359519.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-36793.109Y=-372503.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34871.777Y=-362396.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-38908.195Y=-351092.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-29251.270Y=-222966.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-28964.291Y=-208496.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34956.047Y=-381862.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35021.898Y=-215273.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-35054.699Y=-361443.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35024.688Y=-382940.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30566.004Y=-214473.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35012.426Y=-371678.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-36889.668Y=-355563.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-34946.508Y=-360596.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-26881.494Y=-311876.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-33563.535Y=-325494.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-45838.945Y=-106770.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-728.753Y=-278802.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-33042.734Y=-93800.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-24730.992Y=-326200.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-46690.109Y=-76359.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-28920.318Y=84536.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-36713.059Y=-187075.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-125028.391Y=-168007.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-25090.717Y=-162543.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-28586.430Y=-312527.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-33238.594Y=75053.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-27433.182Y=85784.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-51777.582Y=-233252.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=42522.594Y=-284885.469Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-27198.209Y=-142679.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-49787.582Y=-250665.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35439.121Y=-326493.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-493.653Y=83044.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-28141.676Y=80124.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-28964.258Y=-187781.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-26418.213Y=-321897.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-36884.965Y=-380007.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-28964.258Y=-139357.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-28048.547Y=-150021.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-30730.332Y=-150025.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"many windows\", \"filename\": \"X=-41588.836Y=-168108.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-49353.117Y=-272465.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-32342.395Y=-320050.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-24054.199Y=-384648.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-30087.381Y=73153.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-35012.793Y=-355622.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-122978.695Y=4892.610Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37994.762Y=-387775.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36889.672Y=-387727.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-35030.840Y=-213875.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-34998.078Y=-225446.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-21253.260Y=-357162.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34996.051Y=-226367.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-29951.178Y=-364203.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-31019.688Y=-322116.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-49969.812Y=-107290.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-36336.293Y=-242936.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41239.559Y=-235243.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-38922.559Y=86322.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-49380.598Y=-296563.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-44179.094Y=-342969.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-39956.086Y=-217636.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-38537.438Y=-317882.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=24996.168Y=-85396.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-38394.031Y=-319535.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49203.977Y=-371817.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-23632.594Y=-23223.533Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-32299.988Y=88735.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-27943.479Y=69483.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-14294.498Y=93175.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-19780.520Y=-106798.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-49678.184Y=-342227.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-35021.406Y=-205976.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"concrete texture\", \"filename\": \"X=-41818.074Y=-380213.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37610.949Y=-364778.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39026.531Y=-225843.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43813.535Y=-360475.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-49247.832Y=-380415.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-34955.715Y=-353345.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-34996.051Y=-356475.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-26458.713Y=-391012.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-26458.730Y=-387729.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-43139.754Y=-206892.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37424.848Y=-222136.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37456.883Y=-320972.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-28502.432Y=-199406.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with a stacked design\", \"filename\": \"X=-130671.000Y=-84191.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-3117.327Y=75625.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-4933.785Y=55666.137Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=9524.508Y=67228.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-26997.906Y=84934.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-39586.852Y=-318352.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39919.941Y=-369368.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-49259.359Y=-122606.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-37595.117Y=-308609.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-31074.293Y=42837.117Z=394.585.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30347.158Y=-197004.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49216.223Y=-281758.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=56673.953Y=-224581.438Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39778.082Y=-380075.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43813.559Y=-350418.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-36889.672Y=-391341.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-41919.914Y=-222190.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-552.219Y=-44932.785Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30219.312Y=78789.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40814.824Y=-225845.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41919.914Y=-360344.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49076.359Y=-369750.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40819.637Y=-372547.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-38908.195Y=-391524.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40814.824Y=-222275.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43841.551Y=-362396.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-43752.219Y=-322333.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-43880.711Y=-379362.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41198.641Y=-387718.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-8502.621Y=90040.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-43790.129Y=-222563.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-39026.531Y=-360280.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-43781.621Y=-364822.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43813.527Y=-221541.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-41693.840Y=-98820.852Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-10406.172Y=89930.898Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-28390.008Y=-44487.449Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"curved windows\", \"filename\": \"X=-37731.852Y=75007.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-31991.180Y=72797.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-45103.016Y=-108434.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-49258.527Y=-124276.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=23754.875Y=-326326.125Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-44179.066Y=-168051.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51024.059Y=-350698.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43813.539Y=-212339.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-43788.551Y=-217666.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-48717.035Y=-223983.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43778.230Y=-370605.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-39032.160Y=-372355.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-41377.559Y=-383362.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-57992.531Y=-391287.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-37978.672Y=-360374.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37807.105Y=-103509.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-45927.570Y=-79336.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41823.613Y=-391321.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-36336.289Y=-232298.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-51777.586Y=-236224.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=-37468.883Y=-285159.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-48119.012Y=-70202.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-46154.430Y=-84782.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51749.602Y=-383328.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-552.132Y=-39462.824Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-176349.250Y=-112782.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-40661.863Y=-160179.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37111.219Y=-95873.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43801.285Y=-381900.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43785.332Y=-388041.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-64612.996Y=-50233.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-41919.914Y=-225902.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39478.500Y=-222232.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43868.305Y=-363392.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43835.879Y=-371717.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37984.695Y=-372261.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41925.770Y=-372309.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-42473.305Y=-143773.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-36814.570Y=-166662.375Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-37193.742Y=-251771.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43864.887Y=-224897.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"blue\", \"size\": \"low-rise\", \"shape\": \"boxy\", \"feature\": \"industrial\", \"filename\": \"X=-35821.562Y=-72904.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-170748.859Y=-71307.836Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open corner\", \"filename\": \"X=-41995.027Y=-150340.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-41995.027Y=-153696.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-49003.984Y=-179616.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-49175.879Y=-280756.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-38880.785Y=-282486.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-50279.027Y=-345791.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-48459.211Y=-89107.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"water tower on roof\", \"filename\": \"X=-37318.422Y=-341457.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-52419.105Y=64700.652Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-52217.977Y=-243362.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=182.460Y=-257563.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-47740.582Y=76572.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open entrance\", \"filename\": \"X=-37857.758Y=-169978.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-42473.305Y=-171407.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=-45802.727Y=69125.148Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=127487.453Y=-42554.301Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-156614.047Y=-162526.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open entrance\", \"filename\": \"X=-37254.480Y=-301526.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43916.113Y=-215273.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-38882.336Y=-122744.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-44353.297Y=-73162.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=125275.391Y=-190208.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-46186.969Y=-109006.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-42387.324Y=-77487.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"grid-like facade\", \"filename\": \"X=-30892.715Y=-85774.180Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43670.320Y=-157497.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large open space in center\", \"filename\": \"X=-43124.086Y=-203167.094Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-41274.289Y=-288671.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-37667.543Y=-317440.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-38908.223Y=-213079.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-40830.914Y=-391436.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-39901.387Y=-360203.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40814.824Y=-364956.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43813.555Y=-392260.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43813.539Y=-226367.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49260.438Y=-381309.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=-43863.203Y=-223536.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49185.164Y=-391287.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"irregular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49316.078Y=-384096.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43377.000Y=-74671.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39906.355Y=-372509.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37978.676Y=-225860.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-37994.789Y=-217734.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-49113.062Y=-368684.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43758.379Y=-213469.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-42009.727Y=-217560.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43858.430Y=-354632.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49213.543Y=-386986.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43774.199Y=-391167.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-43831.660Y=-351773.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43788.730Y=-382978.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-40814.824Y=-360177.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43813.543Y=-356475.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40775.773Y=-212885.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49183.086Y=-373253.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-37994.789Y=-212951.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49176.078Y=-370828.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-41938.684Y=-369341.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-51024.055Y=-354246.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-43989.129Y=-353345.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-94191.273Y=-45997.004Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43813.531Y=-218532.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49231.305Y=-389090.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-41919.914Y=-364996.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43833.895Y=-214548.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=4612.288Y=-57918.465Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40775.746Y=-355821.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-125113.805Y=-285539.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-43670.328Y=-161053.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-41588.938Y=-342913.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=-41239.559Y=-232279.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-40486.844Y=-101259.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-48411.238Y=-72939.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-34801.641Y=30202.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7892.152Y=84906.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-12907.175Y=-315025.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"elevated structure\", \"filename\": \"X=-44956.773Y=-78832.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-43268.852Y=-77944.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43988.465Y=-78309.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-53825.000Y=-40600.000Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-47579.727Y=-103332.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-138302.594Y=-321454.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=-42660.578Y=70659.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-48174.156Y=-101510.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-45877.957Y=15734.804Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-51920.203Y=1700.599Z=1344.999.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-44879.816Y=-87283.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=5552.775Y=-241545.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-59054.918Y=-64828.895Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49356.297Y=-108562.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-10187.516Y=-318789.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-47353.426Y=-109521.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-47872.887Y=-74253.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49114.363Y=-70672.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39994.160Y=-106320.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-45318.844Y=-70975.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-2263.218Y=90664.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-48711.699Y=-72291.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"crane on roof\", \"filename\": \"X=-35558.621Y=-112696.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-51298.402Y=-105227.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-2626.930Y=-258993.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49023.418Y=-110398.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-50352.582Y=75209.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-50075.664Y=-71195.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-61365.449Y=49229.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-51610.516Y=-76368.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-50721.703Y=-106173.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-46619.789Y=-105109.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7271.573Y=-294386.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-58055.004Y=28911.924Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-50930.770Y=-78118.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-40004.750Y=-89944.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43805.512Y=-73864.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39901.387Y=-225923.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-37220.754Y=-235297.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=1002.526Y=-284698.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-43823.637Y=-368857.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-41038.207Y=-86313.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-46053.020Y=-51202.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-42972.320Y=-178049.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-42473.391Y=-346518.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-44147.566Y=-101109.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-48845.695Y=-363776.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-16578.568Y=-148199.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-37976.578Y=-383147.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-65073.926Y=24454.615Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-36814.598Y=-271427.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-39478.500Y=-364684.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49268.590Y=-188567.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49213.508Y=-190692.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39312.516Y=-383228.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49213.551Y=-392182.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-38420.191Y=-369389.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49180.246Y=-187237.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43799.102Y=-389083.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-39783.051Y=-391420.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-42473.328Y=-272897.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-43745.004Y=-290471.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-49258.602Y=-279219.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade with decorative elements\", \"filename\": \"X=-49213.559Y=-276854.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-43813.551Y=-387007.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49256.621Y=-383294.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-49199.453Y=-379337.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-49390.520Y=-268133.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49273.348Y=-382266.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6421.269Y=-100671.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-214363.062Y=-221547.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-49314.012Y=-123467.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-38449.496Y=-128327.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-706.495Y=87307.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43164.051Y=-123477.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43834.863Y=-380910.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"circular\", \"feature\": \"arched entrances\", \"filename\": \"X=-108027.664Y=-91109.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-52353.746Y=-294380.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-44335.930Y=-108010.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-49353.117Y=-300047.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-43131.328Y=-296501.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-42473.328Y=-269345.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-45067.660Y=-20633.115Z=50.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-48230.066Y=-109950.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-51109.906Y=-336625.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-162708.344Y=-233628.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62655.902Y=-281919.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-56191.016Y=-140741.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58001.570Y=-282807.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-50736.816Y=-102880.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-56668.047Y=-134914.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-41367.398Y=-77013.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-56341.254Y=-76513.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-42167.293Y=77612.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-64026.586Y=-244144.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=-45945.340Y=79948.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57735.734Y=-83228.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-61416.637Y=-316883.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-67877.438Y=-268191.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-77723.008Y=-171407.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51024.043Y=-176272.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57920.094Y=-369104.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-53073.109Y=-151457.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=1337.327Y=83312.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-41025.285Y=-104268.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated\", \"filename\": \"X=-46270.234Y=-105912.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-53928.590Y=-369322.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-49229.816Y=-277821.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-56191.016Y=-168366.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=170133.109Y=-27882.482Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-40775.746Y=-350981.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-43013.129Y=66268.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-58001.539Y=-184745.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-57989.281Y=-188567.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-51777.547Y=-225018.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-42564.023Y=-336813.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51429.180Y=-363786.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49133.312Y=-372633.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-53666.512Y=-391491.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-55150.551Y=-361740.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-44929.078Y=-72279.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-43810.039Y=-372737.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open entrance\", \"filename\": \"X=-55785.883Y=-225040.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-48717.066Y=-234280.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-24799.039Y=-251337.562Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-38002.754Y=-320136.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-11282.935Y=71970.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-51173.965Y=-122121.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96632.031Y=-170829.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-51002.645Y=-277617.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"slender and tall\", \"filename\": \"X=-38139.117Y=5710.193Z=819.947.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-53173.254Y=-372267.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies\", \"filename\": \"X=-62264.953Y=-177655.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-53052.766Y=-369312.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-57994.066Y=-372076.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-54089.391Y=-387660.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-62648.766Y=-272484.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows\", \"filename\": \"X=-37025.188Y=-88431.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56212.438Y=-185651.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-54000.496Y=-190077.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-52260.320Y=-157950.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-41995.039Y=-242936.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-57883.887Y=-156735.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-47156.090Y=-75572.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79240.477Y=-131747.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-65375.969Y=-296921.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-60665.715Y=-76591.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-58163.934Y=61773.523Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=51877.594Y=25285.277Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-59440.570Y=-68197.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58009.398Y=-281758.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-52703.734Y=-74660.008Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56191.008Y=-365245.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-56191.016Y=-144086.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-56668.043Y=-181000.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57992.027Y=-186786.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-55483.996Y=-369292.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56107.922Y=-387885.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58057.750Y=-277821.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-56107.945Y=-281951.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58001.543Y=-392182.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-63892.168Y=-124369.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56107.922Y=-391500.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"elevated structure\", \"filename\": \"X=-57482.875Y=-290419.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-49141.602Y=44544.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall tower with clock\", \"filename\": \"X=-45658.547Y=-32761.797Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched doorway\", \"filename\": \"X=-57891.832Y=-195752.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=13339.361Y=-128013.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56212.438Y=-190208.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-53125.641Y=-185338.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-50547.020Y=-360228.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51798.949Y=-125814.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58059.812Y=-280756.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55002.855Y=-282226.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-52310.164Y=31839.064Z=444.989.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade with large windows\", \"filename\": \"X=-414.379Y=-28826.322Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"suspended structure\", \"filename\": \"X=-55038.066Y=-266915.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-13526.695Y=76374.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-46015.789Y=-92615.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Gray\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Grid-like facade\", \"filename\": \"X=-67486.617Y=-51697.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-53044.094Y=-131725.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-57347.887Y=-259063.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56107.910Y=-125734.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51798.961Y=-391500.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58001.539Y=-387615.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-62597.078Y=-279774.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-52890.898Y=-286674.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-50507.375Y=-261945.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-53125.641Y=-190138.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-57953.992Y=-378984.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"many windows\", \"filename\": \"X=-60596.148Y=-109397.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-57981.043Y=-370468.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-52166.676Y=-387783.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-68675.641Y=-323265.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-13563.037Y=-55154.090Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with multiple levels\", \"filename\": \"X=-25942.773Y=-63168.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-10246.075Y=-39737.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall clock tower\", \"filename\": \"X=-21851.422Y=-39462.215Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-12394.596Y=-327251.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple floors\", \"filename\": \"X=-37668.180Y=-4321.064Z=-430.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=78477.453Y=-55139.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with many windows\", \"filename\": \"X=-62758.531Y=-102140.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-54951.500Y=-371985.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58214.184Y=-70561.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-65309.949Y=-341676.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=28287.684Y=-217979.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-13006.137Y=-258045.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-50547.016Y=-170333.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-75577.945Y=-319053.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-56668.066Y=-236262.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"box\", \"feature\": \"flat roof\", \"filename\": \"X=-58762.660Y=-91151.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-63251.469Y=-55845.555Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-56621.984Y=-294380.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-52116.461Y=-372070.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58025.195Y=-124276.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-53214.527Y=-125692.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58061.016Y=-390101.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-53214.539Y=-387821.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-52166.699Y=-277474.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-54089.418Y=-282154.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-52998.918Y=-72685.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-50547.020Y=-166977.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-54048.773Y=-372265.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-50547.023Y=-142702.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-49899.953Y=-154204.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=-58369.367Y=-177655.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-54000.496Y=-185627.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58001.543Y=-190692.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-58094.000Y=-381986.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-61418.324Y=-85104.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-49878.242Y=-291639.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-51788.684Y=-298829.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-55048.352Y=-190085.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elaborate facade\", \"filename\": \"X=-56668.047Y=-355715.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open central courtyard\", \"filename\": \"X=-54966.723Y=-131421.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=169340.031Y=-208596.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-63740.297Y=-235301.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-62413.770Y=37781.957Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-70364.492Y=-140427.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62549.727Y=-268530.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-62601.539Y=-267631.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-53214.562Y=-281942.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-52155.152Y=-61645.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"open center\", \"filename\": \"X=-49983.574Y=-131701.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=54165.883Y=-143773.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-28502.432Y=-194598.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-52217.969Y=-162268.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-62232.062Y=-251100.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-78200.008Y=-161055.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=117221.164Y=-262505.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-50834.730Y=-369365.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-54039.812Y=-379749.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=-58001.531Y=-126416.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-51107.109Y=-185532.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58072.219Y=-187657.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-58021.910Y=-389000.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-52010.449Y=-369507.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-52212.207Y=-185368.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56107.910Y=-122205.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57978.234Y=-125352.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55048.359Y=-185342.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-58108.641Y=-383920.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-52212.207Y=-190137.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-51173.984Y=-387843.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-58078.027Y=-279626.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62601.551Y=-282876.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-62388.234Y=-194600.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-49882.340Y=-286329.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-58001.539Y=-121458.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-53304.332Y=-383225.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-54089.379Y=-125814.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-55089.086Y=-379763.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-62564.812Y=-280843.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, vertical lines\", \"filename\": \"X=-54146.281Y=21157.840Z=1320.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-55002.855Y=-277329.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58001.555Y=-276854.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-55170.812Y=-383125.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-63355.047Y=-190771.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-62626.215Y=-278011.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=2322.828Y=-204892.812Z=570.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55002.832Y=-387946.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-55672.809Y=72760.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-63963.758Y=-97869.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-56065.605Y=-372182.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open entrance\", \"filename\": \"X=-54255.988Y=-73268.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-51388.859Y=-82095.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-55183.074Y=-311385.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-52064.508Y=-179600.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-63740.281Y=-232298.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-57412.352Y=-85126.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-55468.406Y=-108381.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open structure\", \"filename\": \"X=-63240.258Y=-93279.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"irregular\", \"feature\": \"many windows\", \"filename\": \"X=-49520.441Y=-43730.824Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-55366.621Y=-59740.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-58673.055Y=-100087.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-3644.822Y=76591.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-55549.770Y=-73929.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57861.977Y=-243362.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-59550.633Y=-79729.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-58516.340Y=-85763.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-4739.821Y=-266689.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-54705.793Y=-53589.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-60978.551Y=-101265.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-8513.611Y=95885.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-64001.160Y=-88545.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-64797.289Y=-113480.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=123285.008Y=-207030.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-6082.892Y=91254.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with setbacks\", \"filename\": \"X=-67110.289Y=46302.039Z=444.990.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-63790.551Y=-319257.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-55337.574Y=-78267.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-55657.137Y=-344110.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-5777.771Y=252.850Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-55450.047Y=-250564.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"angled\", \"feature\": \"glass facade\", \"filename\": \"X=-55785.875Y=-177697.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57996.852Y=-189543.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-48401.457Y=24096.006Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-54293.629Y=-261877.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-54089.418Y=-277389.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-77218.656Y=-84643.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-62556.969Y=-269383.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62601.531Y=-209090.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-57354.652Y=-262685.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62490.027Y=-270373.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-80650.305Y=17341.674Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62601.531Y=-273483.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-64495.141Y=-208651.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-56107.945Y=-277274.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62570.191Y=-206250.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elaborate cornice\", \"filename\": \"X=-63350.621Y=-214961.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-62560.438Y=-271442.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-62428.875Y=-262301.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-62603.562Y=-278839.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-49875.523Y=-288823.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-53214.562Y=-277437.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-62601.539Y=-276904.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-176963.750Y=-258845.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-53701.609Y=-290419.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architecture with arched windows\", \"filename\": \"X=4030.035Y=-335088.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-54993.535Y=-113745.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-50121.430Y=-63449.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71789.500Y=-204328.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-5179.158Y=67434.695Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated base\", \"filename\": \"X=-18280.625Y=49355.816Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Gray\", \"size\": \"High-rise\", \"shape\": \"Rectangular\", \"feature\": \"Floating structure\", \"filename\": \"X=-58210.914Y=-95597.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-48880.738Y=-65806.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-51107.344Y=-216365.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-73923.883Y=-345003.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-67755.180Y=-72837.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-66642.156Y=-190597.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-62601.520Y=-203673.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-55287.207Y=-201962.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-64026.570Y=-181000.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-70499.391Y=-186061.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=2265.289Y=-243813.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-67523.508Y=-83451.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=9589.350Y=77157.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=54820.445Y=-67478.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-64026.586Y=-240591.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-75886.336Y=-75818.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-55799.988Y=-99139.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-155751.641Y=-177615.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-78200.023Y=-150340.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-78200.016Y=-157498.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-70556.758Y=-189786.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-58369.383Y=-233260.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-64907.105Y=-251119.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-69172.859Y=-69899.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-70364.484Y=-263692.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-68664.289Y=-131934.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-67896.727Y=-338854.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66093.398Y=-74204.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated central section\", \"filename\": \"X=-31345.951Y=-101424.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-66513.672Y=-203769.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=119735.312Y=-29660.236Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71789.586Y=-277022.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-71789.562Y=-282876.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-70312.375Y=-260172.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-78200.031Y=-153696.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-70364.477Y=-166977.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-76021.664Y=-168051.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-70364.492Y=-143773.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71777.086Y=-280843.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated stage\", \"filename\": \"X=-52372.418Y=-65616.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open entrance\", \"filename\": \"X=-59226.141Y=-114630.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-67481.812Y=-250986.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-60866.875Y=-96291.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-64520.496Y=-171722.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-56668.922Y=40709.098Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-110700.195Y=-281452.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-91023.695Y=-160785.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-72298.789Y=-73990.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-56043.109Y=-298803.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-73342.367Y=-77838.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-68381.234Y=-282123.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-63589.184Y=-80986.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"skyscraper\", \"filename\": \"X=-141945.500Y=-81632.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"tall\", \"filename\": \"X=-107694.852Y=-176202.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-63019.660Y=-199013.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with arched entrance\", \"filename\": \"X=-70168.156Y=-208490.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-69895.969Y=-272962.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-65386.352Y=-289218.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=87400.930Y=-81306.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71871.422Y=-101450.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-125902.250Y=-102133.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71773.672Y=-269115.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-75602.945Y=-68776.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-57522.559Y=-54724.543Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-70920.594Y=53814.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"abandoned\", \"filename\": \"X=-65460.441Y=-79034.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-67157.445Y=-105125.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-67424.844Y=-199001.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71785.375Y=-206170.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65954.727Y=-272873.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-72057.141Y=-288393.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70967.117Y=-66423.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-76389.523Y=-184730.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-63377.496Y=-188282.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-68381.219Y=-208560.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-67136.703Y=-219415.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-51129.453Y=-372229.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-67388.523Y=-203916.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-73428.195Y=-92082.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-69080.891Y=-80871.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-72126.133Y=-235301.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-67877.438Y=-272842.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-53749.492Y=53109.688Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-70463.211Y=-223012.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-68402.234Y=-100383.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71781.625Y=-205317.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-143316.516Y=-101164.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-74074.461Y=-98840.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-69870.594Y=-243811.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-52671.184Y=-58367.168Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-74217.430Y=-336557.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-76109.367Y=-123904.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=158433.484Y=7923.666Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-69895.969Y=-268321.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-64590.637Y=-272751.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-71230.422Y=-102806.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-61920.266Y=-333262.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-78325.430Y=-80426.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70774.445Y=-103599.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-66681.055Y=-64239.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-72231.359Y=-54115.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large open space in center\", \"filename\": \"X=-76123.617Y=-93456.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-66519.016Y=-66281.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70499.383Y=-214090.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-67002.586Y=-272720.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71758.773Y=-281919.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-73045.367Y=-98382.977Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-69800.438Y=-203718.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-50431.164Y=-307288.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-70168.203Y=-277416.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-70364.492Y=-170333.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-69257.906Y=-98366.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-72777.234Y=-73209.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-67184.930Y=-103011.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-75072.383Y=-314236.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-75427.109Y=-76824.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=121363.219Y=-78097.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-64807.367Y=-69771.195Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-68220.719Y=-90695.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-70364.492Y=-232298.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-68832.875Y=-70708.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-70160.023Y=-68137.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-70006.734Y=-81372.711Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-76504.312Y=-197703.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-64026.543Y=-152307.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-56191.008Y=-171722.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-64026.562Y=-161388.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-64520.488Y=-144086.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"cylindrical\", \"feature\": \"glass windows\", \"filename\": \"X=-64520.496Y=-168366.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83163.523Y=-232696.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-70609.461Y=-251003.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-71789.555Y=-267631.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-4515.004Y=-12217.318Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-68149.250Y=-80404.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-62562.160Y=17524.156Z=819.945.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7571.555Y=-324794.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-66121.188Y=-67319.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66642.164Y=-213334.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71784.977Y=-278839.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65600.250Y=-282165.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66009.891Y=-268161.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=109858.500Y=-203921.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-68790.875Y=-268156.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-69870.555Y=-150340.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-69870.547Y=-153696.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-64939.992Y=-177673.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-66909.258Y=-234280.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-65231.660Y=-381195.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-75521.250Y=-109326.883Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-71588.695Y=-219163.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-66642.164Y=-216588.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-71790.695Y=-208123.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-63338.301Y=-185472.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-66642.164Y=-186815.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-9618.535Y=88384.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=144695.672Y=-150025.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-77478.141Y=-242529.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11320.576Y=-345915.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-8068.069Y=-328579.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-75104.984Y=-55579.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"striped\", \"filename\": \"X=-72522.141Y=57272.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-65524.902Y=-68040.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-69629.867Y=-88120.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-66858.773Y=-84137.773Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"glass facade\", \"filename\": \"X=-71087.547Y=-125226.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=138280.562Y=-92910.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=58511.742Y=-258362.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-67440.031Y=-101900.742Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71194.727Y=-350105.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-72489.133Y=-343527.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black and white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-71600.703Y=-133552.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-66278.672Y=-86000.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-76529.109Y=-195096.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=104793.234Y=55946.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=34213.750Y=-171040.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71789.516Y=-203268.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-66839.766Y=-194600.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66513.688Y=-282147.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-65597.836Y=-262273.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-65600.250Y=-277410.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-65600.234Y=-203678.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66513.688Y=-277518.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65600.234Y=-208471.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-76826.547Y=-207248.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"square\", \"feature\": \"glass facade\", \"filename\": \"X=-71554.555Y=-195965.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66513.672Y=-208490.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-64222.914Y=-268074.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70168.203Y=-281904.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-68790.875Y=-272945.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71885.477Y=-279774.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=184637.188Y=-77986.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-68381.234Y=-277373.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-71789.578Y=-272441.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71789.516Y=-209090.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70306.859Y=-96921.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71828.711Y=-270373.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-9853.581Y=78714.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-76180.008Y=-177969.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-71363.547Y=-333774.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65847.070Y=-89536.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-7683.704Y=-151102.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-65593.289Y=-104325.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-100788.938Y=-360036.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-69870.594Y=-240258.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79050.000Y=-41800.000Z=-445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-74347.188Y=-317572.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70118.992Y=-86780.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70600.062Y=-86044.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-75776.953Y=-89738.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-67613.711Y=-330745.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing at the top\", \"filename\": \"X=-64473.395Y=55333.629Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-10335.065Y=-390605.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical facade\", \"filename\": \"X=-2283.651Y=-291937.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-73656.453Y=-71561.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=150387.344Y=-188985.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-77646.055Y=-100708.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, with horizontal stripes\", \"filename\": \"X=-58831.535Y=-26048.541Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71012.992Y=-81821.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-68826.164Y=-89213.102Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-5447.711Y=80218.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-79471.898Y=-354167.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-69001.305Y=-309327.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-64062.824Y=-71368.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"stripes\", \"filename\": \"X=-79495.578Y=-62426.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-71299.234Y=-346739.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-69125.258Y=-106076.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-8519.498Y=-243701.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=28168.875Y=96149.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-81129.641Y=-310790.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-81846.836Y=-64787.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-72296.031Y=-315700.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-69513.234Y=-65677.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-73577.523Y=-316969.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-61181.129Y=8082.263Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-80103.781Y=-339137.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65372.398Y=-73804.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-67925.484Y=-82415.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-72907.594Y=-316237.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-78060.273Y=-65114.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-88391.031Y=-291936.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-70385.477Y=-382202.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-66552.844Y=-84963.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-66318.344Y=-110495.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-76362.109Y=-312997.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"chimneys\", \"filename\": \"X=-68534.352Y=-364489.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-64319.410Y=-73348.805Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-77853.109Y=42314.629Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-14135.471Y=-323229.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7782.771Y=-213598.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-82374.297Y=-312257.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-67891.391Y=-101105.539Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7083.888Y=73278.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped facade\", \"filename\": \"X=-89827.500Y=-6102.222Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71547.445Y=-84261.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-69870.570Y=-176272.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-70386.812Y=-104642.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-71052.844Y=-121738.867Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-64495.141Y=-203917.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-68793.219Y=-179595.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-79388.234Y=-185358.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with arches and columns\", \"filename\": \"X=-82243.711Y=-221050.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85192.859Y=-186663.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-91377.664Y=-261754.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76378.078Y=-270577.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-85177.477Y=-184730.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-83985.789Y=-313820.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-78283.148Y=-272892.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-76389.555Y=-273690.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-80701.180Y=-284986.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-78178.586Y=-263524.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-80390.492Y=-263512.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=-76342.641Y=-188601.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85140.086Y=-262191.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-79388.242Y=-272965.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-81265.344Y=-258958.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82178.781Y=-258971.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76351.586Y=-271398.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-80595.492Y=-76718.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-68060.719Y=-105515.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71603.641Y=-193358.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-68436.383Y=-204032.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79337.859Y=-70688.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-82008.055Y=-342618.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-71600.711Y=-198707.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-83222.836Y=-206296.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90430.625Y=-215418.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-79792.703Y=-214044.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7575.206Y=-206186.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-76134.289Y=-203523.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-10759.810Y=-148936.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=112612.352Y=-233507.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated balconies\", \"filename\": \"X=-84661.172Y=-73336.789Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-86676.805Y=-309952.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-912.748Y=-209004.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11791.579Y=-208477.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-66850.109Y=-89991.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"openings\", \"filename\": \"X=-1028.900Y=-179929.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7741.721Y=-179563.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-9654.717Y=-217706.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-7761.096Y=-209230.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-7790.816Y=-131673.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open center\", \"filename\": \"X=-85832.469Y=-336553.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7725.625Y=-134801.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=671.570Y=-170575.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7761.097Y=-148199.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7761.096Y=-138520.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-51777.547Y=-221969.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-77451.648Y=-245281.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-9654.717Y=-143838.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-7820.934Y=-140252.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-15137.300Y=-88215.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open archway\", \"filename\": \"X=-10759.807Y=-135070.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11673.244Y=-130282.680Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7759.120Y=-130546.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-9654.720Y=-148929.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-93175.953Y=-213057.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-8536.411Y=-197841.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-7706.418Y=-214535.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-11673.247Y=-149131.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"bricks\", \"filename\": \"X=-11673.251Y=-181032.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83736.812Y=-72886.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-72447.328Y=-100154.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-88927.062Y=-387331.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-83366.984Y=-142388.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-64026.555Y=-148951.562Z=420.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71962.180Y=-179616.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-75766.047Y=-313638.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85118.297Y=-75424.492Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=-102887.648Y=-332882.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-16944.113Y=-222966.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-89931.250Y=36112.980Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=2671.061Y=-197426.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7761.096Y=-143785.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-10375.996Y=-208318.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-77451.688Y=-240184.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85177.484Y=-273011.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=151465.906Y=17372.715Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-75680.312Y=44313.098Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-73743.430Y=-61191.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-83999.828Y=23977.756Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Historic architecture\", \"filename\": \"X=-11365.094Y=-338996.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-76420.203Y=-263325.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-80390.484Y=-259022.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-9654.717Y=-134719.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-69870.547Y=-162860.422Z=445.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-82142.656Y=-131328.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-69870.547Y=-159304.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-64520.500Y=-140741.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines and setbacks\", \"filename\": \"X=-79240.492Y=-177981.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-77723.008Y=-142702.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9654.718Y=-153140.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-82142.680Y=-179922.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83217.078Y=-189944.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85194.969Y=-269886.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid windows\", \"filename\": \"X=-160.956Y=46220.738Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-64643.172Y=-78641.086Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-87367.047Y=-93562.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-82394.570Y=-305724.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-78786.945Y=-317950.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-76331.898Y=-220223.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83843.992Y=-176585.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-82640.828Y=-320727.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-12458.824Y=77221.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-91783.273Y=-203478.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-14760.067Y=-61917.113Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=137332.891Y=-234639.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-90451.758Y=-97632.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-1028.895Y=-53701.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83366.977Y=-139043.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-80301.672Y=-190186.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-80869.273Y=-81729.727Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-81886.742Y=-390009.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-86469.461Y=-74321.914Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-13675.003Y=-312812.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-76415.773Y=-260065.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85188.297Y=-261003.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-80532.594Y=-106564.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-78200.023Y=-181313.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-81176.523Y=-190202.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-82218.570Y=-278634.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-78019.727Y=-282305.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-11494.475Y=-319499.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=156236.516Y=-207030.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-130026.742Y=-203486.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-84874.609Y=-99181.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-78047.586Y=51358.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-82757.227Y=-199736.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83195.703Y=-313109.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-88272.539Y=-318053.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-83190.086Y=-79540.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-82121.789Y=-241855.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall clock tower\", \"filename\": \"X=-83875.688Y=-217220.938Z=544.984.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-88097.391Y=-322173.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-8318.015Y=75810.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-80052.938Y=-125551.297Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-163708.953Y=-194615.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-82456.922Y=-334514.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated walkway\", \"filename\": \"X=-76762.930Y=-217946.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85177.484Y=-190924.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85231.133Y=-271398.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-20894.221Y=37111.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-79342.625Y=-263795.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-76389.516Y=-258208.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85996.844Y=-315831.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-84534.281Y=-111507.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-77610.727Y=-72400.023Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow with a cutout in the center\", \"filename\": \"X=-78084.031Y=12256.574Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-91770.523Y=-207938.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-8482.429Y=-323251.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-49213.566Y=-282807.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82592.117Y=-268083.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76389.523Y=-190190.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-79388.242Y=-268150.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85899.312Y=-359663.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83490.078Y=-78667.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-83683.102Y=-88319.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-78283.141Y=-190261.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82592.109Y=-272956.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-81938.680Y=-72013.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76389.523Y=-267683.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85177.484Y=-258735.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-85488.992Y=-297500.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-86958.867Y=-323563.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85333.219Y=-106594.070Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-79850.023Y=-323190.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-86014.625Y=-309340.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-78573.508Y=-221242.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-82167.188Y=-102964.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"ornate detailing\", \"filename\": \"X=-76535.211Y=-69090.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85184.688Y=-317697.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=24327.717Y=-207905.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-79211.938Y=-322616.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-84020.297Y=-235466.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-77722.984Y=-139357.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-80724.555Y=-185384.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-81133.141Y=-102471.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83283.867Y=-263540.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85209.000Y=-263325.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76363.016Y=-272596.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76361.883Y=-187605.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-131187.594Y=-125705.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-86788.922Y=-84681.461Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-78605.141Y=-168058.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82510.289Y=-92112.648Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-86606.984Y=-316434.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-85806.797Y=-84238.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-76538.562Y=-348327.938Z=313.059.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-75864.125Y=-15173.725Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85200.594Y=-379450.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90756.211Y=-96805.508Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-82224.383Y=-190022.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-83951.625Y=-283123.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=61870.234Y=-303825.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-79690.727Y=-309454.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-73517.016Y=-355833.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-83784.125Y=-194925.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=75846.453Y=-39972.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-90533.469Y=-213233.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, narrow, lots of windows\", \"filename\": \"X=-96869.289Y=-20639.117Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-2361.091Y=-107288.133Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-77737.625Y=-311486.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-88057.883Y=-90563.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-84733.383Y=-325557.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90594.078Y=-214357.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-81624.836Y=-235482.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-86289.008Y=-289543.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-9998.858Y=72393.992Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-82321.367Y=-82462.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85266.633Y=-308484.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-78283.141Y=-185531.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-90607.789Y=-216759.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-79342.625Y=-259077.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76349.875Y=-262191.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"open grid facade\", \"filename\": \"X=-82326.500Y=-170065.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-84726.148Y=-131368.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-83844.008Y=-150025.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-10522.981Y=67446.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-79746.672Y=-77785.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-91003.336Y=-102326.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"stripes\", \"filename\": \"X=-80334.742Y=-57711.195Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"open structure\", \"filename\": \"X=-79306.945Y=-390327.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-87344.875Y=-90179.398Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79577.609Y=-316851.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85126.227Y=-66991.305Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82398.172Y=-80376.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-78964.688Y=-199338.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-91389.734Y=-176902.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated section\", \"filename\": \"X=-92332.680Y=-153490.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85387.008Y=-170018.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-76179.984Y=-131701.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-90435.141Y=-149735.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-83843.992Y=-158970.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-77770.383Y=-341441.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76668.320Y=-215992.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-1266.135Y=88283.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-93376.078Y=-271071.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-74106.062Y=-306911.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-29730.557Y=-350935.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-77972.523Y=-236561.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-80683.859Y=-324037.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-76988.914Y=-312141.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-86432.242Y=-302961.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-1929.241Y=-260777.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76449.844Y=-74605.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-81752.445Y=-73709.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-83866.055Y=-338501.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-91437.547Y=-254191.141Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83844.000Y=-134914.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82592.102Y=-185586.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-85950.625Y=-324557.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-6273.439Y=-327451.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-193860.062Y=-283731.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83601.141Y=-90403.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-5758.495Y=85586.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-87052.766Y=-69873.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-79388.234Y=-190100.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-77746.461Y=-71668.336Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=2171.447Y=82055.820Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-78178.586Y=-258890.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-82880.820Y=-91276.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85125.531Y=-188601.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-76433.500Y=-268530.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76329.531Y=-185891.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85143.641Y=-189789.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-82178.781Y=-263801.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-80301.672Y=-272844.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-76389.516Y=-264226.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-81265.344Y=-263748.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-81482.320Y=-315302.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85193.789Y=-260086.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-2220.428Y=-251703.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-84726.156Y=-179929.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-83283.867Y=-258949.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-86164.727Y=-280873.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-83676.102Y=-376373.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-85177.492Y=-264226.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85156.273Y=-268530.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-9334.736Y=-354002.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-80109.969Y=-107331.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-9579.630Y=-194297.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior space\", \"filename\": \"X=-92712.305Y=-165773.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=94828.156Y=-331306.250Z=544.410.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-86042.672Y=-96008.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-78398.641Y=-301208.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-13280.334Y=-313412.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-96395.664Y=-64411.145Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-76986.633Y=-73196.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=13148.004Y=51499.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-69451.094Y=-235260.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-74538.008Y=-332042.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-84146.102Y=-327550.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-81831.016Y=-311408.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-87241.680Y=-310571.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85143.289Y=-97527.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-76376.625Y=-269583.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"flat roof\", \"filename\": \"X=-831.224Y=-111143.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Windows with bars\", \"filename\": \"X=-75291.406Y=-291145.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-98370.609Y=30043.365Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-86991.406Y=-302283.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-81178.312Y=-74919.211Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-89022.695Y=-91040.164Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-84389.781Y=-318465.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-80395.578Y=-310151.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-88293.734Y=-85504.227Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-86574.219Y=-89797.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-94109.000Y=-122682.836Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-82018.609Y=-325581.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-84834.227Y=-304249.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-100026.977Y=-343485.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-87840.586Y=-291069.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79241.477Y=-78768.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-77493.453Y=-69691.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79080.953Y=-308915.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=98858.242Y=67807.258Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-87825.852Y=-92555.602Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-78035.211Y=-318762.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-81298.078Y=-321601.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-84398.305Y=-305146.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-87492.602Y=-82505.961Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-89033.109Y=-107316.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-88533.875Y=-101045.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-89013.586Y=-288283.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-98786.844Y=-362292.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-88413.406Y=-64937.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-89765.930Y=-98307.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-87537.805Y=-322905.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-98282.953Y=-81927.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-5929.023Y=81144.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7761.096Y=-218503.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7715.549Y=-217541.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-93914.227Y=-69695.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-87481.398Y=-317239.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-91908.523Y=-310997.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83749.391Y=-77818.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-92815.094Y=-302540.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"pyramid\", \"feature\": \"stepped\", \"filename\": \"X=-95829.484Y=5677.342Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-90564.953Y=-286380.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-97605.094Y=-65055.340Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-91529.234Y=-85363.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-92572.984Y=-315917.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=129134.180Y=-203818.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-100267.352Y=-363732.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=75193.984Y=-10981.618Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-2759.880Y=-334544.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-92759.273Y=-82392.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-83174.320Y=-295186.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"irregular\", \"feature\": \"angled facade\", \"filename\": \"X=-1028.899Y=-75946.242Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-1028.891Y=-176585.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94318.336Y=-308876.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-100597.164Y=-336236.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-92128.047Y=-301622.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-79482.969Y=-101551.570Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-72125.164Y=451.795Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-3821.080Y=64812.738Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94833.109Y=-315484.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-93657.078Y=-316986.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Brown\", \"size\": \"Mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched windows and decorative facade\", \"filename\": \"X=-102308.875Y=-82010.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-87036.109Y=-290275.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-91955.578Y=-352316.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-89954.883Y=-76097.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=133.967Y=-321973.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-95719.875Y=-314808.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=710.319Y=84526.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"Art Deco style with vertical lines and setbacks\", \"filename\": \"X=16514.455Y=-291905.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-76653.469Y=-213332.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-153340.188Y=-59450.633Z=313.060.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-81321.812Y=-97309.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-90577.477Y=-212293.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90025.297Y=-293279.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-81509.570Y=-225034.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-83158.969Y=-208819.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-80662.023Y=-217389.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical style\", \"filename\": \"X=-91713.453Y=-205266.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-98782.227Y=-59306.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-83662.102Y=-98560.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-87397.836Y=-330464.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=109300.734Y=-125907.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90876.828Y=-242614.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with large windows\", \"filename\": \"X=-96874.258Y=-244391.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-100157.344Y=-309976.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-86811.617Y=-94304.273Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-2569.547Y=-187183.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-92328.547Y=-157227.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-95208.875Y=-294222.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-89551.977Y=-299311.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-88442.430Y=-80699.758Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-98668.734Y=-318013.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9654.718Y=-212912.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-89863.125Y=-78176.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-1419.256Y=-10603.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-89924.656Y=-313293.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-101677.375Y=-187733.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-100922.359Y=-377038.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-91389.734Y=-259002.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tapered top\", \"filename\": \"X=-88389.320Y=9468.292Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-91391.344Y=-132238.578Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"open space in center\", \"filename\": \"X=-95895.031Y=-140741.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83366.969Y=-166662.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-91340.133Y=-179109.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-91389.734Y=-264099.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=58848.906Y=-92922.266Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-95868.453Y=-133465.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-91389.734Y=-142702.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-92731.703Y=-343452.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-90829.703Y=-245611.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94307.594Y=-294884.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-95313.633Y=-78828.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-13647.885Y=-233565.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-97178.773Y=-292259.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-80133.359Y=-207192.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-79413.711Y=-203523.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-84696.617Y=-254521.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"slender\", \"feature\": \"tall\", \"filename\": \"X=53015.859Y=-294990.188Z=470.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-95753.500Y=-94467.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-95104.141Y=-349216.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-93541.711Y=-81290.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51107.109Y=-190189.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-88371.438Y=-300809.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-60584.809Y=-78341.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-94089.391Y=-212993.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-95194.484Y=-217797.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-83843.992Y=-162526.922Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-83844.016Y=-153382.188Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-97572.750Y=-280097.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-96567.828Y=-94921.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with many windows\", \"filename\": \"X=-101896.109Y=-98874.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94026.602Y=-303786.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-144482.766Y=-14499.707Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-91199.398Y=-311851.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-103502.602Y=-326816.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-92126.734Y=-170829.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-102545.656Y=-342128.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-97451.125Y=-86829.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-92113.562Y=-84159.039Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Brick facade with decorative elements\", \"filename\": \"X=-96330.680Y=-319660.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=123003.453Y=69061.328Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-91715.805Y=-291056.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-97068.414Y=-213233.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96275.812Y=-208496.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96234.367Y=-240658.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-7070.702Y=-359661.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-78283.148Y=-268133.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-94863.250Y=-93965.945Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-95895.023Y=-144086.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-99728.148Y=-302982.531Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-94075.500Y=-80322.641Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-93885.766Y=-289102.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-92720.227Y=-356285.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-10306.573Y=-345299.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entries\", \"filename\": \"X=-95188.211Y=-298507.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-97019.953Y=-320345.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-95706.570Y=-66185.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture with columns\", \"filename\": \"X=-96741.539Y=-152753.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-91117.430Y=-95813.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-93080.539Y=-235309.234Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7811.363Y=-344147.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-94480.672Y=-68807.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-93424.430Y=-103537.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Classical architectural elements\", \"filename\": \"X=-99342.266Y=-297064.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and columns\", \"filename\": \"X=-92350.742Y=-331726.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architecture\", \"filename\": \"X=-100207.602Y=-89438.320Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-92301.102Y=-213101.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-97051.289Y=-214357.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-96640.328Y=-116890.430Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-94287.406Y=-48984.355Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-97595.570Y=-347408.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-116463.781Y=-349314.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-113800.352Y=-39966.352Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-91134.336Y=-291807.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-102165.172Y=-314603.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-89796.445Y=-101857.047Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-5568.681Y=-245289.891Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Arched entrance\", \"filename\": \"X=-95920.383Y=-379473.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-93681.430Y=-72845.664Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=3076.828Y=39866.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-91137.375Y=-300933.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-93165.234Y=-289619.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-8931.816Y=-80627.117Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open interior\", \"filename\": \"X=-93038.016Y=-383496.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94253.945Y=-350256.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90931.031Y=-320765.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"square\", \"feature\": \"glass windows\", \"filename\": \"X=-97213.445Y=-248524.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-138699.188Y=-103754.078Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-99413.047Y=-65752.445Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with large windows\", \"filename\": \"X=-88907.312Y=-349517.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-101304.062Y=-223128.203Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-97848.180Y=-72977.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96495.180Y=-299694.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-97088.102Y=-217985.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-106268.844Y=-224765.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-97228.883Y=-252946.875Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-99627.539Y=-372292.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-92869.898Y=-77701.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-99110.523Y=-311451.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96050.391Y=-285789.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-99680.852Y=-69450.555Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-98386.398Y=-321975.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-102802.344Y=-372798.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7731.389Y=-205059.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=-101700.828Y=-188674.672Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-98990.664Y=-70124.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-99381.930Y=-309108.500Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated sections\", \"filename\": \"X=-93258.359Y=-93184.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-94556.898Y=-297739.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-7791.528Y=-203858.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-92392.484Y=-284755.031Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-118013.672Y=-321268.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-95895.031Y=-264025.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Detailed Facade\", \"filename\": \"X=-98945.359Y=-348866.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-101305.492Y=-142388.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=-91389.719Y=-134578.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-91389.727Y=-130234.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=70359.219Y=-281833.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-95895.016Y=-135246.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-102069.797Y=-263749.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"cuboid\", \"feature\": \"glass windows\", \"filename\": \"X=-102069.797Y=-259043.109Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-97564.914Y=-374321.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Symmetrical windows\", \"filename\": \"X=-93549.367Y=-357047.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-88107.094Y=-81704.930Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-94498.766Y=-288424.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-98885.289Y=-297898.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-8391.469Y=85876.414Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-103130.836Y=-341311.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-96458.055Y=-232852.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-98552.367Y=-379006.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-92525.180Y=-197152.797Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-97088.086Y=-212293.484Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-94089.391Y=-217810.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-89939.625Y=-319794.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-88017.500Y=-61316.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-13860.037Y=70956.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-93847.391Y=-244391.359Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-98818.195Y=-71018.055Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid windows\", \"filename\": \"X=-101852.992Y=-233701.984Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96754.336Y=-156490.422Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-98101.328Y=-298677.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7761.095Y=-129573.367Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-7821.007Y=-152996.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-8824.127Y=-222007.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-11673.244Y=-134930.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-10759.809Y=-143780.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-10759.810Y=-212983.734Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-7744.887Y=-149877.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"Red\", \"size\": \"mid-rise\", \"shape\": \"Rectangular\", \"feature\": \"Historic architecture\", \"filename\": \"X=-12224.872Y=-337708.344Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=89169.352Y=-122425.633Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"arched\", \"feature\": \"arched entrance with clock tower\", \"filename\": \"X=-93978.922Y=-93648.453Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-89965.922Y=-374004.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"blue awnings\", \"filename\": \"X=-83010.789Y=-213902.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-91305.461Y=-314642.688Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"circular\", \"feature\": \"arched windows\", \"filename\": \"X=-82498.305Y=-373959.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"overhanging section\", \"filename\": \"X=-97284.305Y=-165773.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-100218.234Y=-289914.125Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-93445.938Y=-149681.172Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with multiple windows\", \"filename\": \"X=-96189.016Y=-338416.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and narrow\", \"filename\": \"X=-222563.672Y=-150566.719Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-93260.734Y=-240712.969Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-88291.172Y=-308519.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-93175.953Y=-217570.609Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-97136.023Y=-215418.016Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-95194.484Y=-213009.766Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-92275.727Y=-217706.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"yellow\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96312.805Y=-205751.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-97087.875Y=-216446.953Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-9783.513Y=-329269.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-93640.906Y=-296835.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-95284.711Y=-249768.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure\", \"filename\": \"X=-93695.422Y=-160785.547Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96254.008Y=-375896.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-93661.195Y=-70844.289Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=-114758.398Y=-15941.400Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with industrial elements\", \"filename\": \"X=-89044.797Y=-86082.391Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=114488.992Y=-257143.828Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open area at the base\", \"filename\": \"X=-156536.641Y=-210324.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-97491.031Y=-307279.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-99600.672Y=-349475.844Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96362.578Y=-314159.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-91431.445Y=-334524.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-11615.308Y=-330219.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"boxy\", \"feature\": \"elevated structure\", \"filename\": \"X=-215201.297Y=-199001.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design\", \"filename\": \"X=-13920.267Y=-23924.098Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-87024.891Y=-346058.250Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-88988.805Y=-358174.312Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96051.836Y=-305811.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-85177.492Y=-267584.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96714.586Y=-306504.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-97409.352Y=-380913.000Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-90037.930Y=-353520.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-98165.953Y=-307729.625Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"octagonal\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-94391.312Y=21247.850Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-89031.023Y=-321543.938Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-1696.686Y=89287.594Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-94477.445Y=-378073.781Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural details\", \"filename\": \"X=-93033.742Y=-310232.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-95332.000Y=-307672.750Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-85660.266Y=-52645.574Z=70.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-100418.094Y=-350249.375Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-91374.586Y=-305375.156Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-96989.695Y=-375228.406Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-94722.055Y=-355111.062Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"cuboid\", \"feature\": \"grid-like facade\", \"filename\": \"X=96021.273Y=-83077.516Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-95801.828Y=-293523.656Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-97193.039Y=-300447.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"open floor plan\", \"filename\": \"X=-91449.047Y=-285374.438Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-98321.836Y=-311995.812Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-100430.102Y=-66401.859Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"elevated structure with multiple windows\", \"filename\": \"X=-13704.059Y=-336463.562Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing\", \"filename\": \"X=-98043.500Y=-352186.094Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-100174.766Y=-360986.469Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-96624.914Y=-380173.281Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-97441.430Y=-74749.703Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"dark brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-98252.266Y=-373771.906Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical architectural elements\", \"filename\": \"X=-92180.453Y=-304618.219Z=0.000.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"irregular\", \"feature\": \"openings in the center\", \"filename\": \"X=-8983.303Y=72835.023Z=0.000.png\"}\r\n"
  },
  {
    "path": "scene_data/seg_map/env_ue_smallcity.jsonl",
    "content": "{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-7986.052Y=-56308.875Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"columns and grid-like window pattern\", \"filename\": \"X=-6471.368Y=-51927.043Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate roofline\", \"filename\": \"X=-67325.453Y=9507.553Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped windows\", \"filename\": \"X=-9163.408Y=12508.139Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-12994.779Y=34084.669Z=1320.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-23462.664Y=39227.520Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top floor\", \"filename\": \"X=-22729.736Y=-3062.354Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows and decorative cornices\", \"filename\": \"X=-14992.758Y=3856.350Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top, ornate cornice\", \"filename\": \"X=-20853.270Y=9990.665Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14965.234Y=2669.580Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate top\", \"filename\": \"X=-18875.277Y=-6562.814Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=-17961.840Y=-3159.400Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices\", \"filename\": \"X=-17979.670Y=5081.928Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-19767.963Y=1395.115Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows, decorative cornice\", \"filename\": \"X=-23300.707Y=-12662.281Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-23066.934Y=-55140.723Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top\", \"filename\": \"X=-19750.131Y=-6532.671Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=6934.493Y=53771.137Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate windows and intricate architectural details near the roof\", \"filename\": \"X=-22750.164Y=4997.531Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern glass facade on top of a classical stone base\", \"filename\": \"X=7151.518Y=-13457.615Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"uniform windows\", \"filename\": \"X=0.000Y=0.000Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern on exterior walls\", \"filename\": \"X=-21223.070Y=-44051.047Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative details on upper floors\", \"filename\": \"X=-17624.246Y=-57035.277Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-pattern windows\", \"filename\": \"X=-5560.111Y=20182.047Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at ground floor, vertical stripes\", \"filename\": \"X=-12994.779Y=34084.669Z=1320.000.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-15269.597Y=-12741.023Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-16856.750Y=-3027.962Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-17354.697Y=1559.807Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative roof pattern with small statues\", \"filename\": \"X=-16308.020Y=39823.359Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous windows\", \"filename\": \"X=-25256.467Y=47689.129Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14952.408Y=1333.499Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid windows\", \"filename\": \"X=-15266.075Y=-57016.648Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate fa\\u00e7ade\", \"filename\": \"X=-22766.676Y=-2349.454Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows near the top, green cornice, decorative facade\", \"filename\": \"X=-15009.450Y=-5594.827Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative elements\", \"filename\": \"X=-14959.928Y=4997.530Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows in a grid pattern\", \"filename\": \"X=-4361.264Y=-6015.481Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-27122.260Y=46624.523Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=29155.676Y=-35157.180Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-22766.676Y=754.559Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental facade\", \"filename\": \"X=-22769.363Y=1730.112Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches on top floor\", \"filename\": \"X=-20873.053Y=4975.645Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate fa\\u00e7ade with arched windows and decorative elements\", \"filename\": \"X=-22798.133Y=-4052.223Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-17961.840Y=-6322.755Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative roofline\", \"filename\": \"X=23985.074Y=-19323.650Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched ornamental details\", \"filename\": \"X=-14963.129Y=-2349.453Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade\", \"filename\": \"X=-15818.934Y=12507.574Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate decorations around windows\", \"filename\": \"X=-20873.053Y=1231.446Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"modern windows\", \"filename\": \"X=-2721.181Y=-20595.201Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"lots of windows\", \"filename\": \"X=-14566.959Y=23731.582Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative window arches\", \"filename\": \"X=-14941.122Y=-4052.222Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-20517.879Y=-13476.438Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices\", \"filename\": \"X=-20375.107Y=-3371.772Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on top floor\", \"filename\": \"X=-16856.750Y=-6431.935Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate window frames\", \"filename\": \"X=-22824.535Y=3856.352Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-18854.523Y=5157.833Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-19767.961Y=4968.381Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade with a grid pattern\", \"filename\": \"X=15555.627Y=45729.188Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with a central tower and two smaller wings\", \"feature\": \"many windows arranged in a grid pattern\", \"filename\": \"X=-16071.292Y=-37277.387Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-16071.301Y=-53816.332Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-patterned windows\", \"filename\": \"X=-16071.300Y=-45603.035Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-22766.672Y=5886.776Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-35706.449Y=27841.678Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=36727.695Y=20149.496Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance, large windows\", \"filename\": \"X=-21658.494Y=12507.100Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"vertical windows\", \"filename\": \"X=-20488.773Y=-11321.919Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=-15818.932Y=10009.362Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-18875.277Y=-3065.100Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-22766.674Y=-7049.819Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade with numerous arched windows\", \"filename\": \"X=-16884.955Y=5027.061Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate windows and cornice\", \"filename\": \"X=-18854.525Y=1331.407Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on upper floors, flat roof\", \"filename\": \"X=-22758.152Y=-5594.828Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-patterned windows\", \"filename\": \"X=-18612.527Y=10858.467Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-22822.129Y=2669.582Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"cream\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with vertical window arrangement\", \"filename\": \"X=-20273.336Y=-55115.953Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative elements\", \"filename\": \"X=-14963.127Y=5886.776Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows, detailed cornice\", \"filename\": \"X=-17136.430Y=31693.480Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-14963.129Y=-7049.817Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-21191.475Y=40480.277Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-25286.178Y=33922.688Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"columns\", \"filename\": \"X=-5581.806Y=-44376.598Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative elements\", \"filename\": \"X=-27207.715Y=33495.438Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical facade with arches and decorative elements\", \"filename\": \"X=-18377.713Y=43408.168Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with large windows\", \"filename\": \"X=-25144.736Y=44673.996Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative top cornice\", \"filename\": \"X=-27744.982Y=38284.930Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate top\", \"filename\": \"X=-26206.488Y=23882.326Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-29638.689Y=37006.914Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangle\", \"feature\": \"many windows\", \"filename\": \"X=-29824.396Y=32240.900Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-28629.666Y=28079.379Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows and detailed architectural elements at lower floors\", \"filename\": \"X=-10339.784Y=-52094.977Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"light brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows, ornate top\", \"filename\": \"X=-1896.190Y=-36354.055Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"vertical windows\", \"filename\": \"X=-11348.895Y=44700.062Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative cornice\", \"filename\": \"X=-31976.209Y=35967.949Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative architectural details near the roof\", \"filename\": \"X=-30999.518Y=36486.449Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"window grid\", \"filename\": \"X=-3359.307Y=5222.960Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"tiered rectangular\", \"feature\": \"arched lower windows\", \"filename\": \"X=-9841.378Y=-48040.418Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative elements\", \"filename\": \"X=-32954.020Y=33944.746Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-29800.045Y=-37971.359Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative elements at the top\", \"filename\": \"X=-26195.141Y=35604.180Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-46410.066Y=-46808.230Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-25775.414Y=34804.145Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows near the top\", \"filename\": \"X=-31582.227Y=31451.580Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative columns\", \"filename\": \"X=-29159.789Y=-2912.189Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-30228.205Y=-14156.112Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-31058.002Y=30590.326Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"vertical windows\", \"filename\": \"X=-24626.043Y=30309.631Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative details\", \"filename\": \"X=-35236.973Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"combination of modern glass facade and ornate lower floors\", \"filename\": \"X=-17272.705Y=41471.602Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"tiered rectangular\", \"feature\": \"grid pattern windows, waterfront location\", \"filename\": \"X=3418.156Y=57287.469Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-34438.273Y=40587.941Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade\", \"filename\": \"X=-34724.785Y=-58095.863Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-53690.910Y=-36212.430Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"detailed cornice\", \"filename\": \"X=-32241.367Y=32621.334Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"columns\", \"filename\": \"X=5079.069Y=2504.219Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-pattern windows\", \"filename\": \"X=-32057.145Y=-19231.309Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-27570.904Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate top section\", \"filename\": \"X=-36092.156Y=-54859.387Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-28393.848Y=-5763.757Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with numerous windows\", \"filename\": \"X=-28397.201Y=-19231.307Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-33184.031Y=6074.583Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple glass windows\", \"filename\": \"X=-31988.660Y=43828.984Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=-26663.598Y=36329.070Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-27166.830Y=37285.984Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple glass windows\", \"filename\": \"X=-32863.176Y=15585.631Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade on upper floors\", \"filename\": \"X=-27462.992Y=26178.412Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular with stepped levels\", \"feature\": \"brick exterior\", \"filename\": \"X=24698.900Y=27816.876Z=645.000.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-38031.477Y=31803.391Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-29573.969Y=-46930.742Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at ground level\", \"filename\": \"X=-30228.207Y=-12039.302Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-36212.637Y=28706.457Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple large windows\", \"filename\": \"X=-35612.957Y=15508.358Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-37044.336Y=30305.535Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-30640.320Y=-19231.309Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative exterior with large windows\", \"filename\": \"X=-35814.105Y=-4092.248Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=57800.941Y=28261.150Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like windows\", \"filename\": \"X=-8711.078Y=3970.469Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative upper facade\", \"filename\": \"X=-38433.578Y=27270.416Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative window arches\", \"filename\": \"X=-27964.100Y=33055.828Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate top floor\", \"filename\": \"X=-30607.715Y=-56676.332Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices and large windows\", \"filename\": \"X=-30636.969Y=-5735.946Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-31433.133Y=19261.660Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing on the facade\", \"filename\": \"X=-36593.770Y=39366.176Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-32993.859Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like windows\", \"filename\": \"X=-27853.135Y=-56716.570Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-30647.635Y=8810.371Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-34714.445Y=-19231.309Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-30171.713Y=17005.535Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows and decorative elements\", \"filename\": \"X=-38537.309Y=32744.873Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on the lower floors\", \"filename\": \"X=-34060.199Y=-6943.816Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like pattern on the facade\", \"filename\": \"X=-29671.250Y=-53516.258Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate windows and cornices\", \"filename\": \"X=-37986.090Y=14280.418Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"tapered rectangular\", \"feature\": \"stepped setbacks\", \"filename\": \"X=6871.875Y=21210.352Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=-34070.008Y=17840.541Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-30648.654Y=41496.395Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-28223.605Y=39010.453Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade with arched windows\", \"filename\": \"X=-33738.547Y=35233.168Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-47563.488Y=21478.723Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like glass facade\", \"filename\": \"X=-47963.820Y=14883.854Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-33753.227Y=22717.086Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arch entrance\", \"filename\": \"X=-9163.405Y=-39408.613Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with decorative elements\", \"filename\": \"X=-28756.812Y=32601.975Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows with arched tops along the upper floors, green trim at the top\", \"filename\": \"X=-49318.566Y=20731.365Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped windows\", \"filename\": \"X=-23769.514Y=28844.201Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows in the upper floors\", \"filename\": \"X=-42360.395Y=3723.863Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"columns and decorative window frames\", \"filename\": \"X=-10295.503Y=-56433.391Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-39953.172Y=26167.334Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-44651.094Y=25603.283Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43174.645Y=34744.203Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-47431.879Y=1225.981Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"architectural details around windows and roof\", \"filename\": \"X=-43370.754Y=23416.689Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick texture\", \"filename\": \"X=-44319.980Y=28848.037Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=35939.961Y=-13036.114Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-40181.129Y=35571.301Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41088.602Y=37232.184Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate details on the top edges\", \"filename\": \"X=-46252.598Y=-37277.391Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"light\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid windows\", \"filename\": \"X=-41644.719Y=-51553.277Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on upper floors\", \"filename\": \"X=-1623.957Y=10859.318Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-64443.910Y=145.003Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-44604.301Y=37245.234Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-42182.629Y=25146.033Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with stepped levels\", \"feature\": \"many windows\", \"filename\": \"X=-21223.064Y=-37277.391Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at top floor\", \"filename\": \"X=-45407.848Y=36810.348Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-43300.480Y=10287.210Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=68410.367Y=6595.936Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51448.418Y=24420.387Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, decorative elements\", \"filename\": \"X=-46933.547Y=-11292.158Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-9565.405Y=-6707.689Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative elements around windows\", \"filename\": \"X=-47855.047Y=31162.816Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-36195.570Y=5589.013Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-42560.926Y=29838.320Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43866.137Y=24287.709Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"detailed facade with regular window patterns\", \"filename\": \"X=-6692.377Y=-5033.059Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches and vertical lines\", \"filename\": \"X=-45142.738Y=-19231.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-45552.016Y=27107.166Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-45001.207Y=-801.666Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows near the top\", \"filename\": \"X=-49740.492Y=25320.867Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"detailed windows and cornices\", \"filename\": \"X=-48894.242Y=23829.910Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like pattern on the upper floors\", \"filename\": \"X=-46933.543Y=-13487.303Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"tan\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows, ornate roofline\", \"filename\": \"X=-41096.707Y=25475.348Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red and gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-39073.059Y=3999.106Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41969.816Y=38802.727Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on upper floors\", \"filename\": \"X=-43733.270Y=808.585Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate roof and multiple windows\", \"filename\": \"X=-40816.922Y=30663.049Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with columns\", \"filename\": \"X=-41731.227Y=-4088.453Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous windows and balcony-like structures on lower floors\", \"filename\": \"X=-47477.406Y=-19231.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate spires and detailed facades\", \"filename\": \"X=4092.464Y=-19231.309Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"historic facade with intricate architectural details\", \"filename\": \"X=-40411.469Y=20189.869Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-46201.613Y=28319.885Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-46982.543Y=35801.930Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-50132.805Y=35127.012Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous windows\", \"filename\": \"X=-50233.328Y=26103.027Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with large arched entrance\", \"filename\": \"X=-77715.828Y=17602.525Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at ground floor\", \"filename\": \"X=-41451.359Y=-45875.961Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on the ground floor\", \"filename\": \"X=-44606.062Y=-4846.505Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate details with recessed windows and spires on top\", \"filename\": \"X=-40358.453Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-44167.781Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=-49601.969Y=34129.891Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate architectural details with large windows\", \"filename\": \"X=-46976.379Y=-1789.255Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade\", \"filename\": \"X=-40902.324Y=-19231.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-45326.781Y=16251.676Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative balconies\", \"filename\": \"X=-43837.383Y=34063.133Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"columns\", \"filename\": \"X=-60252.832Y=7224.712Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental facade\", \"filename\": \"X=-48794.320Y=32827.434Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-63220.402Y=17592.670Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"modern design with multiple dark window stripes\", \"filename\": \"X=-44378.730Y=12403.087Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick exterior\", \"filename\": \"X=-45623.188Y=33423.469Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-38531.230Y=17977.104Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-48273.320Y=22818.035Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows with arches, detailed facade\", \"filename\": \"X=-48240.691Y=31947.674Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=-42144.004Y=35168.160Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative elements on the upper floors\", \"filename\": \"X=-41826.551Y=30316.053Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice and arched windows\", \"filename\": \"X=-58244.043Y=21478.043Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-43316.496Y=29320.133Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-49372.871Y=24638.379Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-59314.727Y=14132.541Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrances and decorative details\", \"filename\": \"X=-50775.699Y=7668.091Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental\", \"filename\": \"X=-57059.734Y=8999.686Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-52835.859Y=18941.006Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-52038.750Y=29210.525Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-53744.859Y=18530.062Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick exterior, multiple windows\", \"filename\": \"X=-43696.027Y=37883.113Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornices\", \"filename\": \"X=-50958.168Y=-5066.662Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"symmetrical windows with decorative arches\", \"filename\": \"X=-62665.797Y=12996.203Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-49855.785Y=14815.644Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-56264.562Y=26937.707Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"terraces\", \"filename\": \"X=28999.027Y=55985.707Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative top trim\", \"filename\": \"X=-54826.598Y=28025.434Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-41152.176Y=-37277.391Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on top floors\", \"filename\": \"X=-52519.770Y=24065.832Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple arched windows\", \"filename\": \"X=-53711.266Y=23456.920Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorated cornice\", \"filename\": \"X=-53189.801Y=31181.895Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"tiered structure with vertical windows\", \"filename\": \"X=46299.500Y=4246.257Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"light brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-60762.867Y=25752.002Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple rectangular windows with some arched windows in the middle section\", \"filename\": \"X=-53785.082Y=32159.238Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-59769.105Y=24097.021Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-64902.672Y=16870.578Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows, ornate arches above windows\", \"filename\": \"X=-61736.305Y=27614.645Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57827.508Y=29671.814Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-12898.625Y=19247.051Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-57297.395Y=26632.926Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate architectural details\", \"filename\": \"X=-6278.696Y=-48040.418Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-42449.348Y=39518.949Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, decorative elements at the top\", \"filename\": \"X=-57131.520Y=30217.121Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-53780.340Y=28422.305Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-58362.379Y=25685.340Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate windows, staircases at the entrance\", \"filename\": \"X=-63823.359Y=21920.443Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-63985.906Y=6349.138Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-58755.098Y=29183.051Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick texture\", \"filename\": \"X=-61161.223Y=14026.062Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51076.711Y=19739.506Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55316.383Y=16407.191Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows and ornate design\", \"filename\": \"X=-52680.059Y=30386.816Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on the top floor\", \"filename\": \"X=-57330.945Y=19853.715Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows and small balconies\", \"filename\": \"X=-60628.461Y=16662.926Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous uniformly spaced windows\", \"filename\": \"X=-56285.352Y=30577.453Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=-62180.445Y=19096.104Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"stylized roof\", \"filename\": \"X=-50286.551Y=20197.785Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-51811.125Y=19261.848Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55388.316Y=31234.984Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-50587.559Y=11453.354Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with mixed materials\", \"filename\": \"X=23379.883Y=-35424.238Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-61304.340Y=17644.939Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice and arched windows\", \"filename\": \"X=-56508.148Y=21776.076Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-61052.312Y=1266.978Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative window arches\", \"filename\": \"X=-64391.359Y=12249.853Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55167.699Y=22244.438Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-55469.816Y=27310.602Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many evenly spaced windows\", \"filename\": \"X=-64723.207Y=23584.699Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-65726.062Y=2371.944Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"columns\", \"filename\": \"X=-3951.906Y=-56265.461Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows with balconies\", \"filename\": \"X=-2707.609Y=-46475.469Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"brick facade with arched windows\", \"filename\": \"X=-59974.426Y=15211.535Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-56344.977Y=18157.865Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the lower level\", \"filename\": \"X=-62315.062Y=3455.155Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-52433.406Y=13413.484Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with rectangular windows\", \"filename\": \"X=-52292.793Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-9114.146Y=-44378.211Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches above windows\", \"filename\": \"X=-55793.738Y=17158.400Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on upper floors\", \"filename\": \"X=-54326.234Y=17996.891Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental arches and intricate window designs\", \"filename\": \"X=-69684.773Y=13694.940Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-60470.746Y=-23360.553Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-72251.039Y=21332.217Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-57279.211Y=3667.289Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=-67520.016Y=20481.674Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing and arched windows\", \"filename\": \"X=-67880.492Y=10370.317Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-71844.102Y=17312.719Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the bottom\", \"filename\": \"X=-23255.416Y=-19337.928Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-74151.641Y=21328.773Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-4277.594Y=-3485.613Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate lower facade with large windows\", \"filename\": \"X=-72676.867Y=7278.782Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-66102.391Y=11176.401Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate windows\", \"filename\": \"X=-66120.922Y=25919.186Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arches and decorative elements\", \"filename\": \"X=-70691.648Y=18604.346Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-70191.148Y=14471.115Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-67101.859Y=4754.899Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-68311.289Y=23707.168Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-72997.789Y=10994.132Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-70615.297Y=1088.293Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches above windows\", \"filename\": \"X=-65116.926Y=11679.703Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-70715.773Y=22309.770Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-69137.523Y=23311.975Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65639.719Y=25134.881Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-66844.750Y=21141.074Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-66541.516Y=15974.039Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-73159.281Y=-4279.430Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple arched windows\", \"filename\": \"X=-68187.656Y=14788.195Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate architecture\", \"filename\": \"X=-69337.484Y=13007.002Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-72804.586Y=18965.131Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches above several floors\", \"filename\": \"X=-2954.664Y=51149.531Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice and arched windows\", \"filename\": \"X=-67393.531Y=24328.453Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on top floor\", \"filename\": \"X=-68526.141Y=-2526.152Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-69241.836Y=19731.275Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-65800.539Y=21541.621Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with large glass windows on the top floors\", \"filename\": \"X=23321.338Y=-41044.605Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, decorative cornices\", \"filename\": \"X=-67348.734Y=15545.312Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=-73522.789Y=20319.283Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-82229.031Y=11793.120Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"intricate facade\", \"filename\": \"X=-75068.766Y=9783.439Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous windows\", \"filename\": \"X=-77040.703Y=14756.754Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"columns\", \"filename\": \"X=-75189.828Y=-829.615Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous windows with amber lights\", \"filename\": \"X=-76167.398Y=4867.809Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative cornices\", \"filename\": \"X=-79806.852Y=16357.912Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=65185.676Y=19824.906Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-77467.062Y=7075.358Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on upper floor\", \"filename\": \"X=-83653.875Y=14225.184Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top\", \"filename\": \"X=-20844.846Y=-6355.273Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern\", \"filename\": \"X=562.509Y=47316.527Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-79484.945Y=4971.763Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-79609.305Y=8818.915Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-4526.009Y=10859.175Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental detailing on top, large windows\", \"filename\": \"X=8991.906Y=-49704.992Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate fa\\u00e7ade with arched windows and decorative elements\", \"filename\": \"X=-9163.407Y=10009.595Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-18481.357Y=18945.289Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=-2729.017Y=-44028.102Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"horizontal stripes\", \"filename\": \"X=-9217.423Y=-36949.992Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches near the top and detailed facade with columns and pediments\", \"filename\": \"X=-2686.485Y=-49237.289Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-8711.078Y=969.599Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"cream\", \"size\": \"high-rise\", \"shape\": \"rectangular with an extension on top\", \"feature\": \"repetitive window pattern\", \"filename\": \"X=-8612.012Y=25828.846Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=-8706.484Y=-28314.547Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-1799.303Y=-5205.643Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arches under the roof edge\", \"filename\": \"X=-22504.176Y=26634.461Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=-2451.390Y=-51927.043Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall, grid-patterned facade\", \"filename\": \"X=28921.982Y=44603.480Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"tiered rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=4447.410Y=10859.619Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, decorative moldings\", \"filename\": \"X=2440.413Y=-58585.902Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"columns and cornices\", \"filename\": \"X=10643.960Y=-58585.914Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative architectural elements at the top\", \"filename\": \"X=4985.128Y=-57857.129Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"light\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance\", \"filename\": \"X=8991.899Y=-34450.414Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with intricate moldings\", \"filename\": \"X=10679.539Y=-26682.504Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate upper facade\", \"filename\": \"X=8750.337Y=-53835.359Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top\", \"filename\": \"X=10635.318Y=-57219.371Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=10643.955Y=-30795.832Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangle\", \"feature\": \"windows with arches and decorative elements\", \"filename\": \"X=6731.806Y=-25419.336Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arches\", \"filename\": \"X=7076.454Y=-38179.211Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid windows\", \"filename\": \"X=8607.752Y=4237.960Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing near the roofline\", \"filename\": \"X=-34929.418Y=3056.724Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with a setback\", \"feature\": \"grid of windows\", \"filename\": \"X=13565.113Y=28777.337Z=618.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=21553.869Y=-57929.406Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=15283.327Y=-55159.926Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, decorative facade\", \"filename\": \"X=20448.779Y=-57768.660Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arch windows\", \"filename\": \"X=18660.488Y=-58012.867Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=23492.920Y=-53823.840Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with decorative molding\", \"filename\": \"X=23393.180Y=-55159.926Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=10017.494Y=45864.117Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=19434.902Y=-48032.207Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative reliefs\", \"filename\": \"X=15787.706Y=-46023.297Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=-68806.844Y=11941.136Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"rows of windows, classical design elements, decorative cornices\", \"filename\": \"X=21082.996Y=-19326.473Z=1320.000.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"stepped\", \"feature\": \"windows\", \"filename\": \"X=12978.263Y=59751.582Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornices and multiple windows\", \"filename\": \"X=7645.246Y=-53894.672Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"gothic\", \"filename\": \"X=59420.934Y=40074.312Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with large windows\", \"filename\": \"X=23721.906Y=-44136.871Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=18035.514Y=-54105.590Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=15243.961Y=-58755.363Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=-33156.812Y=-4104.862Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31064.035Y=-54158.824Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arches on top floor\", \"filename\": \"X=23893.158Y=-12637.332Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on ground floor\", \"filename\": \"X=22967.244Y=-48050.836Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arch windows\", \"filename\": \"X=15213.374Y=-57866.125Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19535.342Y=-53908.215Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facades with arched entrance\", \"filename\": \"X=15816.639Y=-43762.672Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"vertical lines\", \"filename\": \"X=20337.806Y=34892.974Z=618.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=23447.492Y=-53077.781Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=15243.959Y=-53077.781Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate carvings near the roofline\", \"filename\": \"X=16500.469Y=-35103.391Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=20131.799Y=-44136.867Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like windows\", \"filename\": \"X=21082.996Y=-19326.473Z=1320.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"perforated facade\", \"filename\": \"X=18368.424Y=51605.438Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass exterior\", \"filename\": \"X=42215.578Y=-25200.551Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with decorative elements\", \"filename\": \"X=20448.779Y=-53851.148Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative architectural details\", \"filename\": \"X=23447.492Y=-58755.352Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-patterned facade\", \"filename\": \"X=-43668.086Y=-19231.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"tiered\", \"feature\": \"multiple setbacks with symmetrical design\", \"filename\": \"X=16443.680Y=-27876.764Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"tapered rectangular\", \"feature\": \"tiered sections\", \"filename\": \"X=16845.029Y=20953.131Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=16845.754Y=3047.031Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate roofline\", \"filename\": \"X=16999.072Y=-39740.047Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"detailed facade with decorative elements\", \"filename\": \"X=23380.572Y=-38305.102Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=20534.619Y=-35073.547Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"light gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative facade\", \"filename\": \"X=15813.385Y=-48974.988Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate upper facade with arched windows\", \"filename\": \"X=17365.781Y=-57846.254Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=21553.869Y=-53786.035Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"reddish-brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=15203.733Y=-56724.945Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=19535.342Y=-57985.680Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice, large windows\", \"filename\": \"X=23420.156Y=-57866.125Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=15225.299Y=-53823.840Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid facade\", \"filename\": \"X=21070.428Y=-39740.051Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with step backs\", \"feature\": \"multiple windows and decorative moldings on top floors\", \"filename\": \"X=21795.457Y=-27876.764Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative stonework\", \"filename\": \"X=2435.607Y=-28110.480Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple rows of windows\", \"filename\": \"X=19083.840Y=-12571.863Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate fa\\u00e7ade with arched windows\", \"filename\": \"X=2474.302Y=-57542.641Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice and repetitive window pattern\", \"filename\": \"X=2440.412Y=-25023.404Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative roof trim\", \"filename\": \"X=8750.333Y=-30326.535Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with decorative elements\", \"filename\": \"X=4809.095Y=-54062.055Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate upper facade\", \"filename\": \"X=8750.333Y=-25644.244Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate window frames\", \"filename\": \"X=10643.959Y=-53386.461Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices and symmetrical windows\", \"filename\": \"X=10596.337Y=-54545.117Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top floor\", \"filename\": \"X=7645.242Y=-25426.852Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"striped\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"horizontal stripes\", \"filename\": \"X=24698.900Y=27816.876Z=645.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental facade\", \"filename\": \"X=7645.247Y=-57787.047Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"uniform windows\", \"filename\": \"X=25111.680Y=59965.008Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=-39883.211Y=31031.211Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on lower levels\", \"filename\": \"X=3640.133Y=-39752.730Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=34307.441Y=-40066.621Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows and decorative elements\", \"filename\": \"X=35829.051Y=-55384.387Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate decorations near the roof\", \"filename\": \"X=28047.498Y=-43575.836Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31046.203Y=-49528.004Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"twin towers\", \"filename\": \"X=53884.828Y=32177.270Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with columns and arches\", \"filename\": \"X=28047.492Y=-49920.211Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=29941.113Y=-44280.703Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and ornate architectural details\", \"filename\": \"X=35851.039Y=-57737.742Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, ornate cornices\", \"filename\": \"X=31938.891Y=-57979.059Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=35828.496Y=-45714.043Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on top floor\", \"filename\": \"X=32001.117Y=-18416.891Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=28604.557Y=-13648.361Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-48370.672Y=9025.331Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many small square windows\", \"filename\": \"X=21105.000Y=45832.418Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"stepped\", \"filename\": \"X=25070.980Y=48777.246Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=28036.742Y=-46037.316Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade on upper floors\", \"filename\": \"X=34307.441Y=-34450.414Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=32834.496Y=-49322.523Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple balconies\", \"filename\": \"X=34742.875Y=5222.956Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=22250.613Y=3509.732Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"detailed fa\\u00e7ade with arched windows\", \"filename\": \"X=27956.400Y=-47535.918Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamental details\", \"filename\": \"X=31959.645Y=-44465.570Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=27992.541Y=-56812.359Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=32852.324Y=-53871.156Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=35719.711Y=-47535.918Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31046.203Y=-44330.254Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=33957.414Y=-54130.422Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade\", \"filename\": \"X=33957.414Y=-57993.715Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tiered design\", \"filename\": \"X=31207.676Y=28592.367Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"stepped\", \"feature\": \"grid-like facade\", \"filename\": \"X=34742.879Y=2171.386Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows, tiered structure\", \"filename\": \"X=31899.969Y=-28006.996Z=78.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arches above windows\", \"filename\": \"X=28047.492Y=-53575.285Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=31938.889Y=-53866.203Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple arched windows\", \"filename\": \"X=35801.883Y=-44618.902Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative cornices\", \"filename\": \"X=35827.492Y=-46537.520Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=35832.660Y=-18476.398Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows\", \"filename\": \"X=28548.932Y=-11158.126Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with decorative elements\", \"filename\": \"X=28000.541Y=-48711.570Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative upper facade\", \"filename\": \"X=29969.320Y=-57955.566Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brick\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=28047.492Y=-58507.160Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall and slender\", \"filename\": \"X=42471.266Y=31566.201Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=29151.566Y=-37632.508Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=35881.773Y=-48711.570Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative rooftop\", \"filename\": \"X=35816.492Y=-53800.426Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices and arched windows\", \"filename\": \"X=32852.324Y=-57990.016Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=-8219.306Y=56329.340Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate arches\", \"filename\": \"X=35851.039Y=-43575.832Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"repetitive window pattern\", \"filename\": \"X=-33870.527Y=-54896.367Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, decorative elements on the top\", \"filename\": \"X=29941.113Y=-49517.008Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top\", \"filename\": \"X=29591.104Y=3960.130Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-pattern windows\", \"filename\": \"X=29324.930Y=10783.445Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"gothic architecture\", \"filename\": \"X=54497.051Y=43591.566Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"balconies on each floor\", \"filename\": \"X=34257.008Y=-37239.648Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=31959.645Y=-49231.688Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative architectural elements\", \"filename\": \"X=30016.174Y=-53948.754Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=33882.355Y=-49555.652Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate upper facade with arched windows\", \"filename\": \"X=28022.727Y=-44618.902Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate details around windows\", \"filename\": \"X=31064.039Y=-57801.367Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade with arched windows in the lower section\", \"filename\": \"X=-16071.300Y=-49353.348Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=41251.039Y=-40429.473Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=29155.676Y=-39752.723Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=28022.949Y=-55623.977Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice and brick facade\", \"filename\": \"X=35851.039Y=-49763.926Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular with a tiered design\", \"feature\": \"large windows\", \"filename\": \"X=32539.625Y=-12996.712Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate top floor design with large windows\", \"filename\": \"X=3629.727Y=-46522.914Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate top\", \"filename\": \"X=8005.302Y=-47922.145Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arches above windows and ornate cornice\", \"filename\": \"X=5231.977Y=-25465.570Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate cornice\", \"filename\": \"X=5012.306Y=-49027.281Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like windows\", \"filename\": \"X=8005.302Y=-45152.277Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with stepped section\", \"feature\": \"grid-like windows\", \"filename\": \"X=31130.270Y=20489.881Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative facade\", \"filename\": \"X=46299.496Y=1194.688Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"stepped design with a tiered structure\", \"filename\": \"X=50581.117Y=47507.875Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like windows\", \"filename\": \"X=9457.213Y=-19231.311Z=618.777.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative upper facade\", \"filename\": \"X=11057.474Y=4246.259Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative facade\", \"filename\": \"X=43920.152Y=-39931.090Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and ornate architectural details\", \"filename\": \"X=46972.883Y=-37336.262Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate details along the top\", \"filename\": \"X=46975.215Y=-38806.293Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative arched windows and detailed cornices\", \"filename\": \"X=41251.039Y=-33901.348Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"pink\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=-42693.129Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with arched windows\", \"filename\": \"X=43006.719Y=-34867.738Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative windows and stone carvings\", \"filename\": \"X=45025.242Y=-39980.902Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"vertical windows\", \"filename\": \"X=-37916.676Y=21593.258Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top\", \"filename\": \"X=46299.480Y=-19977.588Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows with decorative elements\", \"filename\": \"X=41127.742Y=-37336.262Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched entrance, decorative elements\", \"filename\": \"X=46236.469Y=-30220.078Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=53675.855Y=-25271.049Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on lower floors\", \"filename\": \"X=42215.590Y=5509.086Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=23426.715Y=-56724.938Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular with tiered sections\", \"feature\": \"large windows\", \"filename\": \"X=48204.055Y=25834.527Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at top, grid-like window pattern\", \"filename\": \"X=48055.176Y=-44449.883Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows, balconies\", \"filename\": \"X=46918.867Y=-33901.348Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=46918.859Y=-40429.473Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=46299.484Y=-25126.607Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and ornate roof decoration\", \"filename\": \"X=43152.238Y=-44051.043Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on the top floors\", \"filename\": \"X=42979.863Y=-39885.316Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices, arched windows\", \"filename\": \"X=5856.952Y=-30170.094Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=48814.781Y=37247.316Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=41204.750Y=-38806.293Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facades with arched entrance\", \"filename\": \"X=44223.547Y=-45833.895Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows with arches above them\", \"filename\": \"X=43920.152Y=-34805.695Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"historical\", \"filename\": \"X=41670.348Y=-19231.309Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"numerous windows\", \"filename\": \"X=46299.480Y=-17782.441Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade with windows\", \"filename\": \"X=42156.426Y=-27841.033Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows near the top\", \"filename\": \"X=46499.543Y=-13049.691Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"gothic-style architecture\", \"filename\": \"X=42215.590Y=2457.518Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade\", \"filename\": \"X=20337.806Y=34892.974Z=618.777.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"striped pattern with glass windows\", \"filename\": \"X=42215.578Y=-30517.246Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"columns and arches\", \"filename\": \"X=-23211.441Y=10009.178Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornamented top\", \"filename\": \"X=45025.242Y=-34715.516Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"uniform windows\", \"filename\": \"X=53041.246Y=-35645.043Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices\", \"filename\": \"X=46866.965Y=-35276.617Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows with decorative arches\", \"filename\": \"X=41072.344Y=-47801.359Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative windows with arches\", \"filename\": \"X=2398.992Y=-29674.035Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice\", \"filename\": \"X=5856.956Y=-53836.438Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tiered design\", \"filename\": \"X=52120.551Y=21918.789Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with a smaller upper section\", \"feature\": \"uniform window grid pattern\", \"filename\": \"X=38554.770Y=35481.941Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative upper cornice\", \"filename\": \"X=8750.337Y=-57872.699Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"gothic-style\", \"filename\": \"X=9978.507Y=-38200.730Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative crown\", \"filename\": \"X=44898.660Y=41163.430Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid of windows\", \"filename\": \"X=4092.465Y=5509.087Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular with staggered sections\", \"feature\": \"lots of windows\", \"filename\": \"X=724.893Y=32750.332Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"lots of windows\", \"filename\": \"X=62927.391Y=-6840.476Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=10643.954Y=-25098.547Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows and decorative stonework\", \"filename\": \"X=41225.859Y=-35545.512Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=41237.547Y=-36584.395Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many small windows\", \"filename\": \"X=32941.355Y=41094.258Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tiered setbacks\", \"filename\": \"X=38931.355Y=47130.730Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on the lower floors\", \"filename\": \"X=-36268.926Y=21393.189Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"tiered\", \"feature\": \"grid-like window structure\", \"filename\": \"X=-7205.009Y=38400.547Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"classical design with intricate detailing and large arched entryway\", \"filename\": \"X=62309.953Y=-12741.026Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass windows\", \"filename\": \"X=-21223.066Y=-58341.027Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many small square windows\", \"filename\": \"X=55761.164Y=-27538.449Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"high-rise\", \"shape\": \"rectangular with setbacks\", \"feature\": \"tiered levels\", \"filename\": \"X=35013.984Y=51048.094Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6731.806Y=-30251.314Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative upper facade\", \"filename\": \"X=6731.811Y=-58035.273Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like facade with large windows\", \"filename\": \"X=58532.066Y=36824.500Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate\", \"filename\": \"X=58531.336Y=-19231.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arches at the entrance\", \"filename\": \"X=61142.652Y=-19231.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-like window pattern\", \"filename\": \"X=63779.473Y=-3107.385Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"many arched windows, decorative details on the upper facade\", \"filename\": \"X=-60026.609Y=28596.311Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative facade\", \"filename\": \"X=57603.730Y=-12741.024Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-pattern windows\", \"filename\": \"X=65843.484Y=24974.840Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular with a dome roof\", \"feature\": \"dome\", \"filename\": \"X=31899.969Y=-28006.996Z=78.743.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"multiple windows\", \"filename\": \"X=3640.128Y=-35157.180Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornice and window arches\", \"filename\": \"X=4809.091Y=-30023.965Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows on the top floor\", \"filename\": \"X=10602.049Y=-55720.766Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=6731.810Y=-53829.234Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative architectural details\", \"filename\": \"X=-65221.070Y=-14189.893Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"dark\", \"size\": \"high-rise\", \"shape\": \"tiered\", \"feature\": \"stepped design\", \"filename\": \"X=13565.113Y=28777.337Z=618.404.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=-36523.805Y=42524.223Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-patterned glass facade\", \"filename\": \"X=2562.584Y=-48998.234Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"many windows\", \"filename\": \"X=2177.017Y=2457.518Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"brown\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"stepped design\", \"filename\": \"X=-3194.237Y=30491.787Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"white\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"striped windows\", \"filename\": \"X=-9163.400Y=-34875.344Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=2444.815Y=-26922.094Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows\", \"filename\": \"X=2440.412Y=-53492.762Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with vertical columns\", \"filename\": \"X=-9480.223Y=-4019.238Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"black\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"glass facade\", \"filename\": \"X=3624.455Y=-37632.512Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=-3811.636Y=-40104.375Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"high-rise\", \"shape\": \"rectangular\", \"feature\": \"tall with repetitive windows\", \"filename\": \"X=9444.236Y=1194.690Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"arched windows at the top\", \"filename\": \"X=10654.197Y=-28904.617Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade\", \"filename\": \"X=2440.413Y=-30795.832Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"low-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative cornices\", \"filename\": \"X=2431.472Y=-54545.117Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"red\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate detailing at the top\", \"filename\": \"X=2443.202Y=-55720.766Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"decorative details above windows\", \"filename\": \"X=7645.242Y=-30201.930Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"grid-patterned glass facade\", \"filename\": \"X=10907.353Y=-45954.680Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"gray\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"large windows\", \"filename\": \"X=41870.402Y=-14189.891Z=0.png\"}\r\n{\"type\": \"building\", \"color\": \"grey\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"ornate facade with repetitive arched and rectangular windows\", \"filename\": \"X=9457.213Y=-19231.311Z=618.png\"}\r\n{\"type\": \"building\", \"color\": \"beige\", \"size\": \"mid-rise\", \"shape\": \"rectangular\", \"feature\": \"windows in a grid pattern\", \"filename\": \"X=3640.135Y=-44402.699Z=0.png\"}\r\n"
  },
  {
    "path": "scripts/sim/airsim_bridge.py",
    "content": "import argparse\nimport io\nimport math\nimport os\nimport socket\nimport subprocess\nimport threading\nimport time\nfrom datetime import datetime\n\nimport airsim\nimport cv2  # OpenCV\nimport numpy as np\nimport yaml\nfrom common import *\n\n\nclass AirsimBridge:\n    def __init__(self, env_name):\n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_airsim_sim)\n        self._sim_thread.start()\n        time.sleep(10)\n\n        self._client = airsim.MultirotorClient()\n        self._client.confirmConnection()\n        self._client.enableApiControl(True)\n        self._client.armDisarm(True)\n\n    def _init_airsim_sim(self):\n        env_dir = \"envs/airsim/\" + self.env_name\n\n        if not os.path.exists(env_dir):\n            raise ValueError(f\"Specified directory {env_dir} does not exist\")\n        \n        command = [\"bash\", f\"{env_dir}/LinuxNoEditor/start.sh\"]\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        print(\"Command output:\\n\", stdout)\n\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll):\n        target_pose = airsim.Pose(airsim.Vector3r(x, -y, -z),\n                                  airsim.to_quaternion(0, 0, math.radians(-yaw)))\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        self._client.simSetVehiclePose(target_pose, True)\n\n    def set_drone_pos(self, x, y, z, pitch, yaw, roll):\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        qua = euler_to_quaternion(pitch, -yaw, roll)\n        target_pose = airsim.Pose(airsim.Vector3r(x, y, z),\n                                  airsim.Quaternionr(qua[0], qua[1], qua[2], qua[3]))\n        self._client.simSetVehiclePose(target_pose, True)\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        time.sleep(0.1)\n\n    def _camera_init(self):\n        '''Camera initialization'''\n        camera_pose = airsim.Pose(airsim.Vector3r(0, 0, 0), airsim.to_quaternion(math.radians(15), 0, 0))\n        self._client.simSetCameraPose(\"0\", camera_pose)\n        time.sleep(1)\n\n    def _drone_init(self):\n        '''Drone initialization'''\n        self.set_drone_pos(0, 0, 0, 0, 0, 0)\n        time.sleep(1)\n\n    def get_camera_data(self, camera_type):\n        valid_types = {'color', 'object_mask', 'depth'}\n        if camera_type not in valid_types:\n            raise ValueError(f\"Invalid camera type. Expected one of {valid_types}, but got '{camera_type}'.\")\n\n        if camera_type == 'color':\n            image_type = airsim.ImageType.Scene\n        elif camera_type == 'depth':\n            image_type = airsim.ImageType.DepthPlanar\n        else:\n            image_type = airsim.ImageType.Segmentation\n\n        responses = self._client.simGetImages([airsim.ImageRequest('front_custom', image_type, False, False)])\n        response = responses[0]\n        if response.pixels_as_float:\n            img_data = np.array(response.image_data_float, dtype=np.float32)\n            img_data = np.reshape(img_data, (response.height, response.width))\n        else:\n            img_data = np.frombuffer(response.image_data_uint8, dtype=np.uint8)\n            img_data = img_data.reshape(response.height, response.width, 3)\n\n        return img_data\n\n    def save_image(self, image_data, file_path):\n        cv2.imwrite(file_path, image_data)\n\n    def process_camera_data(self, file_path, camera_type='color'):\n        img = self.get_camera_data(camera_type)\n        self.save_image(img, file_path)\n        print(\"Image saved\")\n\n\nclass TCPServer:\n    def __init__(self, host='0.0.0.0', port=9999):\n        self.server_address = (host, port)\n        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.server_socket.bind(self.server_address)\n        self.server_socket.listen(1)\n        print(f\"Listening on {host}:{port}\")\n\n    def accept_connection(self):\n        conn, addr = self.server_socket.accept()\n        print(f\"Connected by {addr}\")\n        return conn\n\n    def receive_pose(self, conn):\n        data = conn.recv(1024).decode()\n        print(\"Data received:\", data)\n        return data\n\n\ndef handle_planner(config_params, env_name):\n    airsim_bridge = AirsimBridge(env_name)\n    tcp_server = TCPServer(port=config_params['aim_port'])\n\n    file_path = \"uav_vln_data/test/\"\n    image_id = 0\n    while True:\n        print(\"Ready to connect\")\n        conn = tcp_server.accept_connection()\n        print(\"Connection established!\")\n\n        while True:\n            data = tcp_server.receive_pose(conn)\n            if not data:\n                break\n\n            if \"path:\" in data:\n                file_path = data.split(\"path:\")[1]\n                image_id = 0\n                print(\"File path:\", file_path)\n                continue\n\n            pose = list(map(float, data.split(',')))\n            print(f\"Received pose: {pose}\")\n\n            airsim_bridge.set_camera_pose(*pose)\n            time.sleep(0.1)\n            current_time_str = datetime.now().strftime(\"%Y%m%d_%H%M%S_\")\n            airsim_bridge.process_camera_data(file_path=file_path + current_time_str + str(image_id) + '.png')\n            image_id += 1\n            print(\"Image saved!\")\n\n\ndef load_config(config_file=\"config.yaml\"):\n    with open(config_file, 'r') as f:\n        config = yaml.safe_load(f)\n    return config['thread_params']\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Environment name\")\n    parser.add_argument('--env', type=str, default='env_airsim_18', help=\"input env name\")\n    args = parser.parse_args()\n\n    config_file = \"configs/\" + args.env + \".yaml\"\n    planner_configs = load_config(config_file)\n\n    threads = []\n    for config in planner_configs:\n        thread = threading.Thread(target=handle_planner, args=(config, args.env))\n        threads.append(thread)\n        thread.start()\n\n    for thread in threads:\n        thread.join()\n\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "scripts/sim/common.py",
    "content": "import math\nimport numpy as np\nfrom math import atan2, asin, degrees, cos, sin, sqrt\n\ndef euler_to_quaternion(roll, pitch, yaw):\n    \"\"\"\n    Convert Euler angles (roll, pitch, yaw) to quaternion.\n    The order of Euler angles is yaw-pitch-roll (Z-Y-X axis rotation order).\n    \"\"\"\n    cy = math.cos(yaw * 0.5)\n    sy = math.sin(yaw * 0.5)\n    cp = math.cos(pitch * 0.5)\n    sp = math.sin(pitch * 0.5)\n    cr = math.cos(roll * 0.5)\n    sr = math.sin(roll * 0.5)\n\n    w = cy * cp * cr + sy * sp * sr\n    x = cy * cp * sr - sy * sp * cr\n    y = sy * cp * cr + cy * sp * sr\n    z = sy * cp * sr - cy * sp * cr\n    \n    return [x, y, z, w]\n\ndef quaternion_to_rotation_matrix(QW, QX, QY, QZ):\n    \"\"\"\n    Convert a quaternion to a rotation matrix.\n    \"\"\"\n    R = np.array([\n        [1 - 2*(QY**2 + QZ**2), 2*(QX*QY - QZ*QW), 2*(QX*QZ + QY*QW)],\n        [2*(QX*QY + QZ*QW), 1 - 2*(QX**2 + QZ**2), 2*(QY*QZ - QX*QW)],\n        [2*(QX*QZ - QY*QW), 2*(QY*QZ + QX*QW), 1 - 2*(QX**2 + QY**2)]\n    ])\n    return R\n\ndef calculate_camera_position(QW, QX, QY, QZ, TX, TY, TZ):\n    \"\"\"\n    Calculate the camera position in the world coordinate system.\n    \"\"\"\n    R = quaternion_to_rotation_matrix(QW, QX, QY, QZ)\n    R_t = R.T\n    T = np.array([TX, TY, TZ])\n    camera_position_world = -R_t.dot(T)\n    return camera_position_world\n\ndef rotation_matrix_to_euler_angles(R):\n    \"\"\"\n    Convert rotation matrix to Euler angles (roll, pitch, yaw).\n    \"\"\"\n    pitch = asin(-R[2, 0])\n    if abs(R[2, 0]) != 1:\n        roll = atan2(R[2, 1], R[2, 2])\n        yaw = atan2(R[1, 0], R[0, 0])\n    else:\n        roll = atan2(-R[1, 2], R[1, 1])\n        yaw = 0\n    \n    roll = degrees(roll)\n    pitch = degrees(pitch)\n    yaw = degrees(yaw)\n    \n    return roll, pitch, yaw\n\ndef cam2world(QW, QX, QY, QZ, TX, TY, TZ):\n    camera_position = calculate_camera_position(QW, QX, QY, QZ, TX, TY, TZ)\n    formatted_position = [f\"{x:.8f}\" for x in camera_position]\n    print(f\"Camera position in world coordinates: {', '.join(formatted_position)}\")\n\n    R = quaternion_to_rotation_matrix(QW, QX, QY, QZ)\n    roll, pitch, yaw = rotation_matrix_to_euler_angles(R)\n    print(f\"Camera's orientation (Roll, Pitch, Yaw): {roll}, {pitch}, {yaw}\")\n    rpy = [roll, pitch, yaw]\n\n    return camera_position, rpy\n\ndef euler_to_rotation_matrix(roll, pitch, yaw):\n    \"\"\"\n    Convert Euler angles (roll, pitch, yaw) to rotation matrix.\n    \"\"\"\n    roll = np.radians(roll)\n    pitch = np.radians(pitch)\n    yaw = np.radians(yaw)\n    \n    R_x = np.array([\n        [1, 0, 0],\n        [0, cos(roll), -sin(roll)],\n        [0, sin(roll), cos(roll)]\n    ])\n    \n    R_y = np.array([\n        [cos(pitch), 0, sin(pitch)],\n        [0, 1, 0],\n        [-sin(pitch), 0, cos(pitch)]\n    ])\n    \n    R_z = np.array([\n        [cos(yaw), -sin(yaw), 0],\n        [sin(yaw), cos(yaw), 0],\n        [0, 0, 1]\n    ])\n    \n    R = np.dot(R_z, np.dot(R_y, R_x))\n    return R\n\ndef rotation_matrix_to_quaternion(R):\n    \"\"\"\n    Convert rotation matrix to quaternion (QW, QX, QY, QZ).\n    \"\"\"\n    trace = R[0, 0] + R[1, 1] + R[2, 2]\n    \n    if trace > 0:\n        S = sqrt(trace + 1.0) * 2\n        QW = 0.25 * S\n        QX = (R[2, 1] - R[1, 2]) / S\n        QY = (R[0, 2] - R[2, 0]) / S\n        QZ = (R[1, 0] - R[0, 1]) / S\n    else:\n        i = np.argmax([R[0, 0], R[1, 1], R[2, 2]])\n        if i == 0:\n            S = sqrt(1.0 + R[0, 0] - R[1, 1] - R[2, 2]) * 2\n            QW = (R[2, 1] - R[1, 2]) / S\n            QX = 0.25 * S\n            QY = (R[0, 1] + R[1, 0]) / S\n            QZ = (R[0, 2] + R[2, 0]) / S\n        elif i == 1:\n            S = sqrt(1.0 + R[1, 1] - R[0, 0] - R[2, 2]) * 2\n            QW = (R[0, 2] - R[2, 0]) / S\n            QX = (R[0, 1] + R[1, 0]) / S\n            QY = 0.25 * S\n            QZ = (R[1, 2] + R[2, 1]) / S\n        else:\n            S = sqrt(1.0 + R[2, 2] - R[0, 0] - R[1, 1]) * 2\n            QW = (R[1, 0] - R[0, 1]) / S\n            QX = (R[0, 2] + R[2, 0]) / S\n            QY = (R[1, 2] + R[2, 1]) / S\n            QZ = 0.25 * S\n    \n    return QW, QX, QY, QZ\n\ndef calculate_camera_position_from_world(R_t, camera_position):\n    \"\"\"\n    Given camera_position_world and the transpose of the rotation matrix R_t,\n    calculate the translation vector T.\n    \"\"\"\n    T = -R_t.dot(camera_position)\n    return T\n\ndef world2cam(x, y, z, roll, pitch, yaw):\n    R = euler_to_rotation_matrix(roll, pitch, yaw)\n    QW, QX, QY, QZ = rotation_matrix_to_quaternion(R)\n    print(f\"Quaternion (QW, QX, QY, QZ): {QW}, {QX}, {QY}, {QZ}\")\n\n    T = calculate_camera_position_from_world(R, np.array([x, y, z]))\n    formatted_T = [f\"{t:.8f}\" for t in T]\n    print(f\"Camera position in camera coordinates (TX, TY, TZ): {', '.join(formatted_T)}\")\n\n    return QW, QX, QY, QZ, T[0], T[1], T[2]\n\ndef world2cam_WXYZ(x, y, z, QW, QX, QY, QZ):\n    \"\"\"\n    Converts world coordinates and quaternion to camera coordinates (position and orientation).\n    \"\"\"\n    R = quaternion_to_rotation_matrix(QW, QX, QY, QZ)\n    position_world = np.array([x, y, z])\n    camera_position = calculate_camera_position_from_world(R, position_world)\n    formatted_camera_position = [f\"{t:.8f}\" for t in camera_position]\n    print(f\"Camera position in camera coordinates (TX, TY, TZ): {', '.join(formatted_camera_position)}\")\n\n    return camera_position"
  },
  {
    "path": "scripts/sim/env_bridge.py",
    "content": "import socket\nimport time\nimport threading\nimport argparse\nfrom datetime import datetime\n\nimport numpy as np  \nimport yaml\n\nfrom ue_bridge import UEBridge\nfrom airsim_bridge import AirsimBridge\nfrom gs_bridge import GSBridge\n\n\nclass TCPServer:\n    def __init__(self, host='0.0.0.0', port=9999):\n        self.server_address = (host, port)\n        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.server_socket.bind(self.server_address)\n        self.server_socket.listen(1)\n        print(f\"Listening on {host}:{port}\")\n    \n    def accept_connection(self):\n        conn, addr = self.server_socket.accept()\n        print(f\"Connected by {addr}\")\n        return conn\n    \n    def receive_pose(self, conn):\n\n        data = conn.recv(1024).decode()\n        return data\n\n\ndef load_config(config_file=\"config.yaml\"):\n    with open(config_file, 'r') as f:\n        config = yaml.safe_load(f)\n    return config['thread_params']\n\n\n\ndef handle_planner(config_params, env_name):\n\n    if 'ue' in env_name:\n        env_bridge = UEBridge(config_params['sim_ip'], config_params['sim_port'], env_name)\n    elif 'airsim' in env_name:\n        env_bridge = AirsimBridge(env_name)\n    elif 'gs' in env_name:\n        env_bridge = GSBridge(env_name)\n    else:\n        print(\"Invalid env name!\")\n        return\n\n    tcp_server = TCPServer(port=config_params['aim_port'])\n\n    file_path = \"uav_vln_data/test/\"\n\n    image_id = 0\n\n    while True:\n\n        print(\"ready to be connected\")\n        conn = tcp_server.accept_connection()\n        print(\"Connection established!\")\n\n        while True:\n\n            data = tcp_server.receive_pose(conn)\n            if not data:\n                break \n\n            if \"path:\" in data:\n                file_path = data.split(\"path:\")[1]\n                image_id = 0\n                print(\"file_path:\", file_path)\n                continue\n\n            pose = list(map(float, data.split(',')))\n            if len(pose) != 6:\n                print(\"Invalid pose data!\")\n                continue\n            print(f\"Received pose: {pose}\")\n            \n            if 'gs' in env_name:\n                env_bridge.set_camera_pose(*pose, file_path)\n            else:\n                env_bridge.set_camera_pose(*pose)\n\n            time.sleep(0.1)\n\n            current_time_str = datetime.now().strftime(\"%Y%m%d_%H%M%S_\")\n\n            env_bridge.process_camera_data(file_path = file_path + current_time_str +  str(image_id) + '.png')\n            # env_bridge.process_camera_data(file_path = file_path +'0.png')\n            image_id += 1\n            print(\"Image saved!\")\n\n\ndef main():\n    \n    parser = argparse.ArgumentParser(description=\"env name\")\n    parser.add_argument('--env', type=str, default='env_gs_sjtu02', help=\"input env name\")\n\n    args = parser.parse_args()\n    config_file = \"configs/\" + args.env + \".yaml\"\n\n    planner_configs = load_config(config_file)\n\n    threads = []\n    for config in planner_configs:\n        thread = threading.Thread(target=handle_planner, args=(config, args.env))\n        threads.append(thread)\n        thread.start()\n\n    for thread in threads:\n        thread.join()\n\nif __name__ == \"__main__\":\n    main()\n\n"
  },
  {
    "path": "scripts/sim/gs_bridge.py",
    "content": "import subprocess\nimport os\nimport requests\nimport argparse\nimport psutil\nimport socket \nimport numpy as np  \nimport time\nimport threading\nimport yaml\nfrom common import *\n\nclass GSBridge:  \n    def __init__(self, env_name):  \n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_gs_sim)\n        self._sim_thread.start()\n        self.url = \"http://localhost:18080/render\"\n        time.sleep(10)\n  \n    def _init_gs_sim(self):\n        dataset_dir = \"envs/gs/\" + self.env_name  \n        gs_vis_tool_dir = \"envs/gs/SIBR_viewers/\"  \n        if not os.path.exists(dataset_dir):\n            raise ValueError(f\"Specified directory {dataset_dir} does not exist\")\n        command = [\n            gs_vis_tool_dir + \"install/bin/SIBR_gaussianHierarchyViewer_app\",\n            \"--path\", f\"{dataset_dir}/camera_calibration/aligned\",\n            \"--scaffold\", f\"{dataset_dir}/output/scaffold/point_cloud/iteration_30000\",\n            \"--model-path\", f\"{dataset_dir}/output/merged.hier\",\n            \"--images-path\", f\"{dataset_dir}/camera_calibration/rectified/images\"\n        ]\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        print(\"Command output:\\n\", stdout)\n\n    def transform_euler_to_new_frame(self, roll, pitch, yaw):\n        R = euler_to_rotation_matrix(roll, pitch, yaw)\n        transformation_matrix = np.array([\n            [0, -1, 0],\n            [1, 0, 0],\n            [0, 0, -1]\n        ])\n        new_R = np.dot(transformation_matrix, R)\n        new_roll, new_pitch, new_yaw = rotation_matrix_to_euler_angles(new_R)\n        return new_roll, new_pitch, new_yaw\n    \n    def rotation_matrix_roll(self, roll):\n        return np.array([\n            [1, 0, 0],\n            [0, np.cos(roll), -np.sin(roll)],\n            [0, np.sin(roll), np.cos(roll)]\n        ])\n\n    def rotation_matrix_pitch(self, pitch):\n        return np.array([\n            [np.cos(pitch), 0, np.sin(pitch)],\n            [0, 1, 0],\n            [-np.sin(pitch), 0, np.cos(pitch)]\n        ])\n\n    def rotation_matrix_yaw(self, yaw):\n        return np.array([\n            [np.cos(yaw), -np.sin(yaw), 0],\n            [np.sin(yaw), np.cos(yaw), 0],\n            [0, 0, 1]\n        ])\n\n    def transform_to_camera_frame(self, roll, pitch, yaw):\n        R_roll = self.rotation_matrix_roll(roll)\n        R_pitch = self.rotation_matrix_pitch(pitch)\n        R_yaw = self.rotation_matrix_yaw(yaw)\n        R_combined = np.dot(R_pitch, np.dot(R_yaw, R_roll))\n        QW, QX, QY, QZ = rotation_matrix_to_quaternion(R_combined)\n        print(f\"QW: {QW}, QX: {QX}, QY: {QY}, QZ: {QZ}\")\n        transformation_matrix = np.array([\n            [0, -1, 0],\n            [0, 0, -1],\n            [1, 0, 0]\n        ])\n        new_R = np.dot(transformation_matrix, R_combined)\n        QW_new, QX_new, QY_new, QZ_new = rotation_matrix_to_quaternion(new_R)\n        return QW_new, QX_new, QY_new, QZ_new\n\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll, path_params):\n        yaw = -yaw\n        pitch = -40\n        QW, QX, QY, QZ = self.transform_to_camera_frame(math.radians(roll), math.radians(pitch), math.radians(yaw))\n        camera_position = world2cam_WXYZ(x, y, z, QW, QX, QY, QZ)\n        quat = [QW, QX, QY, QZ]\n        camera_id = 0\n        image_name = \"00000000.png\"\n        image_data = f\"{camera_id} {' '.join(map(str, quat))} {' '.join(map(str, [camera_position[0], camera_position[1], camera_position[2]]))} {0} {image_name}\"\n        camera_params = f\"0 PINHOLE 1436 1077 718.861 718.861 718 538.5\"\n        data = {\n            \"camera\": camera_params,\n            \"image\": image_data,\n            \"path\": path_params\n        }\n        print(data)\n        try:\n            response = requests.post(self.url, data=data)\n            if response.status_code == 200:\n                print(\"Request successful!\")\n                print(response.text) \n            else:\n                print(f\"Request failed, status code: {response.status_code}\")\n                print(response.text)\n            memory = psutil.virtual_memory()\n            print(memory.percent)\n            if memory.percent >= 90:\n                print(\"Memory usage is above 90%\")\n                self.process.terminate()\n                self.__init__()\n        except requests.RequestException as e:\n            print(f\"Error during request: {e}\")\n            time.sleep(20)\n\n    def process_camera_data(self, file_path):\n        pass\n\nclass TCPServer:\n    def __init__(self, host='0.0.0.0', port=9999):\n        self.server_address = (host, port)\n        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.server_socket.bind(self.server_address)\n        self.server_socket.listen(1)\n        print(f\"Listening on {host}:{port}\")\n    \n    def accept_connection(self):\n        conn, addr = self.server_socket.accept()\n        print(f\"Connected by {addr}\")\n        return conn\n    \n    def receive_pose(self, conn):\n        data = conn.recv(1024).decode()\n        print(\"data\", data)\n        return data\n\ndef handle_planner(config_params, env_name):\n    gs_bridge = GSBridge(env_name)\n    tcp_server = TCPServer(port=config_params['aim_port'])\n    file_path = \"uav_vln_data/test\"\n    image_id = 0\n    while True:\n        print(\"ready connet\")\n        conn = tcp_server.accept_connection()\n        print(\"Connection established!\")\n        while True:\n            data = tcp_server.receive_pose(conn)\n            if not data:\n                break\n            if \"path:\" in data:\n                file_path = data.split(\"path:\")[1]\n                image_id = 0\n                print(\"file_path:\", file_path)\n                continue\n            pose = list(map(float, data.split(',')))\n            if len(pose) != 6:\n                print(\"Invalid pose data!\")\n                continue\n            print(f\"Received pose: {pose}\")\n            gs_bridge.set_camera_pose(*pose, file_path)\n            time.sleep(0.1)\n            image_id += 1\n            print(\"Image saved!\")\n\ndef load_config(config_file=\"config.yaml\"):\n    with open(config_file, 'r') as f:\n        config = yaml.safe_load(f)\n    return config['thread_params']\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"env name\")\n    parser.add_argument('--env', type=str, default='env_gs_sjtu02', help=\"input env name\")\n    args = parser.parse_args()\n    config_file = \"configs/\" + args.env + \".yaml\"\n    planner_configs = load_config(config_file)\n    threads = []\n    for config in planner_configs:\n        thread = threading.Thread(target=handle_planner, args=(config, args.env))\n        threads.append(thread)\n        thread.start()\n    for thread in threads:\n        thread.join()\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "scripts/sim/gtav_bridge.py",
    "content": "#!/usr/bin/env python3\r\n# -*- coding: utf-8 -*-\r\n\r\nfrom utils.Constants import IMG_WIDTH, IMG_HEIGHT\r\n\r\nfrom deepgtav.messages import Start, Stop, Scenario, Dataset, Commands, frame2numpy, GoToLocation, TeleportToLocation, SetCameraPositionAndRotation\r\nfrom deepgtav.messages import StartRecording, StopRecording, SetClockTime, SetWeather, CreatePed\r\nfrom deepgtav.client import Client\r\n\r\nfrom utils.BoundingBoxes import add_bboxes, parseBBox2d_LikePreSIL, parseBBoxesVisDroneStyle, parseBBox_YoloFormatStringToImage\r\nfrom utils.utils import save_image_and_bbox, save_meta_data, getRunCount, generateNewTargetLocation\r\nfrom scipy.spatial.transform import Rotation as R\r\nimport argparse\r\nimport time\r\nimport cv2\r\nimport matplotlib.pyplot as plt\r\nfrom PIL import Image\r\nfrom random import uniform\r\nfrom math import sqrt\r\nimport numpy as np\r\nimport pyautogui\r\nimport os\r\nimport base64\r\nimport shutil\r\nimport select\r\nimport socket\r\nfrom datetime import datetime\r\nimport threading\r\nimport yaml\r\nfrom pathlib import Path\r\n\r\nclass GTAVBridge:\r\n    def __init__(self, host='127.0.0.1', port=8000, save_dir='./output'):\r\n        self.host = '127.0.0.1'\r\n        self.port = 8000\r\n        self.save_dir = os.path.normpath(save_dir)\r\n        self.client = Client(ip=self.host, port=self.port)\r\n        scenario = Scenario(drivingMode=[786603,0], vehicle=\"voltic\", location=[245.23306274414062, -998.244140625, 29.205352783203125])\r\n        dataset = Dataset(location=True, time=True, exportBBox2D=True, segmentationImage=True, exportLiDAR=True, maxLidarDist=5000)   \r\n        self.client.sendMessage(Start(scenario=scenario, dataset=dataset))  \r\n\r\n        self.test_x = 0.0\r\n        self.test_y = 0.0\r\n        self.test_z = 120.0\r\n\r\n        self.test_roll = 0.0\r\n        self.test_pitch = 0.0\r\n        self.test_yaw = 0.0\r\n\r\n    def setup_scenario(self):\r\n        \"\"\"Set up the scenario, dataset, and send start command\"\"\"\r\n        scenario = Scenario(drivingMode=[786603,0], vehicle=\"voltic\", location=[245.23306274414062, -998.244140625, 29.205352783203125])\r\n        dataset = Dataset(location=True, time=True, exportBBox2D=True, segmentationImage=True, exportLiDAR=True, maxLidarDist=5000)   \r\n        self.client.sendMessage(Start(scenario=scenario, dataset=dataset))\r\n\r\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll):\r\n        pitch = 0.0 \r\n        roll = 0.0\r\n\r\n        rotation = R.from_euler('y', 90, degrees=True)  # Inverse rotation\r\n        rotation_matrix = rotation.as_matrix()\r\n\r\n        out_x = -y\r\n        out_y = x\r\n        position = np.array([out_x, out_y, z])\r\n        rotated_position = np.dot(rotation_matrix, position)\r\n\r\n        rotation_angles = np.array([roll, pitch, yaw])  # [pitch, yaw, roll]\r\n        rotated_angles = rotation.inv().apply(rotation_angles)\r\n\r\n        print(f\"Original Position: x: {x}, y: {y}, z: {z}\")\r\n        print(f\"Rotated Position: x: {rotated_position[0]}, y: {rotated_position[1]}, z: {rotated_position[2]}\")\r\n        print(f\"Original Angles: pitch: {pitch}, yaw: {yaw}, roll: {roll}\")\r\n        print(f\"Rotated Angles: pitch: {rotated_angles[0]}, yaw: {rotated_angles[1]}, roll: {rotated_angles[2]}\")\r\n        \r\n        self.client.sendMessage(SetCameraPositionAndRotation(\r\n            0, 0, -10,\r\n            rotation_angles[0], rotation_angles[1], rotation_angles[2]\r\n        ))\r\n        self.client.sendMessage(TeleportToLocation(position[0], position[1], position[2]))\r\n\r\n        time.sleep(0.02)\r\n\r\n    def process_camera_data(self, file_path):\r\n        output_path = file_path\r\n        output_filename = output_path.replace(\".raw\", \".jpg\")\r\n        print(f\"Converted to {output_filename}\")\r\n        shutil.move(\"./color.raw\", file_path)\r\n\r\n    def press_key_l(self):\r\n        \"\"\"Simulate pressing the 'l' key\"\"\"\r\n        pyautogui.press('l')\r\n\r\n    def move_raw_image(self, source, destination):\r\n        \"\"\"Move raw image file to the destination path\"\"\"\r\n        shutil.move(source, destination)\r\n\r\n    def stop_client(self):\r\n        \"\"\"Stop the client and disconnect\"\"\"\r\n        self.client.sendMessage(Stop())\r\n        self.client.close()\r\n\r\nclass TCPServer:\r\n    def __init__(self, host='0.0.0.0', port=9999, timeout=80):\r\n        self.server_address = (host, port)\r\n        self.timeout = timeout  # Timeout in seconds\r\n        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\r\n        self.server_socket.bind(self.server_address)\r\n        self.server_socket.listen(1)\r\n        print(f\"Listening on {host}:{port}\")\r\n    \r\n    def accept_connection(self):\r\n        conn, addr = self.server_socket.accept()\r\n        print(f\"Connected by {addr}\")\r\n        return conn\r\n    \r\n    def receive_pose(self, conn):\r\n        ready_to_read, _, _ = select.select([conn], [], [], self.timeout)\r\n        \r\n        if ready_to_read:\r\n            data = conn.recv(1024).decode()\r\n            return data\r\n        else:\r\n            print(f\"No data received within {self.timeout} seconds.\")\r\n            return 'timeout'\r\n\r\ndef handle_planner(config_params):\r\n    tcp_server = TCPServer(port=config_params['aim_port'])\r\n    print(\"config_params['ue_ip']:\", config_params['ue_ip'])\r\n    print(\"config_params['ue_port']:\", config_params['ue_port'])\r\n    gtav_bridge = GTAVBridge(host=config_params['ue_ip'], port=config_params['ue_port'])\r\n    file_path = \"./data_gen/tmp\"\r\n\r\n    image_id = 0\r\n    while True:\r\n        print(\"ready to connect\")\r\n        conn = tcp_server.accept_connection()\r\n        print(\"Connection established!\")\r\n        i = 0\r\n        \r\n        while True:\r\n            data = tcp_server.receive_pose(conn)\r\n            if not data:\r\n                break\r\n            if \"path:\" in data:\r\n                base_path = \"./data_gen/color_file/\"\r\n                current_time = time.strftime(\"%Y-%m-%d_%H-%M-%S\", time.localtime())\r\n                image_id = 0\r\n                file_path = os.path.join(base_path, current_time)\r\n                \r\n                if not os.path.exists(file_path):\r\n                    os.makedirs(file_path)\r\n                    print(f\"Directory {file_path} created successfully.\")\r\n                print(\"file_path:\", file_path)\r\n                continue\r\n\r\n            pose = list(map(float, data.split(',')))\r\n            print(f\"Received pose: {pose}\")\r\n\r\n            gtav_bridge.set_camera_pose(*pose)\r\n\r\n            current_time_str = datetime.now().strftime(\"%Y%m%d_%H%M%S_\")\r\n\r\n            gtav_bridge.process_camera_data(file_path + \"\\\\\" + current_time_str + str(image_id) + '.png')\r\n            time.sleep(0.1)\r\n            image_id += 1\r\n            print(\"Image saved!\")\r\n\r\ndef load_config(config_file=\"config.yaml\"):\r\n    with open(config_file, 'r') as f:\r\n        config = yaml.safe_load(f)\r\n    return config['date_gen_config']['thread_params']\r\n\r\ndef main():\r\n    parser = argparse.ArgumentParser(description=\"env name\")\r\n    print(\"test !!\")\r\n    parser.add_argument('--env', type=str, default='env_game_gtav', help=\"input env name\")\r\n\r\n    args = parser.parse_args()\r\n\r\n    print(args.env)\r\n    cur_path = Path(__file__)\r\n    ros_dir = cur_path.parent.parent\r\n    config_file = args.env + \".yaml\"\r\n\r\n    planner_configs = load_config(config_file)\r\n\r\n    threads = []\r\n    for config in planner_configs:\r\n        thread = threading.Thread(target=handle_planner, args=(config,))\r\n        threads.append(thread)\r\n        thread.start()\r\n    \r\n    for thread in threads:\r\n        thread.join()\r\n\r\nif __name__ == '__main__':\r\n    print(\"test\")\r\n    main()"
  },
  {
    "path": "scripts/sim/ue_bridge.py",
    "content": "import argparse\nimport io\nimport os\nimport socket\nimport subprocess\nimport threading\nimport time\nfrom datetime import datetime\n\nimport cv2\nimport numpy as np\nimport yaml\nfrom unrealcv import Client\n\n\nclass UEBridge:\n    def __init__(self, ue_ip, ue_port, env_name):\n        self.kill_failed_process()\n        time.sleep(1)\n\n        port = self.find_available_port()\n        print(f\"Available port: {port}\")\n        self.modify_port_in_ini(port, env_name)\n        ue_port = port\n\n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_ue_sim)\n        self._sim_thread.start()\n        time.sleep(15)\n\n        self._client = Client((ue_ip, ue_port))\n        self._connection_check()\n\n        self._camera_init()\n        print(\"cam init\")\n\n    def find_available_port(self):\n        port = 9000\n        while True:\n            result = subprocess.run(['lsof', f'-i:{port}'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n            netstat_output = result.stdout.decode()\n\n            if f':{port}' not in netstat_output:\n                return port\n            port += 1\n\n    def modify_port_in_ini(self, port, ue_env_name):\n        ini_file = f\"envs/ue/{ue_env_name}/City_UE52/Binaries/Linux/unrealcv.ini\"\n        with open(ini_file, 'r') as file:\n            lines = file.readlines()\n\n        with open(ini_file, 'w') as file:\n            for line in lines:\n                if line.startswith(\"Port=\"):\n                    file.write(f\"Port={port}\\n\")\n                else:\n                    file.write(line)\n\n    def kill_failed_process(self):\n        result = subprocess.run(['pgrep', '-n', 'CrashReport'], stdout=subprocess.PIPE)\n        cr_pid = result.stdout.decode().strip()\n        if len(cr_pid) > 0:\n            subprocess.run(['kill', '-9', cr_pid])\n\n        result = subprocess.run(['pgrep', '-n', 'CitySample'], stdout=subprocess.PIPE)\n        cr_pid = result.stdout.decode().strip()\n        if len(cr_pid) > 0:\n            subprocess.run(['kill', '-9', cr_pid])\n\n    def _init_ue_sim(self):\n        env_dir = \"envs/ue/\" + self.env_name\n        if not os.path.exists(env_dir):\n            raise ValueError(f\"Specified directory {env_dir} does not exist\")\n\n        command = [\"bash\", f\"{env_dir}/CitySample.sh\"]\n\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        print(\"Command output:\\n\", stdout)\n        time.sleep(2)\n\n    def __del__(self):\n        self._client.disconnect()\n\n    def _connection_check(self):\n        '''Check if connected'''\n        if self._client.connect():\n            print('UnrealCV connected successfully')\n        else:\n            print('UnrealCV is not connected')\n            exit()\n\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll):\n        '''Set camera position'''\n        x = x * 100\n        y = - y * 100\n        z = z * 100\n        camera_settings = {\n            'location': {'x': x, 'y': y, 'z': z},\n            'rotation': {'pitch': pitch, 'yaw': -yaw, 'roll': roll}\n        }\n\n        self._client.request('vset /camera/0/location {x} {y} {z}'.format(**camera_settings['location']))\n        self._client.request('vset /camera/1/location {x} {y} {z}'.format(**camera_settings['location']))\n        self._client.request('vset /camera/0/rotation {pitch} {yaw} {roll}'.format(**camera_settings['rotation']))\n        self._client.request('vset /camera/1/rotation {pitch} {yaw} {roll}'.format(**camera_settings['rotation']))\n        print('camera_settings', camera_settings)\n\n    def _camera_init(self):\n        '''Camera initialization'''\n        time.sleep(2)\n        self._client.request('vset /cameras/spawn')\n        self._client.request('vset /camera/1/size 1920 1080')\n        time.sleep(2)\n        self.set_camera_pose(150, 400, 15, 0, 0, 0)  # Initial position\n        time.sleep(2)\n\n    def get_camera_data(self, camera_type):\n        valid_types = {'lit', 'object_mask', 'depth'}\n        if camera_type not in valid_types:\n            raise ValueError(f\"Invalid camera type. Expected one of {valid_types}, but got '{camera_type}'.\")\n\n        if camera_type == 'lit':\n            data = self._client.request('vget /camera/1/lit png')\n            return cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)\n        elif camera_type == 'object_mask':\n            data = self._client.request('vget /camera/1/object_mask png')\n            return cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)\n        elif camera_type == 'depth':\n            data = self._client.request('vget /camera/1/depth npy')\n            depth_np = np.load(io.BytesIO(data))\n            return depth_np  # Return depth data\n\n    def save_image(self, image_data, file_path):\n        cv2.imwrite(file_path, image_data)\n\n    def process_camera_data(self, file_path, camera_type='lit'):\n        img = self.get_camera_data(camera_type)\n        self.save_image(img, file_path)\n\n\nclass TCPServer:\n    def __init__(self, host='0.0.0.0', port=9999):\n        self.server_address = (host, port)\n        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.server_socket.bind(self.server_address)\n        self.server_socket.listen(1)\n        print(f\"Listening on {host}:{port}\")\n\n    def accept_connection(self):\n        conn, addr = self.server_socket.accept()\n        print(f\"Connected by {addr}\")\n        return conn\n\n    def receive_pose(self, conn):\n        data = conn.recv(1024).decode()\n        return data\n\n\ndef load_config(config_file=\"config.yaml\"):\n    with open(config_file, 'r') as f:\n        config = yaml.safe_load(f)\n    return config['thread_params']\n\n\ndef handle_planner(config_params, env_name):\n    ue_bridge = UEBridge(config_params['ue_ip'], config_params['ue_port'], env_name)\n    tcp_server = TCPServer(port=config_params['aim_port'])\n\n    file_path = \"uav_vln_data/test/\"\n    image_id = 0\n    while True:\n        print(\"ready connet\")\n        conn = tcp_server.accept_connection()\n        print(\"Connection established!\")\n\n        while True:\n            data = tcp_server.receive_pose(conn)\n            if not data:\n                break\n\n            if \"path:\" in data:\n                file_path = data.split(\"path:\")[1]\n                image_id = 0\n                print(\"file_path:\", file_path)\n                continue\n\n            pose = list(map(float, data.split(',')))\n            if len(pose) != 6:\n                print(\"Invalid pose data!\")\n                continue\n            print(f\"Received pose: {pose}\")\n\n            ue_bridge.set_camera_pose(*pose)\n            time.sleep(0.1)\n\n            current_time_str = datetime.now().strftime(\"%Y%m%d_%H%M%S_\")\n            ue_bridge.process_camera_data(file_path=file_path + '0.png')\n            image_id += 1\n            print(\"Image saved!\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"env name\")\n    parser.add_argument('--env', type=str, default='env_ue_smallcity', help=\"input env name\")\n    args = parser.parse_args()\n    config_file = \"configs/\" + args.env + \".yaml\"\n    planner_configs = load_config(config_file)\n\n    threads = []\n    for config in planner_configs:\n        thread = threading.Thread(target=handle_planner, args=(config, args.env))\n        threads.append(thread)\n        thread.start()\n\n    for thread in threads:\n        thread.join()\n\n\nif __name__ == \"__main__\":\n    main()"
  },
  {
    "path": "scripts/toolchain/pcdgen_tool.sh",
    "content": "#!/bin/bash\n\nRED='\\e[31m'\nGREEN='\\e[32m'\nYELLOW='\\e[33m'\nBLUE='\\e[34m'\nRESET='\\e[0m' \n\n\n\n# launch ros file\nif [ -z \"$1\" ]; then\n  echo -e \"${RED}Error: You must provide an environment name. ${RESET}\"\n  echo \"Usage: $0 env_name\"\n  exit 1\nfi\n\nexport ENV=$1 \n\nif [[ \"$ENV\" == *\"gs\"* ]]; then\n  echo -e \"${YELLOW} The GS scene already has a point cloud data when it is generated ${RESET}\"\n  exit 1\nfi\n\nif [[ \"$ENV\" == *\"ue\"* ]]; then\n  echo -e \"${YELLOW}The UE scene already has a point cloud data when it is generated ${RESET}\"\n  exit 1\nfi\n\n\npython3 tool_ws/src/pcd_gen/scripts/airsim_pointcloud.py --env \"$ENV\"\n\n"
  },
  {
    "path": "scripts/toolchain/seggen_tool.sh",
    "content": "#!/bin/bash\n\nRED='\\e[31m'\nGREEN='\\e[32m'\nYELLOW='\\e[33m'\nBLUE='\\e[34m'\nRESET='\\e[0m' \n\n\n# conda deactivate\n\nsource tool_ws/install/setup.bash\n\n# launch ros file\nif [ -z \"$1\" ]; then\n  echo -e \"${RED}Error: You must provide an environment name. ${RESET}\"\n  echo \"Usage: $0 env_name bev|manual\"\n  exit 1\nfi\n\nexport ENV=$1 \n\nif [[ \"$ENV\" == *\"gs\"* ]]; then\n  echo -e \"${YELLOW}Detected GS in environment name, Use manual annotation method ${RESET}\"\n  ros2 launch tool_ws/src/seg_gen/launch/manual_seg_launch.py\n  exit 1\nfi\n\nif [[ \"$ENV\" == *\"ue\"* ]]; then\n  echo -e \"${YELLOW}In the UE env, we have collected precise landmarks based on Unreal Engine 5, and it is not recommended to perform further generation. ${RESET}\"\n  exit 1\nfi\n\nif [ -z \"$2\" ]; then\n  echo -e \"${YELLOW}Use the default BEV generation method ${RESET}\"\n  ros2 launch tool_ws/src/seg_gen/launch/bev_seg_launch.py\n  sleep 0.5\n  python3 tool_ws/src/seg_gen/scripts/bev_seg_gen.py --env \"$ENV\"\nfi\n\nseg_mode=$2\n\ncase \"$seg_mode\" in\n  \"bev\")\n    ros2 launch tool_ws/src/seg_gen/launch/bev_seg_launch.py\n    sleep 0.5\n    python3 tool_ws/src/seg_gen/scripts/bev_seg_gen.py --env \"$ENV\"\n    ;;\n  \"manual\")\n    ros2 launch tool_ws/src/seg_gen/launch/manual_seg_launch.py\n    ;;\n  *)\n    echo \"Usage: $0 <env_name> <launch_file>\"\n    ;;\nesac"
  },
  {
    "path": "scripts/toolchain/trajgen_tool.sh",
    "content": "#!/bin/bash\n\nRED='\\e[31m'\nGREEN='\\e[32m'\nYELLOW='\\e[33m'\nBLUE='\\e[34m'\nRESET='\\e[0m' \n\nconda deactivate\n\n# source setup \nsource tool_ws/install/setup.sh\n# set env name\n\n\n# launch ros file\nif [ -z \"$1\" ]; then\n  echo -e \"${RED}Error: You must provide an environment name.${RESET}\"\n  echo \"Usage: $0 env_name bev|manual\"\n  exit 1\nfi\n\nexport ENV=$1\n\n# launch ros file\nros2 launch tool_ws/src/traj_gen/launch/traj_gen_launch.py"
  },
  {
    "path": "tool_ws/src/ins_gen/gpt.py",
    "content": "import base64\n\nimport random\nimport logging\nfrom mimetypes import guess_type\nfrom openai import OpenAI\nfrom process import get_png_images, read_jsonl, find_consecutive_turns\n \n        \nclass OpenAIClient:\n    def __init__(self,api_key,model):\n        \"\"\"\n        初始化 Azure OpenAI 客户端\n        \"\"\"\n        self.client = OpenAI(\n            api_key=api_key,\n        )\n        self.model = model\n        self.token_usage = 0  # 记录 Token 使用量\n        self.input_token = 0\n        self.output_token = 0\n    \n    def update_token_usage(self, response):\n        \"\"\"\n        更新 Token 使用量\n        \"\"\"\n        self.token_usage += response.usage.total_tokens\n        self.input_token += response.usage.prompt_tokens\n        self.output_token += response.usage.completion_tokens \n\n    def local_image_to_data_url(self, image_path):\n        \"\"\"\n        将本地图像转换为 Base64 编码的 Data URL\n        \"\"\"\n        mime_type, _ = guess_type(image_path)\n        if mime_type is None:\n            mime_type = 'application/octet-stream'\n\n        with open(image_path, \"rb\") as image_file:\n            base64_encoded_data = base64.b64encode(image_file.read()).decode('utf-8')\n\n        return f\"data:{mime_type};base64,{base64_encoded_data}\"\n    def get_landmark(self, img_path):\n        \"\"\"\n        使用 GPT-4 模型识别图像中的地标\n        \"\"\"\n        try:\n            # 将图片转换为 Base64 数据\n            data_url = self.local_image_to_data_url(img_path)\n\n            # 调用 Azure OpenAI 客户端\n            response = self.client.chat.completions.create(\n                model=self.model,\n                messages=[\n                    {\n                        \"role\": \"system\",\n                        \"content\": \"You are an assistant proficient in image recognition. You can accurately identify the object closest to you in the image and its different features from surrounding objects, and reply to me in JSON format.\"\n                    },\n                    {\n                        \"role\": \"user\",\n                        \"content\": [\n                            {\n                                \"type\": \"text\",\n                                \"text\": \"The target is the nearest prominent landmark to me。Answer me a dictionary like {color:, feature: , size: , type: }.\"\n                            },\n                            {\n                                \"type\": \"image_url\",\n                                \"image_url\": {\n                                    \"url\": data_url\n                                }\n                            }\n                        ]\n                    }\n                ]\n            )\n            self.update_token_usage(response)  # 更新 token 计数\n            output = response.choices[0].message.content\n            output = output.strip(\"```json\").strip(\"```\")\n            return output\n        except Exception as e:\n            logging.error(f\"Error in get_landmark: {e}\")\n            return None\n        \n    def get_aim_landmark(self, img_paths,info):\n        \"\"\"\n        使用 GPT-4 模型识别图像中的地标\n        \"\"\"\n        img_data = []\n        for path in img_paths:\n            data_url = self.local_image_to_data_url(path)\n            img_data.append(data_url)\n        if 'dir' in info['aim_landmark']:\n            dirs = f\"The target building is at {info['aim_landmark']['dir']} of the image.\"\n        else:\n            dirs = f\"The target building is at center of the image.\"\n        \n        response = self.client.chat.completions.create(\n            model=self.model,\n            messages=[\n                {\n                    \"role\": \"system\",\n                    \"content\": \"You are an assistant who is proficient in image recognition. You can accurately identify the object in the picture and its characteristics that are different from the surrounding objects.I will give you the three final images you will see. Please focus on the last image and tell me the features of the target building and reply to me in the form of JSON.\"\n                },\n                {\n                    \"role\": \"user\",\n                    \"content\": [\n                        {\n                            \"type\": \"text\",\n                            \"text\": dirs +\" Answer me a dictionary like {color: **, feature: **, size: **, type: **}.\"\n                        },\n                        {\n                            \"type\": \"image_url\",\n                            \"image_url\": {\n                                \"url\": img_data[0]\n                            }\n                        },\n                        {\n                            \"type\": \"image_url\",\n                            \"image_url\": {\n                                \"url\": img_data[1]\n                            }\n                        },\n                        {\n                            \"type\": \"image_url\",\n                            \"image_url\": {\n                                \"url\": img_data[2]\n                            }\n                        }\n                    ]\n                }\n            ]\n        )\n        self.update_token_usage(response)  # 更新 token 计数\n        output = response.choices[0].message.content\n        output = output.strip(\"```json\").strip(\"```\")\n        return output\n\n    def get_action(self, action, data):\n        \"\"\"\n        根据动作类型生成动作描述\n        \"\"\"\n        action_dic = {\n            'go straight': ['Proceed straight', 'Move ahead', 'Advance forward', 'Move forward', 'Walk straight', 'Head straight', 'Keep going straight', 'Go directly ahead'],\n            'turn left': ['Go left', 'Take a left', 'Make a left turn', 'Turn to the left', 'Shift left', 'Veer left'],\n            'turn right': ['Go right', 'Take a right', 'Make a right turn', 'Turn to the right', 'Shift right', 'Veer right'],\n            'move left': ['Shift to the left', 'Step left', 'Slide left', 'Move towards the left', 'Lean left', 'Adjust leftward'],\n            'move right': ['Shift to the right', 'Step right', 'Slide right', 'Move towards the right', 'Lean right', 'Adjust rightward'],\n            'go up': ['Move up', 'Ascend', 'Rise', 'Go upwards', 'Climb', 'Elevate'],\n            'go down': ['Move down', 'Descend', 'Fall', 'Go downwards', 'Drop', 'Lower'],\n            'up':['Move up', 'Ascend', 'Rise', 'Go upwards', 'Climb', 'Elevate'],\n            'down':['Move down', 'Descend', 'Fall', 'Go downwards', 'Drop', 'Lower']\n        }\n\n        if action in action_dic:\n            straight = random.sample(action_dic['go straight'], 1)\n            if action == 'go straight':\n                return str(straight) + 'to' + str(data) \n            else:\n                act = random.sample(action_dic[action], 1)\n                return str(act) + 'to' + str(data) \n        else:\n            return \"Invalid action key\"\n\n    def get_instruction(self, actions):\n        try:\n            response = self.client.chat.completions.create(\n                model=self.model,\n                messages=[\n                    {\n                        \"role\": \"system\",\n                        \"content\": \"You are an assistant proficient in text processing. You need to help me combine these scattered actions and landmarks into a sentence using words with similar meanings and more appropriate words, making them smooth, fluent, and accurate. If the landmarks of adjacent actions are similar or even identical, please use pronouns to refer to them.\"\n                    },\n                    {\n                        \"role\": \"user\",\n                        \"content\": f\"Data: {actions}.\"\n                    }\n                ]\n            )\n            self.update_token_usage(response)  # 更新 token 计数\n            return response.choices[0].message.content\n        except Exception as e:\n            logging.error(f\"Error in get_instruction: {e}\")\n            return 'None'\n\n    def process(self, file_path):\n        \"\"\"\n        同步处理指定文件夹路径的数据\n        \"\"\"\n        try:\n\n            actions,aim_landmark = read_jsonl(file_path + '/pose.jsonl')\n            imgs_path, img_id = get_png_images(file_path)\n            indices = find_consecutive_turns(actions)\n\n            end_landmark = self.get_aim_landmark(imgs_path[-3:],aim_landmark)\n\n            \n            return end_landmark, actions, indices, imgs_path, img_id\n        except Exception as e:\n            logging.error(f\"Error in process: {e}\")\n            logging.error(f\"file: {file_path}\")\n            return None, None, None, None, None\n    def get_token_usage(self):\n        \"\"\"\n        获取当前 Token 使用量\n        \"\"\"\n        return {'total':self.token_usage,'input':self.input_token,'output':self.output_token}"
  },
  {
    "path": "tool_ws/src/ins_gen/gpt_api_config.json",
    "content": "[\n    {\n        \"key\": \"***\",\n        \"model\": \"***\"\n    }\n]\n\n\n\n\n\n\n\n"
  },
  {
    "path": "tool_ws/src/ins_gen/gpt_generation.py",
    "content": "import os\nimport json\nimport asyncio\nimport aiofiles\nfrom tqdm.asyncio import tqdm\nfrom typing import List, Dict\nfrom process import process_act\nfrom gpt import OpenAIClient  \n\n\n\nfile_lock = asyncio.Lock()  \ndef get_subdirectories_with_paths(folder_path):\n    return [\n        (index, name, os.path.join(folder_path, name)) \n        for index, name in enumerate(os.listdir(folder_path)) \n        if os.path.isdir(os.path.join(folder_path, name))\n    ]\n\ndef get_subfolders(folder_path):\n    return [\n        os.path.join(folder_path, name) \n        for name in os.listdir(folder_path) \n        if os.path.isdir(os.path.join(folder_path, name))\n    ]\n\nasync def retry_async(func, *args, retries=3, delay=2, **kwargs):\n    for attempt in range(retries):\n        try:\n            return await asyncio.to_thread(func, *args, **kwargs)\n        except Exception as e:\n            if attempt < retries - 1:\n                await asyncio.sleep(delay)\n            else:\n                raise e\n\nasync def get_data(end_landmark, actions, indices, imgs_path, client: OpenAIClient):\n    if len(indices) == 1:\n        instructions = await retry_async(client.get_instruction, client.get_action('go straight', end_landmark))\n    else:\n        actions_list = []\n        for i, index in enumerate(indices):\n            if i == 0:\n                landmark = await retry_async(client.get_landmark, imgs_path[index+1])\n                act = actions[index]['action']['type']\n                actions_list.extend(client.get_action(act, landmark))\n            else:\n                if  int(indices[i-1]) == int(indices[i]) -1:\n                    act = actions[index]['action']['type']\n                    actions_list.extend(f'slightly {act}')\n                else:\n                    first_landmark = await retry_async(client.get_landmark, imgs_path[index+1])\n                    act = actions[index]['action']['type']\n                    actions_list.extend(client.get_action(act, first_landmark))\n        act = actions[-2]['action']['type']\n        actions_list.extend(client.get_action(act, end_landmark))\n        instructions = await retry_async(client.get_instruction, actions_list)\n    return instructions\n\nasync def gpt(folder_path, client: OpenAIClient):\n    end_landmark, actions, indices, imgs_path, img_id = await retry_async(client.process, folder_path)\n    if end_landmark is None:\n        return None\n\n    instructions = await get_data(end_landmark, actions, indices, imgs_path, client)\n\n    data_actions = process_act(actions)\n    pos = [item['action']['pos'] for item in actions]\n    yaw = [item['action']['yaw'] for item in actions]\n    return instructions, data_actions, pos, yaw, img_id\n\nasync def process_folder(folder, pool: 'OpenAIPool',base_folder):\n    try:\n        client = await pool.get_client()\n        instructions, data_actions, pos, yaw, img_id = await gpt(folder, client)\n        return ('success', {\n            'image_path': os.path.relpath(folder, base_folder),\n            'gpt_instruction': instructions,\n            'action': data_actions,\n            'index_list': img_id,\n            'pos': pos,\n            'yaw': yaw,\n        })\n    except Exception as e:\n        return ('fail', {'path': folder, 'error': str(e)})\n\nclass OpenAIPool:\n    def __init__(self, configs: List[Dict]):\n        self.clients = []\n        for conf in configs:\n            client = OpenAIClient(\n                api_key=conf['key'],\n                model=conf['model']\n            )\n            self.clients.append(client)\n        self.index = 0\n        self.lock = asyncio.Lock()\n\n    async def get_client(self) -> OpenAIClient:\n        async with self.lock:\n            client = self.clients[self.index]\n            self.index = (self.index + 1) % len(self.clients)\n            return client\n\n    def get_tokens(self):\n        token_usage = 0  \n        input_token = 0\n        output_token = 0\n        for client in self.clients:\n            tokens = client.get_token_usage()\n            token_usage += tokens['total']\n            input_token += tokens['input']\n            output_token += tokens['output']\n        return {'total':token_usage,'input':input_token,'output':output_token}\n    \n\n    def __init__(self, configs: List[Dict]):\n        self.clients = []\n        for conf in configs:\n            client = OpenAIClient(\n                api_key=conf[\"key\"],\n                model=conf[\"model\"]\n            )\n            self.clients.append(client)\n        self.index = 0\n        self.lock = asyncio.Lock()\n\n    async def get_client(self) -> OpenAIClient:\n        async with self.lock:\n            client = self.clients[self.index]\n            self.index = (self.index + 1) % len(self.clients)\n            return client\n    def get_tokens(self):\n        token_usage = 0  # 记录 Token 使用量\n        input_token = 0\n        output_token = 0\n        for client in self.clients:\n            tokens = client.get_token_usage()\n            token_usage += tokens['total']\n            input_token += tokens['input']\n            output_token += tokens['output']\n        return {'total':token_usage,'input':input_token,'output':output_token}\n        \nasync def sem_task(task, semaphore):\n    async with semaphore:\n        return await task\n\n\n\nasync def main():\n    import argparse\n\n    parser = argparse.ArgumentParser(description=\"Instruction Generation\")\n    parser.add_argument('-j', '--json', type=str, required=True, help=\"json_path\")\n    parser.add_argument('-n', '--type', type=str, required=True, help=\"data_name\")\n    args = parser.parse_args()\n\n    json_path = args.json\n\n\n    with open(\"tool_ws/src/ins_gen/gpt_api_config.json\", \"r\") as config_file:\n        api_configs = json.load(config_file)\n\n    pool = OpenAIPool(api_configs)\n\n\n\n    with open(json_path,'r')as f:\n        folders = json.load(f)\n\n    \n    traj_folder = \"Your Trajectory Folders PATH\"\n    data_path = f\"tool_ws/src/ins_gen/instructions/{args.type}.json\"\n\n\n    os.makedirs(os.path.dirname(data_path), exist_ok=True)\n\n    semaphore = asyncio.Semaphore(10)  # Limit the number of concurrent connections, usually set to twice the number of APIs\n    \n    tasks = [\n        sem_task(process_folder(folder, pool, traj_folder), semaphore)\n        for folder in folders\n    ]\n    results = []\n    count = 0  \n\n    async with aiofiles.open(data_path, 'w', encoding='utf-8') as file:\n        await file.write('[')  \n        first = True  \n\n        for future in tqdm(asyncio.as_completed(tasks), total=len(tasks), desc=f\"Processing Folders:\"):\n            status, data = await future\n            if status == 'success':\n                results.append(data)\n                count += 1\n\n            if count >= 10:\n                if not first:\n                    await file.write(',') \n                await file.write(','.join(json.dumps(result, indent=4, ensure_ascii=False) for result in results))\n                results.clear()  \n                count = 0  \n                first = False\n\n        if results:\n            if not first:\n                await file.write(',')  \n            await file.write(','.join(json.dumps(result, indent=4, ensure_ascii=False) for result in results))\n\n        await file.write(']')\n    \n    token_usage = 0\n    input_token = 0\n    output_token = 0\n    for tokens in [pool.get_tokens()]:\n        token_usage += tokens['total']\n        input_token += tokens['input']\n        output_token += tokens['output']\n        \n    print('Used token:',{'total':token_usage,'input':input_token,'output':output_token})\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())"
  },
  {
    "path": "tool_ws/src/ins_gen/process.py",
    "content": "import json\nimport os\n\n\ndef extract_number(s):\n    # 移除 `.png` 后缀部分\n    if s.endswith(\".png\"):\n        s = s.rsplit('.', 1)[0]  # 删除文件扩展名\n\n    # 查找最后一个 \"_\" 后的数字部分\n    last_underscore_pos = s.rfind(\"_\")\n    if last_underscore_pos == -1:\n        return float(\"inf\")  # 没有找到 \"_\" 时，返回一个较大的值，表示该项排最后\n\n    number_part = s[last_underscore_pos + 1:]\n\n    # 尝试将数字部分转换为整数，如果失败则返回 float('inf')\n    try:\n        return int(number_part)\n    except ValueError:\n        return float(\"inf\")\n\ndef sort_strings(strings):\n    # 使用 sorted 排序，根据数字部分排序\n    return sorted(strings, key=extract_number)\n\n\n\ndef count_png_images(folder_path):\n    count = 0\n    for root, dirs, files in os.walk(folder_path):\n        for file in files:\n            if file.lower().endswith('.png'):\n                count += 1\n    return count\n\ndef get_png_images(folder_path):\n    png_paths = []\n    img_id = []\n    for root, dirs, files in os.walk(folder_path):\n        # 对子目录和文件列表排序\n        dirs.sort()  # 按子目录名排序\n        files.sort()  # 按文件名排序\n        for file in files:\n            if file.lower().endswith('.png'):\n                # 获取文件的完整路径\n                png_paths.append(os.path.join(root, file))\n                img_id.append(file.replace('.png',''))\n            if file.lower().endswith('.jpg'):\n                # 获取文件的完整路径\n                png_paths.append(os.path.join(root, file))\n                img_id.append(file.replace('.jpg',''))\n    \n    # 如果需要整体排序，可以基于路径排序\n    png_paths = sort_strings(png_paths)\n    img_id = sort_strings(img_id)\n    return png_paths,img_id\n\n\n\ndef read_jsonl(file_path):\n    with open(file_path, 'r') as f:\n        content = f.read()\n\n    # 用来存储完整的 JSON 对象\n    json_objects = []\n\n    # 变量用于跟踪大括号的嵌套层级\n    brace_count = 0\n    start = 0\n\n    # 遍历文件内容\n    for idx, char in enumerate(content):\n        if char == '{':\n            # 增加嵌套层级\n            if brace_count == 0:\n                start = idx  # 记录开始位置\n            brace_count += 1\n        elif char == '}':\n            # 减少嵌套层级\n            brace_count -= 1\n            if brace_count == 0:\n                # 当嵌套层级归零时，意味着我们找到了一个完整的 JSON 对象\n                json_str = content[start:idx+1]\n                try:\n                    # 将字符串解析为字典\n                    json_objects.append(json.loads(json_str))\n                except json.JSONDecodeError as e:\n                    print(f\"Error parsing JSON: {e}\")\n                    continue\n    action_dicts = [d for d in json_objects if 'action' in d]\n    aim_landmark = [d for d in json_objects if 'aim_landmark' in d]\n\n    if len(aim_landmark) == 0:\n        aim_landmark = [{'aim_landmark':{\"error\":None}}] \n    return action_dicts,aim_landmark[-1]\n\n\n\n\ndef merge_adjacent(data):\n    \"\"\"\n    根据 'name' 合并相邻的相同对象，并对 'value' 求和。\n    \n    :param data: 输入数据列表，格式为 [{'name': ..., 'value': ...}, ...]\n    :return: 合并后的数据列表\n    \"\"\"\n    if not data:\n        return []\n\n    merged_data = [data[0]]  # 初始化结果列表，包含第一个元素\n\n    for item in data[1:]:\n        # 如果当前对象的 name 与前一个合并对象的 name 相同\n        if item['name'] == merged_data[-1]['name']:\n            # 合并 value\n            merged_data[-1]['value'] += item['value']\n        else:\n            # 不同的 name，作为新的对象加入结果列表\n            merged_data.append(item)\n\n    return merged_data\n\ndef find_consecutive_turns(actions):\n    consecutive_turns = []\n    \n    for i in range(1, len(actions) - 2):  # 从第二个到倒数第二个元素\n        current_action = actions[i]['action']['type']\n \n        next_action = actions[i + 1]['action']['type']\n        \n        # 如果前后动作不同且当前动作和前一个动作相同\n        if  current_action != next_action:\n            consecutive_turns.append(i)\n    \n    return consecutive_turns\n\n\ndef check(file_path):\n    # 检查并重命名 `pose.json` 为 `pose.jsonl`\n    pose_json_path = os.path.join(file_path, 'pose.json')\n    pose_jsonl_path = os.path.join(file_path, 'pose.jsonl')\n    \n    if os.path.exists(pose_json_path):\n        os.rename(pose_json_path, pose_jsonl_path)\n\n    # 检查是否存在 `pose.jsonl`\n    if os.path.exists(pose_jsonl_path):\n        json_object = read_jsonl(pose_jsonl_path)\n       \n        acts_len = len(json_object)\n        imgs_len = count_png_images(file_path)  # 获取 PNG 图片数量\n\n        # 验证动作长度和图片数量的关系\n        return acts_len == imgs_len \n    else:\n        return False\n\n\ndef process_act(actions):\n    # 定义动作名称与数字的对应关系\n    action_map = {\n        \"stop\":0,\n        \"go straight\": 1,\n        \"turn left\": 2,\n        \"turn right\": 3,\n        \"go up\": 4,\n        \"go down\": 5,\n        \"move left\": 6,\n        \"move right\": 7,\n        'up':-1,\n        'down':-2\n    }\n    \n    # 转换列表\n    transformed_list = [action_map[action['action']['type']] for action in actions]\n    \n    return transformed_list\n\n\n\n"
  },
  {
    "path": "tool_ws/src/pcd_gen/scripts/airsim_pointcloud.py",
    "content": "import airsim\nimport numpy as np  \nimport time\nfrom datetime import datetime\nimport threading\nimport yaml\nfrom pathlib import Path\nimport os\nimport sys\nsys.path.append(str(Path(__file__).parents[4]))\nprint(str(Path(__file__).parents[4]))\nfrom scripts.sim.common import *\n# from common import *\nimport json\nimport re\nimport argparse\nimport subprocess\n\ndef get_next_sequence_number(save_dir, env_name):\n    files = os.listdir(save_dir + '/' + env_name)\n    pattern = re.compile(rf\"{env_name}_(\\d{{5}})_\")\n    max_seq_num = 0\n\n    for file in files:\n        match = pattern.search(file)\n        if match:\n            seq_num = int(match.group(1))\n            if seq_num > max_seq_num:\n                max_seq_num = seq_num\n\n    return max_seq_num + 1\n\nclass AirsimBridge:  \n    def __init__(self, env_name):  \n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_airsim_sim)\n        self._sim_thread.start()\n        time.sleep(10)\n\n        self.global_point_cnt = 0\n        self._client = airsim.MultirotorClient()\n        self._client.confirmConnection()\n        self._client.enableApiControl(True)\n        self._client.armDisarm(True)\n\n    def _init_airsim_sim(self):\n        env_dir = \"envs/airsim/\" + self.env_name\n\n        if not os.path.exists(env_dir):\n            raise ValueError(f\"Specified directory {env_dir} does not exist\")\n        \n        command = [\"bash\", f\"{env_dir}/LinuxNoEditor/start.sh\"]\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        print(\"Command output:\\n\", stdout)\n\n    def __del__(self):  \n        print(\"end\")\n    \n    def _connection_check(self):  \n        if self._client.confirmConnection():  \n            print('Airsim connected successfully')  \n            self._client.enableApiControl(True)\n            self._client.armDisarm(True)\n        else:  \n            print('Airsim is not connected')  \n            exit()\n  \n    def set_drone_pos(self, x, y, z, pitch, yaw, roll):\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        qua = euler_to_quaternion(roll, pitch, yaw)\n        target_pose = airsim.Pose(airsim.Vector3r(x, -y, z),\n                                  airsim.Quaternionr(qua[0], qua[1], qua[2], qua[3]))\n        self._client.simSetVehiclePose(target_pose, True)\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n\n    def get_images(self, camera_names, update_frequency=10):\n        responses = []\n\n        for camera_name in camera_names:\n            simGetCameraInfo = self._client.simGetCameraInfo(camera_name)\n            camera_responses = self._client.simGetImages([\n                airsim.ImageRequest(f\"{camera_name}\", airsim.ImageType.Scene),\n                airsim.ImageRequest(f\"{camera_name}\", airsim.ImageType.DepthPlanar, True),\n                airsim.ImageRequest(f\"{camera_name}\", airsim.ImageType.Segmentation)\n            ])\n            responses.extend(camera_responses)\n\n        time.sleep(1 / update_frequency)\n        return responses\n\n    def process_images(self, camera_names, save_dir, env_name):\n        seq_num = get_next_sequence_number(save_dir, env_name)\n        images = self.get_images(camera_names)\n        print(\"images len\", len(images))\n        print(\"save images\")\n        \n        for i, response in enumerate(images):\n            seq_str = f\"{seq_num:05d}\"\n            image_types = {0: 'color', 1: 'depth', 5: 'object_mask'}\n            filename = f\"{save_dir}/{env_name}/{env_name}_{seq_str}_{response.camera_name}_{image_types[response.image_type]}\"\n            if response.pixels_as_float:\n                airsim.write_pfm(os.path.normpath(filename + '.pfm'), airsim.get_pfm_array(response))\n            else:\n                airsim.write_file(os.path.normpath(filename + '.png'), response.image_data_uint8)\n        \n            if i == 0:\n                pose_info = {\n                    \"id\": f\"{env_name}_{seq_str}_{response.camera_name}\",\n                    \"pos\": {\n                        \"x\": response.camera_position.x_val,\n                        \"y\": response.camera_position.y_val,\n                        \"z\": response.camera_position.z_val\n                    },\n                    \"orient\": {\n                        \"w\": response.camera_orientation.w_val,\n                        \"x\": response.camera_orientation.x_val,\n                        \"y\": response.camera_orientation.y_val,\n                        \"z\": response.camera_orientation.z_val\n                    }\n                }\n                jsonl_filename = f\"{save_dir}/{env_name}/{env_name}.jsonl\"\n                with open(jsonl_filename, 'a') as jsonl_file:\n                    jsonl_file.write(json.dumps(pose_info) + '\\n')\n\n    def get_lidar_data(self, update_frequency=400):\n        accumulated_points = []\n\n        for _ in range(int(0.005 * update_frequency)):\n            lidar_data_horizontal = self._client.getLidarData(lidar_name='LidarSensor1')\n            lidar_data_vertical = self._client.getLidarData(lidar_name='LidarSensor2')\n\n            if len(lidar_data_horizontal.point_cloud) < 3 and len(lidar_data_vertical.point_cloud) < 3:\n                print(\"\\tNo points received from Lidar data\")\n                time.sleep(1.0 / update_frequency)\n                continue\n\n            if len(lidar_data_horizontal.point_cloud) >= 3:\n                points_horizontal = np.array(lidar_data_horizontal.point_cloud, dtype=np.float32)\n                points_horizontal = points_horizontal.reshape(-1, 3)\n                self.global_point_cnt += points_horizontal.shape[0]\n                print(f\"\\tReceived {points_horizontal.shape[0]} horizontal Lidar points, global count: {self.global_point_cnt}\")\n\n                pos_horizontal = lidar_data_horizontal.pose.position\n                orientation_horizontal = lidar_data_horizontal.pose.orientation\n                rotation_matrix_horizontal = quaternion_to_rotation_matrix(orientation_horizontal.w_val, orientation_horizontal.x_val, orientation_horizontal.y_val, orientation_horizontal.z_val)\n                world_points_horizontal = np.dot(points_horizontal, rotation_matrix_horizontal.T) + np.array([pos_horizontal.x_val, pos_horizontal.y_val, pos_horizontal.z_val])\n                final_points = world_points_horizontal.copy()\n                final_points[:, 1] = -world_points_horizontal[:, 1]\n                final_points[:, 2] = -world_points_horizontal[:, 2]\n                accumulated_points.append(final_points)\n\n            if len(lidar_data_vertical.point_cloud) >= 3:\n                points_vertical = np.array(lidar_data_vertical.point_cloud, dtype=np.float32)\n                points_vertical = points_vertical.reshape(-1, 3)\n                self.global_point_cnt += points_vertical.shape[0]\n                print(f\"\\tReceived {points_vertical.shape[0]} vertical Lidar points, global count: {self.global_point_cnt}\")\n\n                pos_vertical = lidar_data_vertical.pose.position\n                orientation_vertical = lidar_data_vertical.pose.orientation\n                rotation_matrix_vertical = quaternion_to_rotation_matrix(orientation_vertical.w_val, orientation_vertical.x_val, orientation_vertical.y_val, orientation_vertical.z_val)\n                world_points_vertical = np.dot(points_vertical, rotation_matrix_vertical.T) + np.array([pos_vertical.x_val, pos_vertical.y_val, pos_vertical.z_val])\n                final_points = world_points_vertical.copy()\n                final_points[:, 1] = -world_points_vertical[:, 1]\n                final_points[:, 2] = -world_points_vertical[:, 2]\n                accumulated_points.append(final_points)\n\n            time.sleep(1.0 / update_frequency)\n\n        if accumulated_points:\n            return np.vstack(accumulated_points)\n        else:\n            return np.array([])\n\n    def save_point_cloud(self, point_cloud, file_path):\n        file_exists = os.path.isfile(file_path)\n        \n        with open(file_path, 'a' if file_exists else 'w') as f:\n            if not file_exists:\n                f.write('# .PCD v0.7 - Point Cloud Data file format\\n')\n                f.write('VERSION 0.7\\n')\n                f.write('FIELDS x y z\\n')\n                f.write('SIZE 4 4 4\\n')\n                f.write('TYPE F F F\\n')\n                f.write('COUNT 1 1 1\\n')\n                f.write(f'WIDTH {point_cloud.shape[0]}\\n')\n                f.write('HEIGHT 1\\n')\n                f.write('VIEWPOINT 0 0 0 1 0 0 0\\n')\n                f.write(f'POINTS {point_cloud.shape[0]}\\n')\n                f.write('DATA ascii\\n')\n            else:\n                with open(file_path, 'r') as original_file:\n                    lines = original_file.readlines()\n                width_line_index = next(i for i, line in enumerate(lines) if line.startswith('WIDTH'))\n                points_line_index = next(i for i, line in enumerate(lines) if line.startswith('POINTS'))\n                original_width = int(lines[width_line_index].split()[1])\n                original_points = int(lines[points_line_index].split()[1])\n                new_width = original_width + point_cloud.shape[0]\n                new_points = original_points + point_cloud.shape[0]\n                lines[width_line_index] = f'WIDTH {new_width}\\n'\n                lines[points_line_index] = f'POINTS {new_points}\\n'\n                with open(file_path, 'w') as original_file:\n                    original_file.writelines(lines)\n            \n            np.savetxt(f, point_cloud, fmt='%f %f %f')\n        print(f\"Point cloud saved to {file_path}\")\n\n    def process_lidar_data(self, file_path):\n        point_cloud = self.get_lidar_data()\n        if point_cloud.size > 0:\n            self.save_point_cloud(point_cloud, file_path)\n\ndef handle_planner(config_params, global_configs, type):\n    env_name = global_configs['datagen']['env']\n    airsim_bridge = AirsimBridge(env_name)\n    file_path = \"tool_ws/src/pcd_gen/tmp_pcd_map/\" if type == 'lidar' else 'image/'\n    \n\n    map_bound = global_configs['traj_map']['MapBound']\n    lidar_delta = global_configs['traj_map']['LidarDelta']\n\n    x_min, x_max, y_min, y_max, z_min, z_max = map_bound\n    dx, dy, dz = lidar_delta\n\n    for z in range(z_min, z_max, dz):\n        for x in range(x_min, x_max, dx):\n            for y in range(y_min, y_max, dy):\n\n                airsim_bridge.set_drone_pos(x, y, z, 0, 0, 0)\n                print(f\"Current drone position: ({x}, {y}, {z})\")\n                time.sleep(0.05)\n                \n                if type == 'image':\n                    time.sleep(0.5)\n                    camera_names = [\"bottom_custom\"]\n                    airsim_bridge.process_images(camera_names, file_path, env_name)\n\n                elif type == 'lidar':\n                    airsim_bridge.process_lidar_data(file_path + env_name + '.pcd')\n\ndef load_config(config_file=\"config.yaml\"):\n    with open(config_file, 'r') as f:\n        config = yaml.safe_load(f)\n    return config\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"env name\")\n    \n    parser.add_argument('--env', type=str, default='env_airsim_16', help=\"input env name\")\n    parser.add_argument('--type', type=str, default='lidar', help=\"data type\")\n\n    args = parser.parse_args()\n\n    config_file = \"configs/\" + args.env + \".yaml\"\n\n    global_configs = load_config(config_file)\n    planner_configs = global_configs['thread_params']\n\n    threads = []\n    for config in planner_configs:\n        thread = threading.Thread(target=handle_planner, args=(config, global_configs, args.type))\n        threads.append(thread)\n        thread.start()\n    \n    for thread in threads:\n        thread.join()\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tool_ws/src/pcd_gen/scripts/gtav_pointcloud.py",
    "content": "#!/usr/bin/env python3\r\n# -*- coding: utf-8 -*-\r\n\r\nfrom utils.Constants import IMG_WIDTH, IMG_HEIGHT\r\n\r\n\r\nfrom deepgtav.messages import Start, Stop, Scenario, Dataset, Commands, frame2numpy, GoToLocation, TeleportToLocation, SetCameraPositionAndRotation\r\nfrom deepgtav.messages import StartRecording, StopRecording, SetClockTime, SetWeather, CreatePed\r\nfrom deepgtav.client import Client\r\n\r\nfrom utils.BoundingBoxes import add_bboxes, parseBBox2d_LikePreSIL, parseBBoxesVisDroneStyle, parseBBox_YoloFormatStringToImage\r\nfrom utils.utils import save_image_and_bbox, save_meta_data, getRunCount, generateNewTargetLocation\r\n\r\nimport argparse\r\nimport time\r\nimport cv2\r\nimport matplotlib.pyplot as plt\r\nfrom PIL import Image\r\nfrom random import uniform\r\nfrom math import sqrt\r\nimport numpy as np\r\nimport os\r\nimport base64\r\nimport open3d\r\nimport matplotlib.pyplot as plt\r\nfrom mpl_toolkits.mplot3d import Axes3D\r\n\r\n\r\n\r\nif __name__ == '__main__':\r\n    parser = argparse.ArgumentParser(description=None)\r\n    parser.add_argument('-l', '--host', default='127.0.0.1', help='The IP where DeepGTAV is running')\r\n    parser.add_argument('-p', '--port', default=8000, help='The port where DeepGTAV is running')\r\n    parser.add_argument('-s', '--save_dir', default='F:\\\\GTAV\\\\output\\\\', help='The directory the generated data is saved to')\r\n    # args = parser.parse_args()\r\n\r\n    # TODO for running in VSCode\r\n    args = parser.parse_args('')\r\n    \r\n    args.save_dir = os.path.normpath(args.save_dir)\r\n\r\n    client = Client(ip=args.host, port=args.port)\r\n    \r\n    scenario = Scenario(drivingMode=0, vehicle=\"buzzard\", location=[245.23306274414062, -998.244140625, 29.205352783203125])\r\n    dataset=Dataset(location=True, time=True, exportBBox2D=True, segmentationImage=True, exportLiDAR=True, maxLidarDist=5000, exportStencilImage=True, exportLiDARRaycast=True, exportDepthBuffer=True)    \r\n    \r\n    client.sendMessage(Start(scenario=scenario, dataset=dataset))\r\n\r\n\r\n    # Adjustments for recording from UAV perspective\r\n    client.sendMessage(SetCameraPositionAndRotation(z = -20, rot_x = -90))\r\n\r\n\r\n    count = 0\r\n    bbox2d_old = \"\"\r\n    errors = []\r\n\r\n\r\n    # SETTINGS\r\n\r\n    currentTravelHeight = 40\r\n    x_start, y_start = -388, 0\r\n    x_target, y_target = 1165, -553\r\n\r\n\r\n    if not os.path.exists(os.path.join(args.save_dir, 'images')):\r\n        os.makedirs(os.path.join(args.save_dir, 'images'))\r\n    if not os.path.exists(os.path.join(args.save_dir, 'labels')):\r\n        os.makedirs(os.path.join(args.save_dir, 'labels'))\r\n    if not os.path.exists(os.path.join(args.save_dir, 'meta_data')):\r\n        os.makedirs(os.path.join(args.save_dir, 'meta_data'))\r\n\r\n    if not os.path.exists(os.path.join(args.save_dir, 'image')):\r\n        os.makedirs(os.path.join(args.save_dir, 'image'))\r\n    if not os.path.exists(os.path.join(args.save_dir, 'depth')):\r\n        os.makedirs(os.path.join(args.save_dir, 'depth'))\r\n    if not os.path.exists(os.path.join(args.save_dir, 'StencilImage')):\r\n        os.makedirs(os.path.join(args.save_dir, 'StencilImage'))\r\n    if not os.path.exists(os.path.join(args.save_dir, 'SegmentationAndBBox')):\r\n        os.makedirs(os.path.join(args.save_dir, 'SegmentationAndBBox'))\r\n    if not os.path.exists(os.path.join(args.save_dir, 'LiDAR')):\r\n        os.makedirs(os.path.join(args.save_dir, 'LiDAR'))\r\n    \r\n    \r\n        \r\n\r\n    run_count = getRunCount(args.save_dir)\r\n\r\n\r\n    messages = []\r\n    emptybbox = []\r\n\r\n    while True:\r\n        try:\r\n            count += 1\r\n            if count > 50 and count % 10 == 0:\r\n                client.sendMessage(StartRecording())\r\n            if count > 50 and count % 10 == 1:\r\n                client.sendMessage(StopRecording())\r\n                \r\n\r\n            if count == 2:\r\n                client.sendMessage(TeleportToLocation(-388, 0, 400))\r\n                client.sendMessage(GoToLocation(1165, -553, 400))\r\n\r\n            if count == 4:\r\n                client.sendMessage(SetClockTime(12))\r\n\r\n            if count == 150:\r\n                client.sendMessage(SetClockTime(0))\r\n\r\n            if count == 200:\r\n                client.sendMessage(SetClockTime(19))\r\n            \r\n\r\n            if count == 250:\r\n                currentTravelHeight = 25\r\n\r\n            if count == 300:\r\n                currentTravelHeight = 100\r\n\r\n            if count == 380:\r\n                currentTravelHeight = 40\r\n\r\n\r\n\r\n            message = client.recvMessage()  \r\n            \r\n            # None message from utf-8 decode error\r\n            if message == None:\r\n                continue\r\n            \r\n            estimated_ground_height = message[\"location\"][2] - message[\"HeightAboveGround\"]\r\n            if message[\"HeightAboveGround\"] > currentTravelHeight + 3 or message[\"HeightAboveGround\"] < currentTravelHeight - 3:\r\n                direction = np.array([x_target - message[\"location\"][0], y_target - message[\"location\"][1]])\r\n                direction = direction / np.linalg.norm(direction)\r\n                direction = direction * 50\r\n                x_temporary = message[\"location\"][0] + direction[0]\r\n                y_temporary = message[\"location\"][1] + direction[1]\r\n                client.sendMessage(GoToLocation(x_temporary, y_temporary, estimated_ground_height + currentTravelHeight))\r\n                # print(\"Correcting height\")\r\n            else:\r\n                client.sendMessage(GoToLocation(x_target, y_target, estimated_ground_height + currentTravelHeight))\r\n\r\n            # print(message[\"segmentationImage\"])\r\n            # Plot Segmentation Image and Bounding Box image overlayed for testing \r\n            if message[\"segmentationImage\"] != None and message[\"segmentationImage\"] != \"\":\r\n                bboxes = parseBBoxesVisDroneStyle(message[\"bbox2d\"])\r\n                \r\n                filename = f'{run_count:04}' + '_' + f'{count:010}'\r\n                save_image_and_bbox(args.save_dir, filename, frame2numpy(message['frame']), bboxes)\r\n                save_meta_data(args.save_dir, filename, message[\"location\"], message[\"HeightAboveGround\"], message[\"CameraPosition\"], message[\"CameraAngle\"], message[\"time\"], \"CLEAR\")\r\n                \r\n                bbox_image = add_bboxes(frame2numpy(message['frame'], (IMG_WIDTH,IMG_HEIGHT)), parseBBox_YoloFormatStringToImage(bboxes))\r\n                \r\n                nparr = np.fromstring(base64.b64decode(message[\"segmentationImage\"]), np.uint8)\r\n                segmentationImage = cv2.imdecode(nparr, cv2.IMREAD_ANYCOLOR)\r\n\r\n                dst = cv2.addWeighted(bbox_image, 0.5, segmentationImage, 0.5, 0.0)\r\n\r\n                # cv2.namedWindow(\"CombinedImage\", cv2.WINDOW_NORMAL)\r\n                # cv2.resizeWindow(\"CombinedImage\", 1280, 720)\r\n                # cv2.imshow(\"CombinedImage\", dst)\r\n                # cv2.waitKey(1)\r\n\r\n                filename = f'{run_count:04}' + '_' + f'{count:010}' + \".png\"\r\n                cv2.imwrite(os.path.join(args.save_dir, \"image\", filename), bbox_image)\r\n                cv2.imwrite(os.path.join(args.save_dir, \"SegmentationAndBBox\", filename), dst)\r\n\r\n            # print(message[\"LiDAR\"])\r\n            if message[\"LiDAR\"] != None and message[\"LiDAR\"] != \"\":\r\n                # print(message[\"LiDAR\"])\r\n                a = np.frombuffer(base64.b64decode(message[\"LiDAR\"]), np.float32)\r\n                a = a.reshape((-1, 4))\r\n                points3d = np.delete(a, 3, 1)\r\n\r\n                # 获取 Z 坐标并对其进行归一化\r\n                z_min, z_max = points3d[:, 0].min(), points3d[:, 0].max()\r\n                z_norm = (points3d[:, 0] - z_min) / (z_max - z_min)\r\n\r\n                # 创建 RGB 颜色数组，基于 Z 坐标的归一化值创建颜色梯度（例如，蓝色到红色）\r\n                colors = np.zeros((points3d.shape[0], 3))\r\n                colors[:, 0] = z_norm  # 红色分量随高度增加\r\n                colors[:, 2] = 1 - z_norm  # 蓝色分量随高度减少\r\n\r\n                point_cloud = open3d.geometry.PointCloud()\r\n                point_cloud.points = open3d.utility.Vector3dVector(points3d)\r\n                point_cloud.colors = open3d.utility.Vector3dVector(colors)\r\n                # open3d.visualization.draw_geometries([point_cloud])\r\n\r\n                open3d.io.write_point_cloud(os.path.join(args.save_dir, \"LiDAR\", filename.replace('.png', '.ply')), point_cloud)\r\n\r\n                # fig = plt.figure(figsize=(20,15))\r\n                # ax = fig.add_subplot(111, projection='3d')\r\n                # # ax.view_init(30, - 90 - 90 -8)\r\n                # ax.view_init(0, 0)\r\n                # ax.scatter(points3d[:,0], points3d[:,1], points3d[:,2], c=points3d[:,2], s=2)\r\n                # plt.savefig(os.path.join(args.save_dir, \"LiDAR\", filename))\r\n                # plt.show()\r\n            \r\n            if message[\"StencilImage\"]!=None and message[\"StencilImage\"]!=\"\":\r\n                print(\"stencilImage\")\r\n                cv2.imwrite(os.path.join(args.save_dir, \"StencilImage\", filename), bbox_image)\r\n            if message[\"DepthBuffer\"]!=None and message[\"DepthBuffer\"]!=\"\":\r\n                print(\"DepthBuffer\")\r\n                a = np.frombuffer(base64.b64decode(message[\"DepthBuffer\"]), np.float32)\r\n                a = a.reshape((1080, 1920))\r\n                \r\n                a = cv2.normalize(a, None, 0, 255, cv2.NORM_MINMAX)\r\n                a = np.uint8(a)\r\n\r\n                # print('ori', a.max(), a.min())\r\n                # a = np.delete(a, 0, 1)\r\n                # a = np.delete(a, 0, 1)\r\n                # a = np.delete(a, 0, 1)\r\n                # a = a.reshape(2160, 3840)\r\n\r\n                # nparr = np.fromstring(base64.b64decode(message[\"DepthBuffer\"]), np.uint8)\r\n                # a = cv2.imdecode(nparr, cv2.IMREAD_ANYCOLOR)\r\n\r\n                print('after', a.max(), a.min())\r\n                cv2.imwrite(os.path.join(args.save_dir, \"depth\", filename), a)\r\n            if message[\"LiDARRaycast\"]!=None and message[\"LiDARRaycast\"]!=\"\":\r\n                print(\"LiDARRaycast\")\r\n            if message[\"LiDAR\"]!=None and message[\"LiDAR\"]!=\"\":\r\n                print(\"LiDAR\")\r\n            # print(\"stencilImage\", message[\"StencilImage\"]!=None)\r\n            # print(\"DepthBuffer\", message[\"DepthBuffer\"]!=None)\r\n            # print(\"LiDARRaycast\", message[\"LiDARRaycast\"]!=None)\r\n\r\n            \r\n        except KeyboardInterrupt:\r\n            break\r\n            \r\n    # We tell DeepGTAV to stop\r\n    client.sendMessage(Stop())\r\n    # client.close()\r\n\r\n\r\n\r\n"
  },
  {
    "path": "tool_ws/src/pcd_gen/tmp_pcd_map/save_tmp_pcd_here.txt",
    "content": ""
  },
  {
    "path": "tool_ws/src/seg_gen/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(seg_gen)\n\nadd_definitions(-DROOT=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\")\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_BUILD_TYPE \"Release\")\nset(CMAKE_CXX_FLAGS_RELEASE \"-O3 -Wall -fPIC\")\n\nfind_package(ament_cmake REQUIRED)\nfind_package(Eigen3 REQUIRED)\nfind_package(pcl_ros REQUIRED)\nfind_package(yaml-cpp REQUIRED) \nfind_package(nlohmann_json REQUIRED)\nfind_package(rclcpp REQUIRED)\nfind_package(sensor_msgs REQUIRED)\nfind_package(geometry_msgs REQUIRED)\nfind_package(nav_msgs REQUIRED)\nfind_package(visualization_msgs REQUIRED)\nfind_package(cv_bridge REQUIRED)\n\ninclude_directories(\n  include\n  ${Eigen_INCLUDE_DIRS}\n  ${EIGEN3_INCLUDE_DIRS} \n  ${PCL_INCLUDE_DIRS}\n)\nlink_directories(${PCL_LIBRARY_DIRS})\n\nadd_executable(bev_seg_node src/bev_seg_node.cc)\nament_target_dependencies(bev_seg_node\n  rclcpp\n  std_msgs\n  sensor_msgs\n  geometry_msgs\n  nav_msgs\n  visualization_msgs \n  cv_bridge\n  Eigen3\n  yaml-cpp\n  pcl_ros \n  nlohmann_json \n)\ntarget_link_libraries(bev_seg_node jsoncpp glog nlohmann_json::nlohmann_json yaml-cpp)\ninstall(TARGETS bev_seg_node\n  DESTINATION lib/${PROJECT_NAME}\n)\ninstall(DIRECTORY launch/\n  DESTINATION share/${PROJECT_NAME}/launch\n)\n\n\nadd_executable(manual_seg_node src/manual_seg_node.cc)\nament_target_dependencies(manual_seg_node\n  rclcpp\n  std_msgs\n  sensor_msgs\n  geometry_msgs\n  nav_msgs\n  visualization_msgs \n  cv_bridge\n  Eigen3\n  yaml-cpp\n  pcl_ros \n  nlohmann_json \n)\ntarget_link_libraries(manual_seg_node jsoncpp glog nlohmann_json::nlohmann_json yaml-cpp)\ninstall(TARGETS manual_seg_node\n  DESTINATION lib/${PROJECT_NAME}\n)\ninstall(DIRECTORY launch/\n  DESTINATION share/${PROJECT_NAME}/launch\n)\n\nament_package()\n"
  },
  {
    "path": "tool_ws/src/seg_gen/include/loadpcdmap.hpp",
    "content": "#ifndef LOADPCDMAP\n#define LOADPCDMAP\n\n#include \"rclcpp/rclcpp.hpp\" \n#include <pcl/io/pcd_io.h>\n#include <pcl/point_cloud.h>\n#include <pcl_conversions/pcl_conversions.h>\n#include <pcl/filters/voxel_grid.h>     \n\n#include <pcl/filters/statistical_outlier_removal.h>\n#include <sensor_msgs/msg/point_cloud2.hpp>\n\nsensor_msgs::msg::PointCloud2 loadpcdfile(const std::string& pcdfilename) {\n    RCLCPP_INFO(rclcpp::get_logger(\"loadpcdfile\"), \"Loading PCD file, please wait...\");\n    \n    sensor_msgs::msg::PointCloud2 cloud_msg;\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    \n    if (pcl::io::loadPCDFile(pcdfilename, *cloud) == -1) {\n        RCLCPP_ERROR(rclcpp::get_logger(\"loadpcdfile\"), \"Failed to load PCD file: %s\", pcdfilename.c_str());\n        return cloud_msg;\n    }\n    \n    pcl::toROSMsg(*cloud, cloud_msg);\n    cloud_msg.header.frame_id = \"map\";  \n    RCLCPP_INFO(rclcpp::get_logger(\"loadpcdfile\"), \"PCD file loaded successfully.\");\n    \n    return cloud_msg;\n}\n\nsensor_msgs::msg::PointCloud2 filterPointCloud(const sensor_msgs::msg::PointCloud2& input_cloud, float leaf_size) {\n    pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n    pcl::fromROSMsg(input_cloud, *pcl_cloud);\n\n    pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;\n    voxel_filter.setInputCloud(pcl_cloud);\n    voxel_filter.setLeafSize(leaf_size, leaf_size, leaf_size);\n    voxel_filter.filter(*filtered_cloud);\n\n    sensor_msgs::msg::PointCloud2 output_cloud;\n    pcl::toROSMsg(*filtered_cloud, output_cloud);\n    output_cloud.header = input_cloud.header;  \n\n    RCLCPP_INFO(rclcpp::get_logger(\"filterPointCloud\"), \n                \"PointCloud filtered. Original points: %zu, Filtered points: %zu\", \n                pcl_cloud->points.size(), filtered_cloud->points.size());\n\n    return output_cloud;\n}\n\n\n\nsensor_msgs::msg::PointCloud2 removeOutliersWithStatisticalFilter(\n    const sensor_msgs::msg::PointCloud2& input_cloud, int mean_k, double stddev_mul_thresh) {\n\n    pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n\n    pcl::fromROSMsg(input_cloud, *pcl_cloud);\n\n    pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;\n    sor.setInputCloud(pcl_cloud);\n    sor.setMeanK(mean_k);                    \n    sor.setStddevMulThresh(stddev_mul_thresh); \n    sor.filter(*filtered_cloud);               \n\n    sensor_msgs::msg::PointCloud2 output_cloud;\n    pcl::toROSMsg(*filtered_cloud, output_cloud);\n    output_cloud.header = input_cloud.header; \n\n    return output_cloud;\n}\n\n\nsensor_msgs::msg::PointCloud2 scalePointCloud(const sensor_msgs::msg::PointCloud2& input_cloud) {\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    pcl::fromROSMsg(input_cloud, *cloud);\n\n    for (auto& point : cloud->points) {\n        point.x *= 100.0;\n        point.y *= 100.0;\n        point.z *= 100.0;\n    }\n\n    sensor_msgs::msg::PointCloud2 output_cloud;\n    pcl::toROSMsg(*cloud, output_cloud);\n    output_cloud.header = input_cloud.header;\n\n    return output_cloud;\n}\n\nsensor_msgs::msg::PointCloud2 loadPlyFile(const std::string& plyFilename) {\n    RCLCPP_INFO(rclcpp::get_logger(\"loadPlyFile\"), \"Loading PLY file, please wait...\");\n\n    sensor_msgs::msg::PointCloud2 cloud_msg;\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n    std::ifstream file(plyFilename);\n    if (!file.is_open()) {\n        RCLCPP_ERROR(rclcpp::get_logger(\"loadPlyFile\"), \"Failed to open PLY file: %s\", plyFilename.c_str());\n        return cloud_msg;\n    }\n\n    std::string line;\n    bool isHeader = true;\n    size_t vertexCount = 0;\n\n\n    while (std::getline(file, line) && isHeader) {\n        if (line.find(\"end_header\") != std::string::npos) {\n            isHeader = false;\n        } else if (line.find(\"element vertex\") != std::string::npos) {\n            std::istringstream iss(line);\n            std::string element;\n            iss >> element >> element >> vertexCount; \n        }\n    }\n\n    if (vertexCount == 0) {\n        RCLCPP_ERROR(rclcpp::get_logger(\"loadPlyFile\"), \"No vertices found in PLY file.\");\n        return cloud_msg;\n    }\n\n    cloud->resize(vertexCount);\n    for (size_t i = 0; i < vertexCount; ++i) {\n        if (!std::getline(file, line)) {\n            RCLCPP_ERROR(rclcpp::get_logger(\"loadPlyFile\"), \"Failed to read vertex data.\");\n            // RCLCPP_ERROR(clcpp::get_logger(\"loadPlyFile\"),\"Failed to read vertex data.\");\n            return cloud_msg; \n        }\n        std::istringstream iss(line);\n        pcl::PointXYZ point;\n        iss >> point.x >> point.y >> point.z;\n        cloud->points[i] = point;\n    }\n\n    file.close();\n    pcl::toROSMsg(*cloud, cloud_msg);\n    cloud_msg.header.frame_id = \"map\";  \n    RCLCPP_INFO(rclcpp::get_logger(\"loadPlyFile\"), \"PLY file loaded successfully.\");\n    \n    return cloud_msg;\n}\n\n\n#endif\n"
  },
  {
    "path": "tool_ws/src/seg_gen/launch/bev_seg_launch.py",
    "content": "from launch import LaunchDescription\nfrom launch.actions import TimerAction\nfrom launch_ros.actions import Node\nimport os\nfrom launch.actions import DeclareLaunchArgument, LogInfo\nfrom launch.substitutions import LaunchConfiguration\nfrom ament_index_python.packages import get_package_share_directory\nfrom launch.launch_service import LaunchService\nfrom pathlib import Path\n\ndef generate_launch_description():\n    # 获取包的共享目录路径\n    # planner_share_dir = get_package_share_directory('traj_gen')\n    # cur_path = Path(__file__).resolve()\n    # package_path = cur_path.parent.parent\n    # RViz 配置文件路径\n    # rviz_config = os.path.join(package_path, 'rviz', 'traj_gen.rviz')\n    \n\n    return LaunchDescription([\n        DeclareLaunchArgument('env', default_value='env_airsim_16', description='Environment configuration file'),\n        # # 启动 RViz 节点\n        # 延迟启动 Planner 节点，设置自动重启延迟为 2 秒\n        Node(\n            package='seg_gen',\n            executable='bev_seg_node',\n            name='bev_seg_node',\n            output='screen',\n            parameters=[{'env': LaunchConfiguration('env')}],\n        ),\n    ])\n\n\ndef main():\n    launch_description = generate_launch_description()\n    launch_service = LaunchService()\n    launch_service.include_launch_description(launch_description)\n    \n    print(\"Launching the ROS 2 launch file...\")\n    launch_service.run()\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tool_ws/src/seg_gen/launch/manual_seg_launch.py",
    "content": "from launch import LaunchDescription\nfrom launch.actions import TimerAction\nfrom launch_ros.actions import Node\nimport os\nfrom launch.actions import DeclareLaunchArgument, LogInfo\nfrom launch.substitutions import LaunchConfiguration\nfrom ament_index_python.packages import get_package_share_directory\nfrom launch.launch_service import LaunchService\nfrom pathlib import Path\n\ndef generate_launch_description():\n    # 获取包的共享目录路径\n    # planner_share_dir = get_package_share_directory('traj_gen')\n    # cur_path = Path(__file__).resolve()\n    # package_path = cur_path.parent.parent\n    # RViz 配置文件路径\n    # rviz_config = os.path.join(package_path, 'rviz', 'traj_gen.rviz')\n    cur_path = Path(__file__).resolve()\n    package_path = cur_path.parent.parent\n    # RViz 配置文件路径\n    rviz_config = os.path.join(package_path, 'rviz', 'seg_gen.rviz')\n\n    return LaunchDescription([\n        DeclareLaunchArgument('env', default_value='env_airsim_16', description='Environment configuration file'),\n\n\n        Node(\n            package='rviz2',\n            executable='rviz2',\n            name='rviz',\n            output='screen',\n            arguments=['-d', rviz_config]\n        ),\n\n        # # 启动 RViz 节点\n        # 延迟启动 Planner 节点，设置自动重启延迟为 2 秒\n        Node(\n            package='seg_gen',\n            executable='manual_seg_node',\n            name='manual_seg_node',\n            output='screen',\n            parameters=[{'env': LaunchConfiguration('env')}],\n        ),\n    ])\n\n\ndef main():\n    launch_description = generate_launch_description()\n    launch_service = LaunchService()\n    launch_service.include_launch_description(launch_description)\n    \n    print(\"Launching the ROS 2 launch file...\")\n    launch_service.run()\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tool_ws/src/seg_gen/package.xml",
    "content": "<?xml version=\"1.0\"?>\n<package format=\"3\">\n  <name>seg_gen</name>\n  <version>0.0.0</version>\n  <description>The ue_dage_gen package for ROS2.</description>\n\n  <!-- Maintainer information -->\n  <maintainer email=\"kermit@todo.todo\">kermit</maintainer>\n\n  <!-- License information -->\n  <license>Apache-2.0</license>\n\n  <!-- Dependencies for building and running the package -->\n  <buildtool_depend>ament_cmake</buildtool_depend>\n\n  <!-- Build dependencies -->\n  <build_depend>rclcpp</build_depend>\n  <build_depend>std_msgs</build_depend>\n  <build_depend>geometry_msgs</build_depend>\n  <build_depend>sensor_msgs</build_depend>\n  <build_depend>nav_msgs</build_depend>\n  <build_depend>visualization_msgs</build_depend>\n  <build_depend>airsim_ros_pkgs</build_depend>\n\n  <!-- Execution dependencies -->\n  <exec_depend>rclcpp</exec_depend>\n  <exec_depend>std_msgs</exec_depend>\n  <exec_depend>geometry_msgs</exec_depend>\n  <exec_depend>sensor_msgs</exec_depend>\n  <exec_depend>nav_msgs</exec_depend>\n  <exec_depend>visualization_msgs</exec_depend>\n  <exec_depend>airsim_ros_pkgs</exec_depend>\n  <exec_depend>pcl_ros</exec_depend>\n\n  <!-- Test dependencies (optional) -->\n  <test_depend>ament_lint_auto</test_depend>\n  <test_depend>ament_lint_common</test_depend>\n\n  <!-- Export build type -->\n  <export>\n    <build_type>ament_cmake</build_type>\n  </export>\n</package>\n"
  },
  {
    "path": "tool_ws/src/seg_gen/rviz/seg_gen.rviz",
    "content": "Panels:\n  - Class: rviz_common/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded:\n        - /Global Options1\n        - /Status1\n        - /Axes1\n        - /PointCloud21\n        - /PointCloud21/Topic1\n        - /Marker1\n      Splitter Ratio: 0.5\n    Tree Height: 793\n  - Class: rviz_common/Selection\n    Name: Selection\n  - Class: rviz_common/Tool Properties\n    Expanded:\n      - /2D Goal Pose1\n      - /Publish Point1\n    Name: Tool Properties\n    Splitter Ratio: 0.5886790156364441\n  - Class: rviz_common/Views\n    Expanded:\n      - /Current View1\n    Name: Views\n    Splitter Ratio: 0.5\n  - Class: rviz_common/Time\n    Experimental: false\n    Name: Time\n    SyncMode: 0\n    SyncSource: PointCloud2\nVisualization Manager:\n  Class: \"\"\n  Displays:\n    - Alpha: 0.5\n      Cell Size: 1\n      Class: rviz_default_plugins/Grid\n      Color: 160; 160; 164\n      Enabled: true\n      Line Style:\n        Line Width: 0.029999999329447746\n        Value: Lines\n      Name: Grid\n      Normal Cell Count: 0\n      Offset:\n        X: 0\n        Y: 0\n        Z: 0\n      Plane: XY\n      Plane Cell Count: 10\n      Reference Frame: <Fixed Frame>\n      Value: true\n    - Class: rviz_default_plugins/Axes\n      Enabled: true\n      Length: 1000\n      Name: Axes\n      Radius: 1\n      Reference Frame: <Fixed Frame>\n      Value: true\n    - Alpha: 1\n      Autocompute Intensity Bounds: true\n      Autocompute Value Bounds:\n        Max Value: -1.7835206985473633\n        Min Value: -20.502275466918945\n        Value: true\n      Axis: Z\n      Channel Name: intensity\n      Class: rviz_default_plugins/PointCloud2\n      Color: 255; 255; 255\n      Color Transformer: AxisColor\n      Decay Time: 0\n      Enabled: true\n      Invert Rainbow: false\n      Max Color: 255; 255; 255\n      Max Intensity: 4096\n      Min Color: 0; 0; 0\n      Min Intensity: 0\n      Name: PointCloud2\n      Position Transformer: XYZ\n      Selectable: true\n      Size (Pixels): 3\n      Size (m): 0.5\n      Style: Flat Squares\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        Filter size: 10\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /seg_gen/show/global_map\n      Use Fixed Frame: true\n      Use rainbow: true\n      Value: true\n    - Class: rviz_default_plugins/Marker\n      Enabled: true\n      Name: Marker\n      Namespaces:\n        {}\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        Filter size: 10\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /seg_gen/show/visualization_marker\n      Value: true\n    - Class: rviz_default_plugins/Marker\n      Enabled: true\n      Name: Marker\n      Namespaces:\n        {}\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        Filter size: 10\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /seg_gen/show/seg_point\n      Value: true\n  Enabled: true\n  Global Options:\n    Background Color: 48; 48; 48\n    Fixed Frame: map\n    Frame Rate: 30\n  Name: root\n  Tools:\n    - Class: rviz_default_plugins/Interact\n      Hide Inactive Objects: true\n    - Class: rviz_default_plugins/MoveCamera\n    - Class: rviz_default_plugins/Select\n    - Class: rviz_default_plugins/FocusCamera\n    - Class: rviz_default_plugins/Measure\n      Line color: 128; 128; 0\n    - Class: rviz_default_plugins/SetInitialPose\n      Covariance x: 0.25\n      Covariance y: 0.25\n      Covariance yaw: 0.06853891909122467\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /initialpose\n    - Class: rviz_default_plugins/SetGoal\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /goal_pose\n    - Class: rviz_default_plugins/PublishPoint\n      Single click: true\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /clicked_point\n  Transformation:\n    Current:\n      Class: rviz_default_plugins/TF\n  Value: true\n  Views:\n    Current:\n      Angle: 0.014999895356595516\n      Class: rviz_default_plugins/TopDownOrtho\n      Enable Stereo Rendering:\n        Stereo Eye Separation: 0.05999999865889549\n        Stereo Focal Distance: 1\n        Swap Stereo Eyes: false\n        Value: false\n      Invert Z Axis: false\n      Name: Current View\n      Near Clip Distance: 0.009999999776482582\n      Scale: 3.843996047973633\n      Target Frame: <Fixed Frame>\n      Value: TopDownOrtho (rviz_default_plugins)\n      X: 1.3501031398773193\n      Y: 3.447972297668457\n    Saved: ~\nWindow Geometry:\n  Displays:\n    collapsed: false\n  Height: 1090\n  Hide Left Dock: false\n  Hide Right Dock: true\n  QMainWindow State: 000000ff00000000fd000000040000000000000156000003a4fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000003a4000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000003a4fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d000003a4000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000063f0000003efc0100000002fb0000000800540069006d006501000000000000063f000002fb00fffffffb0000000800540069006d00650100000000000004500000000000000000000004e3000003a400000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000\n  Selection:\n    collapsed: false\n  Time:\n    collapsed: false\n  Tool Properties:\n    collapsed: false\n  Views:\n    collapsed: true\n  Width: 1599\n  X: 332\n  Y: 142\n"
  },
  {
    "path": "tool_ws/src/seg_gen/scripts/bev_seg_gen.py",
    "content": "import cv2\nimport numpy as np\nimport open3d as o3d\nimport json\nimport yaml\nimport os\nfrom pathlib import Path\nimport argparse\n\ndef find_black_regions_centers(pgm_file):\n\n    img = cv2.imread(pgm_file, cv2.IMREAD_GRAYSCALE)\n\n\n    _, binary_img = cv2.threshold(img, 1, 255, cv2.THRESH_BINARY_INV)\n\n\n    kernel = np.ones((3, 3), np.uint8)  \n    eroded_img = cv2.erode(binary_img, kernel, iterations=2)\n\n\n    contours, _ = cv2.findContours(eroded_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n\n\n    centers = []\n    for contour in contours:\n\n        M = cv2.moments(contour)\n        if M[\"m00\"] != 0:  \n\n            cx = int(M[\"m10\"] / M[\"m00\"])\n            cy = int(M[\"m01\"] / M[\"m00\"])\n            centers.append((cx, cy))\n\n    return img, centers\n\n\ndef load_map_config(yaml_file):\n    \"\"\"\n    从YAML文件加载地图配置，提取resolution和origin，并将origin转化为二维坐标。\n    \n    :param yaml_file: 配置文件路径\n    :return: (resolution, origin) 元组\n    \"\"\"\n    with open(yaml_file, 'r') as file:\n\n        config = yaml.safe_load(file)\n    \n\n    resolution = config.get('resolution', 4.0) \n    origin = config.get('origin', [0, 0, 0.0])  \n    \n    origin_2d = origin[:2]  \n    \n    return resolution, origin_2d\n\ndef visualize_point_cloud_with_real_coordinates(ply_file, real_coordinates, height=0.0):\n    \"\"\"\n    可视化点云与通过栅格地图获得的真实坐标点，并修改真实坐标点的高度(z坐标)。\n\n    :param ply_file: PLY格式的点云文件路径\n    :param real_coordinates: 真实坐标点 [(real_x, real_y), ...]\n    :param height: 修改后的z坐标值（默认值为0.0）\n    \"\"\"\n\n    # print(\"wait pcd read\")\n    pcd = o3d.io.read_point_cloud(ply_file)\n    \n\n    real_points = np.array(real_coordinates)\n    \n\n    real_points[:, 2] += 1 \n\n    # print(\"wait pcd show\")\n\n\n    real_pcd = o3d.geometry.PointCloud()\n    real_pcd.points = o3d.utility.Vector3dVector(real_points)\n    \n\n    real_pcd.paint_uniform_color([1, 0, 0])  \n\n    o3d.visualization.draw_geometries([pcd, real_pcd], window_name=\"Point Cloud with Real Coordinates\")\n\ndef convert_to_jsonl_format(real_coordinates, output_filename=\"real_coordinates.jsonl\"):\n\n\n    with open(output_filename, 'w') as outfile:\n        for (real_x, real_y, real_z) in real_coordinates:\n\n            filename = f\"X={real_x}Y={real_y}Z={real_z}.png\"\n\n            record = {\n                \"type\": \"building\",\n                \"color\": \"test\",\n                \"size\": \"test\",\n                \"shape\": \"test\",\n                \"feature\": \"test\",\n                \"filename\": filename\n            }\n            \n\n            json.dump(record, outfile)\n            outfile.write(\"\\n\") \n\n    print(f\"JSONL文件已保存为 {output_filename}\")\n\n\ndef find_highest_point_in_point_cloud(points, target_x, target_y, tolerance=5.0):\n\n    points = np.array(points)\n    \n    x_diff = np.abs(points[:, 0] - target_x)\n    y_diff = np.abs(points[:, 1] - target_y)\n    \n    mask = (x_diff <= tolerance) & (y_diff <= tolerance)\n    \n    target_points = points[mask]\n    \n    if target_points.size == 0:\n        return 0.0  \n    \n    highest_point_z = np.max(target_points[:, 2])\n    return highest_point_z\n    \n\ndef get_real_z_from_point_cloud(pcds, x, y):\n\n    \n    points = np.asarray(pcds.points)\n    \n    highest_z = find_highest_point_in_point_cloud(points, x, y)\n    return highest_z\n\ndef read_ply(file_path):\n\n    point_cloud = o3d.io.read_point_cloud(file_path)\n    \n\n    print(f\"点云包含 {len(point_cloud.points)} 个点\")\n    \n    return point_cloud\n\n\ndef convert_to_real_coordinates(img, centers, resolution, origin, ply_file):\n\n    points = read_ply(ply_file)\n\n    image_width = img.shape[0]\n    print(\"image_width\", image_width)\n    real_coordinates = []\n    for (cx, cy) in centers:\n        real_x = origin[0] + cx * resolution\n        real_y = (image_width-cy) * resolution + origin[1]\n        real_z = get_real_z_from_point_cloud(points, real_x, real_y)\n\n        real_coordinates.append((real_x, real_y, real_z))\n\n    return real_coordinates\n\n\ndef load_yaml_config(yaml_file_path):\n    with open(yaml_file_path, 'r') as file:\n        config = yaml.safe_load(file)\n    return config\n\nif __name__ == '__main__':\n\n    parser = argparse.ArgumentParser(description=\"env name\")\n    parser.add_argument('--env', type=str, default='env_airsim_16', help=\"input env name\")\n    args = parser.parse_args()\n    env_ = args.env\n\n    cur_path = Path(__file__).resolve()\n    pro_path_ = cur_path.parent.parent.parent.parent.parent\n\n    seg_data_file = str(pro_path_) + \"/tool_ws/src/seg_gen/env_seg_info/\"\n    scene_data_file = str(pro_path_) + \"/scene_data/\"\n\n    pcd_file = scene_data_file +  \"/pcd_map/\" + env_ + \".pcd\"  \n    pgm_file = seg_data_file + env_ + \"/\" + env_ + \".pgm\"\n    yaml_file = seg_data_file + env_ + \"/\" + env_ + \".yaml\"\n    jsonl_file = seg_data_file + env_ + \"/\" + env_ + \".jsonl\"\n    jsonl_file_in_data = scene_data_file + \"/seg_map/\" + env_ + \".jsonl\"\n    \n    print(\"pgm_file\", pgm_file)\n    img, centers = find_black_regions_centers(pgm_file)\n    resolution , origin = load_map_config(yaml_file)\n\n    real_coordinates = convert_to_real_coordinates(img, centers, resolution, origin, pcd_file)\n\n    convert_to_jsonl_format(real_coordinates, output_filename = jsonl_file)\n    convert_to_jsonl_format(real_coordinates, output_filename = jsonl_file_in_data)\n\n    img_color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)\n\n    for (cx, cy) in centers:\n        cv2.circle(img_color, (cx, cy), 2, (0, 0, 255), -1) \n\n\n    cv2.imwrite(seg_data_file + env_ + \"/\" + env_ + 'output_with_centers.jpg', img_color)\n\n    print(\"show img\")\n    visualize_point_cloud_with_real_coordinates(pcd_file, real_coordinates)\n    print(\"after show\")\n\n\n"
  },
  {
    "path": "tool_ws/src/seg_gen/src/bev_seg.cc",
    "content": "#include <iostream>\n#include <thread>\n#include <chrono>\n#include <map>\n#include <queue>\n#include <string>\n#include <unordered_map>\n#include <random>\n#include <cstdlib>\n#include <filesystem> \n#include <yaml-cpp/yaml.h>\n#include <rclcpp/rclcpp.hpp>\n#include <pcl/point_cloud.h>\n#include <pcl/point_types.h>\n#include <pcl/io/pcd_io.h>\n#include <opencv2/opencv.hpp>\n#include <sensor_msgs/msg/image.hpp>\n#include <std_msgs/msg/string.hpp>\n\n\nnamespace fs = std::filesystem;\n\nclass BEVMapGenerator {\npublic:\n    BEVMapGenerator(const rclcpp::Node::SharedPtr& node, const std::string& env_name)\n    : node_(node), env_(env_name) {\n\n        pr_dir_ = getProjectDir();\n        \n        yaml_path_ = pr_dir_ + \"/configs/\" + env_ + \".yaml\";\n        pcd_file_path_ = pr_dir_ + \"/scene_data/pcd_map/\" + env_ + \".pcd\";\n        seg_output_path_ = pr_dir_ + \"/tool_ws/src/seg_gen/env_seg_info/\" + env_;\n\n        std::filesystem::path output_dir(seg_output_path_);\n        if (!std::filesystem::exists(output_dir)) {\n            bool success = std::filesystem::create_directories(output_dir); \n            if (!success) {\n                std::cout<< \"\\033[31m\" << \"cannot create directory at \" << seg_output_path_ << \"\\033[30m\" <<std::endl;\n                return;\n            } \n        }\n\n        loadYaml(yaml_path_);\n\n        loadPCDAndGenerateBEV();\n    }\n\nprivate:\n    std::shared_ptr<rclcpp::Node> node_; \n    std::string pcd_file_path_;\n    double voxel_size_;\n    std::string yaml_path_;\n    std::string seg_output_path_;\n    std::string pr_dir_;\n    std::string pcd_map_path_;\n    std::string output_path_;\n    std::string seg_json_path_;\n    std::string env_;\n    double map_elevation_;\n    double height_thresh_;\n    double pcd_scale_ratio_;\n\n    void loadYaml(std::string yaml_file){\n        std::ifstream ifile(yaml_file);\n        if (!ifile) {\n            std::cerr << \"YAML file not found: \" << yaml_file << std::endl;\n            return;\n        }\n            auto yaml = YAML::LoadFile(yaml_file);\n            voxel_size_ = yaml[\"seg_map\"][\"bev_voxel_size\"].as<double>();\n            map_elevation_ = yaml[\"traj_map\"][\"map_elevation\"].as<double>();\n            height_thresh_ = yaml[\"traj_map\"][\"min_height_thresh\"].as<double>();\n            pcd_scale_ratio_ = yaml[\"traj_map\"][\"pcd_scale_ratio\"].as<double>();\n\n            std::cout << \"voxel_size_\" << voxel_size_ << std::endl;\n    }\n\n\n    std::string getProjectDir() {\n        fs::path currentPath(__FILE__);\n        fs::path pr_dir = currentPath.parent_path().parent_path().parent_path().parent_path().parent_path();\n        return pr_dir.string();\n    }\n\n\nvoid loadPCDAndGenerateBEV() {\n\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);\n    if (pcl::io::loadPCDFile<pcl::PointXYZ>(pcd_file_path_, *cloud) == -1) {\n        RCLCPP_ERROR(node_->get_logger(), \" PCD file error: %s\", pcd_file_path_.c_str());\n        return;\n    }\n    RCLCPP_INFO(node_->get_logger(), \"pcd load successful: %s\", pcd_file_path_.c_str());\n    \n    pcl::PointCloud<pcl::PointXYZ>::Ptr scaled_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    std::cout << \" pcd_scale_ratio_ \" << pcd_scale_ratio_ << std::endl;\n    for (const auto& point : *cloud) {\n        scaled_cloud->points.push_back(pcl::PointXYZ(\n            point.x * pcd_scale_ratio_,\n            point.y * pcd_scale_ratio_,\n            point.z * pcd_scale_ratio_\n        ));\n    }\n\n    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>);\n    std::cout << \"  map_elevation_ + height_thresh_ :\" <<  map_elevation_ + height_thresh_ << std::endl;\n    for (const auto& point : scaled_cloud->points) {\n        if (point.z > map_elevation_ + height_thresh_) {\n            filtered_cloud->points.push_back(point);\n        }\n    }\n\n\n    float min_x = FLT_MAX, max_x = -FLT_MAX;\n    float min_y = FLT_MAX, max_y = -FLT_MAX;\n    std::cout << \" point size :\" << filtered_cloud->points.size() << std::endl;\n    for (const auto& point : filtered_cloud->points) {\n        min_x = std::min(min_x, point.x);\n        max_x = std::max(max_x, point.x);\n        min_y = std::min(min_y, point.y);\n        max_y = std::max(max_y, point.y);\n    }\n    // std::cout << \" min_x\" << min_x <<  \"max_x\" << max_x << std::endl;\n    // std::cout << \" voxel_size_\" << voxel_size_ << std::endl;\n\n    int map_width = static_cast<int>((max_x - min_x) / voxel_size_);\n    int map_height = static_cast<int>((max_y - min_y) / voxel_size_);\n    // std::cout << \" map_width: \" << map_width <<  \"map_height\" << map_height << std::endl;\n    // std::cout <<\" error before\" << std::endl;\n\n    cv::Mat bev_map(map_height, map_width, CV_8UC1, cv::Scalar(255));\n\n    for (const auto& point : filtered_cloud->points) {\n\n        int x = static_cast<int>((point.x - min_x) / voxel_size_);\n        int y = static_cast<int>((point.y - min_y) / voxel_size_);\n\n\n        if (x >= 0 && x < map_width && y >= 0 && y < map_height) {\n            bev_map.at<uchar>(map_height - 1 - y, x) = 0; \n        }\n    }\n\n\n    std::string image_file_path = seg_output_path_ + \"/\" + env_ + \".pgm\";\n    cv::imwrite(image_file_path, bev_map);\n    RCLCPP_INFO(node_->get_logger(), \"BEV map saved %s\", image_file_path.c_str());\n\n\n    generateMapYaml(image_file_path, min_x , min_y);\n}\n\n    void generateMapYaml(const std::string& image_path, float min_x, float min_y) {\n        std::ofstream yaml_file(seg_output_path_ + \"/\" + env_ + \".yaml\");\n        \n\n        yaml_file << \"image: \" << image_path << \"\\n\";\n        yaml_file << \"resolution: \" << voxel_size_ << \"\\n\";\n        yaml_file << \"origin: [\" << min_x << \", \" << min_y << \", 0.0]\\n\";  \n        yaml_file.close();\n\n        RCLCPP_INFO(node_->get_logger(), \"map config file saved as %s/bev_map.yaml\", seg_output_path_.c_str());\n    }\n};"
  },
  {
    "path": "tool_ws/src/seg_gen/src/bev_seg_node.cc",
    "content": "#include \"bev_seg.cc\"\n\n\n\nint main(int argc, char** argv) {\n    std::string env_name_ = \"env_airsim_16\";\n\n    if(const char* env = std::getenv(\"ENV\")) {\n        env_name_ = (std::string)env;\n        std::cout << \"Your ENV is: \" << env_name_ << '\\n';\n    }\n\n    rclcpp::init(argc, argv);\n\n    auto node = rclcpp::Node::make_shared(\"seg_gen_node\");\n\n    BEVMapGenerator bev_map_generator(node, env_name_);\n\n    return 0;\n}\n"
  },
  {
    "path": "tool_ws/src/seg_gen/src/manual_seg_node.cc",
    "content": "#include <rclcpp/rclcpp.hpp>\n#include <sensor_msgs/msg/point_cloud2.hpp>\n#include <geometry_msgs/msg/pose_stamped.hpp>\n#include <std_msgs/msg/string.hpp>\n#include <visualization_msgs/msg/marker.hpp> \n#include <pcl/io/pcd_io.h>\n#include <pcl/point_cloud.h>\n#include <pcl_conversions/pcl_conversions.h>\n#include <pcl/filters/voxel_grid.h>\n#include <pcl/filters/statistical_outlier_removal.h>\n#include <nlohmann/json.hpp>\n#include <fstream>\n#include <memory>\n#include \"loadpcdmap.hpp\"\n\nusing json = nlohmann::json;\nnamespace fs = std::filesystem;\n\n\n\nstd::string getProjectDir() {\n    fs::path currentPath(__FILE__);\n    \n    fs::path rosDir = currentPath.parent_path().parent_path().parent_path().parent_path().parent_path();\n    \n    return rosDir.string();  \n}\n\n\n\nclass PointCloudVisualizer : public rclcpp::Node\n{\npublic:\n    PointCloudVisualizer(std::string env) : Node(\"point_cloud_visualizer\"), env_(env)\n    {\n        goal_pose_subscriber_ = this->create_subscription<geometry_msgs::msg::PoseStamped>(\n            \"/goal_pose\", 10, std::bind(&PointCloudVisualizer::pose_callback, this, std::placeholders::_1));\n\n        global_map_Pub_ = this->create_publisher<sensor_msgs::msg::PointCloud2>(\"/seg_gen/show/global_map\", 10);\n\n        marker_pub_ = this->create_publisher<visualization_msgs::msg::Marker>(\"/seg_gen/show/seg_point\", 10);\n        \n\n        pro_path_ = getProjectDir();\n        pcd_file_ = pro_path_ + \"/scene_data/pcd_map/\" + env_ + \".pcd\";\n        seg_file_ = pro_path_ + \"/scene_data/seg_map/\" + env_ + \".jsonl\";\n\n        sensor_msgs::msg::PointCloud2 cloud_msg = loadpcdfile(pcd_file_);\n        cloud_msg.header.frame_id = \"map\";\n        cloud_msg.header.stamp = this->get_clock()->now();\n\n        cloud_msg = filterPointCloud(cloud_msg, 2.0);\n        cloud_msg = removeOutliersWithStatisticalFilter(cloud_msg, 60, 2.0);\n\n        rclcpp::sleep_for(std::chrono::seconds(10));\n        global_map_Pub_->publish(cloud_msg);\n\n        jsonl_file_.open(seg_file_, std::ios::out | std::ios::app);\n        if (!jsonl_file_.is_open()) {\n            RCLCPP_ERROR(this->get_logger(), \"Unable to open JSONL file for writing.\");\n        }\n    }\n\n    ~PointCloudVisualizer()\n    {\n        if (jsonl_file_.is_open()) {\n            jsonl_file_.close();\n        }\n    }\n\nprivate:\n    void pose_callback(const geometry_msgs::msg::PoseStamped::SharedPtr msg)\n    {\n\n        nlohmann::ordered_json pose_data;\n        pose_data[\"type\"] = \"building\";\n        pose_data[\"color\"] = \"test\";\n        pose_data[\"size\"] = \"test\";\n        pose_data[\"shape\"] = \"test\";\n        pose_data[\"feature\"] = \"test\";\n\n\n        std::ostringstream filename_stream;\n        filename_stream << \"X=\" << std::setprecision(15) << msg->pose.position.x\n                        << \"Y=\" << std::setprecision(15) << msg->pose.position.y\n                        << \"Z=\" << std::setprecision(15) << msg->pose.position.z\n                        << \".png\";\n        pose_data[\"filename\"] = filename_stream.str();\n\n        std::ofstream output_file(seg_file_, std::ios_base::app);\n        output_file << pose_data.dump() << std::endl;\n        frame_id++;\n\n        publish_marker(msg);\n    }\n\n    void publish_marker(const geometry_msgs::msg::PoseStamped::SharedPtr msg)\n    {\n        visualization_msgs::msg::Marker marker;\n        marker.header.frame_id = \"map\";\n        marker.header.stamp = this->get_clock()->now();\n        marker.ns = \"goal_pose\";\n        marker.id = frame_id;\n        marker.type = visualization_msgs::msg::Marker::SPHERE;\n        marker.action = visualization_msgs::msg::Marker::ADD;\n        marker.pose = msg->pose;\n        marker.pose.position.z = 100;  \n        marker.scale.x = 5; \n        marker.scale.y = 5;\n        marker.scale.z = 5 ;\n        marker.color.a = 1.0;  \n        marker.color.r = 1.0; \n        marker.color.g = 0.0;\n        marker.color.b = 0.0;\n\n        marker_pub_->publish(marker);\n    }\n\n    sensor_msgs::msg::PointCloud2 loadpcdfile(const std::string& pcdfilename)\n    {\n        RCLCPP_INFO(rclcpp::get_logger(\"loadpcdfile\"), \"Loading PCD file, please wait...\");\n\n        sensor_msgs::msg::PointCloud2 cloud_msg;\n        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n        if (pcl::io::loadPCDFile(pcdfilename, *cloud) == -1) {\n            RCLCPP_ERROR(rclcpp::get_logger(\"loadpcdfile\"), \"Failed to load PCD file: %s\", pcdfilename.c_str());\n            return cloud_msg;\n        }\n\n        pcl::toROSMsg(*cloud, cloud_msg);\n        cloud_msg.header.frame_id = \"map\";\n        RCLCPP_INFO(rclcpp::get_logger(\"loadpcdfile\"), \"PCD file loaded successfully.\");\n\n        return cloud_msg;\n    }\n\n    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr global_map_Pub_;\n    rclcpp::Subscription<geometry_msgs::msg::PoseStamped>::SharedPtr goal_pose_subscriber_;\n    rclcpp::Publisher<visualization_msgs::msg::Marker>::SharedPtr marker_pub_; \n    std::ofstream jsonl_file_;\n    std::string pro_path_;\n    std::string pcd_file_;\n    std::string seg_file_; \n    std::string env_;\n    int frame_id = 0; \n};\n\nint main(int argc, char **argv)\n{\n\n\n    std::string env_name_ = \"env_airsim_16\";\n\n    if(const char* env = std::getenv(\"ENV\")) {\n        env_name_ = (std::string)env;\n        std::cout << \"Your ENV is: \" << env_name_ << '\\n';\n    }\n    rclcpp::init(argc, argv);\n\n    auto node = std::make_shared<PointCloudVisualizer>(env_name_);\n\n    rclcpp::spin(node);\n\n    rclcpp::shutdown();\n\n    return 0;\n}\n"
  },
  {
    "path": "tool_ws/src/traj_gen/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\nproject(traj_gen)\n\nadd_definitions(-DROOT=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\")\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_BUILD_TYPE \"Release\")\nset(CMAKE_CXX_FLAGS_RELEASE \"-O3 -Wall -fPIC\")\n\nfind_package(ament_cmake REQUIRED)\nfind_package(Eigen3 REQUIRED)\nfind_package(pcl_ros REQUIRED)\nfind_package(yaml-cpp REQUIRED) \nfind_package(nlohmann_json REQUIRED)\nfind_package(rclcpp REQUIRED)\nfind_package(sensor_msgs REQUIRED)\nfind_package(geometry_msgs REQUIRED)\nfind_package(nav_msgs REQUIRED)\nfind_package(visualization_msgs REQUIRED)\n\ninclude_directories(\n  include\n  ${Eigen_INCLUDE_DIRS}\n  ${EIGEN3_INCLUDE_DIRS} \n  ${PCL_INCLUDE_DIRS}\n  ${OMPL_INCLUDE_DIRS}\n)\n\nlink_directories(${PCL_LIBRARY_DIRS})\n\n\nadd_executable(traj_gen_node src/traj_gen_node.cc)\n\nament_target_dependencies(traj_gen_node\n  rclcpp\n  std_msgs\n  sensor_msgs\n  geometry_msgs\n  nav_msgs\n  visualization_msgs \n  Eigen3\n  yaml-cpp\n  pcl_ros \n  nlohmann_json \n)\n\ntarget_link_libraries(traj_gen_node jsoncpp glog nlohmann_json::nlohmann_json yaml-cpp)\n\ninstall(TARGETS traj_gen_node\n  DESTINATION lib/${PROJECT_NAME}\n)\ninstall(DIRECTORY launch/\n  DESTINATION share/${PROJECT_NAME}/launch\n)\n\nament_package()\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/common.h",
    "content": "/**\n * @file base.h\n * @author WangLiansheng (wangliansheng@pjlab.org.cn)\n * @brief \n * @version 0.1\n * @date 2023-06-14\n * \n * @copyright Copyright (c) 2023\n * \n */\n#ifndef BASE_H_H_H_\n#define BASE_H_H_H_\n\n\n#include <vector>\n#include <cmath>\n#include <Eigen/Core>\n#include <Eigen/Geometry>\n#include <ros/ros.h>\n#include \"base/eigen_types.h\"\n\n\nenum UavState {\n  DISARM = 1,\n  ARM,\n  LAND,       // 3 \n  TAKEOFF,\n  HOVER,\n  FLY\n};\n\n/* 无人的飞行状态 */\nstruct State\n{\n  Eigen::Vector3d pt;      // 全局的位置\n  Eigen::Vector3d vel;     // 全局的速度  \n  Eigen::Vector3d acc;     // body\n  Eigen::Vector3d rpy;     // Yaw是全局的，r、p body\n\n  State(){\n    pt  = Eigen::Vector3d::Zero();\n    vel = Eigen::Vector3d::Zero();\n    acc = Eigen::Vector3d::Zero();\n    rpy = Eigen::Vector3d::Zero();\n  }\n};\n\n\n#endif"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/eigen_types.h",
    "content": "\n\n#include <Eigen/Core>\n#include <Eigen/Geometry>\n\n\n\nusing Vec2i = Eigen::Vector2i;\nusing Vec3i = Eigen::Vector3i;\n\nusing Vec2d = Eigen::Vector2d;\nusing Vec2f = Eigen::Vector2f;\nusing Vec3d = Eigen::Vector3d;\nusing Vec3f = Eigen::Vector3f;\nusing Vec5d = Eigen::Matrix<double, 5, 1>;\nusing Vec5f = Eigen::Matrix<float, 5, 1>;\nusing Vec6d = Eigen::Matrix<double, 6, 1>;\nusing Vec6f = Eigen::Matrix<float, 6, 1>;\nusing Vec15d = Eigen::Matrix<double, 15, 1>;\n\nusing Mat1d = Eigen::Matrix<double, 1, 1>;\nusing Mat3d = Eigen::Matrix3d;\nusing Mat3f = Eigen::Matrix3f;\nusing Mat4d = Eigen::Matrix4d;\nusing Mat4f = Eigen::Matrix4f;\nusing Mat5d = Eigen::Matrix<double, 5, 5>;\nusing Mat5f = Eigen::Matrix<float, 5, 5>;\nusing Mat6d = Eigen::Matrix<double, 6, 6>;\nusing Mat6f = Eigen::Matrix<float, 6, 6>;\nusing Mat15d = Eigen::Matrix<double, 15, 15>;\nusing Mat6x3d = Eigen::Matrix<double, 6, 3> ;\n\n\nusing Quatd = Eigen::Quaterniond;\nusing Quatf = Eigen::Quaternionf;"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/geometry_eigen_vector.hpp",
    "content": "#pragma once\n\n#include <Eigen/Dense>\n#include <geometry_msgs/Point.h>\n#include <geometry_msgs/Pose.h>\n#include <geometry_msgs/Quaternion.h>\n#include <geometry_msgs/Vector3.h>\n#include <nav_msgs/Path.h>\n\n\n\nEigen::Quaterniond geometryToEigen(const geometry_msgs::Quaternion& vec_ros)\n{\n  Eigen::Quaterniond vec_eigen;\n  vec_eigen.x() = vec_ros.x;\n  vec_eigen.y() = vec_ros.y;\n  vec_eigen.z() = vec_ros.z;\n  vec_eigen.w() = vec_ros.w;\n  return vec_eigen;\n}\n\ngeometry_msgs::Quaternion eigenToGeometry(const Eigen::Quaterniond& vec_eigen)\n{\n  geometry_msgs::Quaternion vec_ros;\n  vec_ros.x = vec_eigen.x();\n  vec_ros.y = vec_eigen.y();\n  vec_ros.z = vec_eigen.z();\n  vec_ros.w = vec_eigen.w();\n  return vec_ros;\n}\n\n//Vectors and Points\nEigen::Vector3d geometryToEigen(const geometry_msgs::Vector3& vec_ros)\n{\n  Eigen::Vector3d vec_eigen;\n  vec_eigen.x() = vec_ros.x;\n  vec_eigen.y() = vec_ros.y;\n  vec_eigen.z() = vec_ros.z;\n  return vec_eigen;\n}\n\nEigen::Vector3d geometryToEigen(const geometry_msgs::Point& vec_ros)\n{\n  Eigen::Vector3d vec_eigen;\n  vec_eigen.x() = vec_ros.x;\n  vec_eigen.y() = vec_ros.y;\n  vec_eigen.z() = vec_ros.z;\n  return vec_eigen;\n}\n\ngeometry_msgs::Vector3 eigenToGeometry(const Eigen::Vector3d& vec_eigen)\n{\n  geometry_msgs::Vector3 vec_ros;\n  vec_ros.x = vec_eigen.x();\n  vec_ros.y = vec_eigen.y();\n  vec_ros.z = vec_eigen.z();\n  return vec_ros;\n}\n\ngeometry_msgs::Point vectorToPoint(const geometry_msgs::Vector3& vector)\n{\n  geometry_msgs::Point point;\n  point.x = vector.x;\n  point.y = vector.y;\n  point.z = vector.z;\n  return point;\n}\n\nEigen::Affine3d geometryToEigen(const geometry_msgs::Pose& pose_ros)\n{\n  Eigen::Affine3d pose;\n  Eigen::Vector3d translation(pose_ros.position.x, pose_ros.position.y,\n                              pose_ros.position.z);\n  Eigen::Quaterniond rotation(pose_ros.orientation.w, pose_ros.orientation.x,\n                              pose_ros.orientation.y, pose_ros.orientation.z);\n\n  pose = Eigen::Translation3d(translation) * rotation;\n  return pose;\n}\n\nstd::vector<Eigen::Vector3d> PathToEigen(const nav_msgs::Path& path)\n{\n  std::vector<Eigen::Vector3d> path_eigen;\n  for (const auto& pose : path.poses)\n  {\n    path_eigen.push_back(geometryToEigen(pose.pose.position));\n  }\n  return path_eigen;\n}\n\n\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/logs.h",
    "content": "/**\n * @file logs.h\n * @author WangLiansheng (wangliansheng@pjlab.org.cn)\n * @brief \n * @version 0.1\n * @date 2023-05-13\n * \n * @copyright Copyright (c) 2023\n * \n */\n#ifndef CUSTOM_LOG_H_\n#define CUSTOM_LOG_H_\n\n\n#include <ctime>\n#include <chrono>\n#include <iomanip>\n#include <iostream>\n#include <filesystem>\n#include <glog/logging.h>\n#include <gflags/gflags.h>\n\n#include <ros/package.h>\n\n#include \"base/parameter.h\"\n\nusing namespace std;\nusing namespace google;\n\n#define LOG_COLOR_RESET \"\\033[0m\"\n#define LOG_COLOR_RED \"\\033[31m\"\n#define LOG_COLOR_GREEN \"\\033[32m\"\n#define LOG_COLOR_YELLOW \"\\033[33m\"\n#define LOG_COLOR_BLUE \"\\033[34m\"\n#define LOG_COLOR_MAGENTA \"\\033[35m\"\n#define LOG_COLOR_CYAN \"\\033[36m\"\n#define LOG_COLOR_WHITE \"\\033[37m\"\n\n\n#define LOG_INFO(msg) LOG(INFO) << LOG_COLOR_GREEN << \" ---> \"<< msg << LOG_COLOR_RESET\n#define LOG_INFO_R(msg) LOG(INFO) << LOG_COLOR_RED << \" ---> \"<< msg << LOG_COLOR_RESET\n#define LOG_WARNING(msg) LOG(WARNING) << LOG_COLOR_YELLOW << \" ---> \"<< msg << LOG_COLOR_RESET\n#define LOG_ERROR(msg) LOG(ERROR) << LOG_COLOR_RED << \" ---> \"<< msg << LOG_COLOR_RESET\n\n\nvoid init_log(){\n\tgoogle::InitGoogleLogging(\"Logs\");\n  FLAGS_logbufsecs = 0;\n\tFLAGS_alsologtostderr = true;\n\tFLAGS_minloglevel = google::INFO;\n}\n\nvoid save_log(const string& dir){\n  auto now = std::chrono::system_clock::now();\n\tstd::time_t now_c = std::chrono::system_clock::to_time_t(now);\n  char time_str[100];\n  strftime(time_str, sizeof(time_str), \"%Y%m%d-%H%M%S\", localtime(&now_c));\n\tstd::string package_path, log_folder;\n\tif (dir == \"local\"){\n\t\tpackage_path = std::string(g_root_dir);\n\t\tlog_folder = package_path + \"/logs/\" + time_str + \"/\";\n\t\tif (!std::filesystem::exists(log_folder)) {\n\t\t\tstd::filesystem::create_directory(log_folder);\n\t\t}\n\t}else{\n\t\tlog_folder = dir + \"/\" + time_str + \"/\";\n\t\tif (!std::filesystem::exists(log_folder)) {\n\t\t\tstd::filesystem::create_directory(log_folder);\n\t\t}\n\t}\n  FLAGS_log_dir = log_folder;\n}\n\n#endif"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/math.hpp",
    "content": "#pragma once\n\n#include <Eigen/Dense>\n#include <geometry_msgs/Quaternion.h>\n\n\n\n\ndouble DisFromEigen2Eigen(Eigen::Vector3d first, Eigen::Vector3d second){\n    return (second - first).norm();\n}\n\n\n// 将ROS四元数转换为姿态角\nEigen::Vector3d quaternionToEulerAngles(const geometry_msgs::Quaternion& q)\n{\n    Eigen::Vector3d euler;\n    \n    // 计算滚转角（绕X轴旋转）\n    euler[0] = atan2(2 * (q.w * q.x + q.y * q.z), 1 - 2 * (q.x * q.x + q.y * q.y));\n\n    // 计算俯仰角（绕Y轴旋转）\n    euler[1] = asin(2 * (q.w * q.y - q.z * q.x));\n\n    // 计算偏航角（绕Z轴旋转）\n    euler[2] = atan2(2 * (q.w* q.z + q.x * q.y), 1 - 2 * (q.y * q.y + q.z * q.z));\n\n    return euler;\n}\n\nEigen::Vector3d quaternionToEulerAngles(const Eigen::Quaterniond& q)\n{\n    Eigen::Vector3d euler;\n\n    euler[0] = atan2(2 * (q.w() * q.x() + q.y() * q.z()), 1 - 2 * (q.x() * q.x() + q.y() * q.y()));\n\n    euler[1] = asin(2 * (q.w() * q.y() - q.z() * q.x()));\n\n    euler[2] = atan2(2 * (q.w() * q.z() + q.x() * q.y()), 1 - 2 * (q.y() * q.y() + q.z() * q.z()));\n\n    return euler;\n}\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/parameter.h",
    "content": "/**\n * @file parameter.h\n * @author WangLiansheng (wangliansheng@pjlab.org.cn)\n * @brief \n * @version 0.1\n * @date 2023-06-14\n * \n * @copyright Copyright (c) 2023\n * \n */\n\n#ifndef PARAMETER_H_WLS_\n#define PARAMETER_H_WLS_\n#include <atomic>\n#include <string>\n#include <ros/ros.h>\n#include <yaml-cpp/yaml.h>\nusing namespace std;\n\n#define MAX_VEL 2.0\n#define MAX_W   0.5\n#define DEFAULT_HEIGHT 1.0\n\nstd::string g_root_dir = std::string(ROOT);\n\nnamespace Para {\n  /* logs */\n  inline bool g_save_logs = false;\n\n  /* ROS topic */\n  inline std::string g_gpt_srv_topic = \"/uav_gpt_srv\";\n  inline std::string g_uav_odom_topic = \"/mavros/local_position/pose\";\n  inline std::string g_global_map_topic = \"/visual/global_map\";\n\n  /* plan env */\n  inline std::string g_map_name = \"pjlab.pcd\";\n\n  inline double g_max_vel = 1.5;\n  inline double g_max_w = 0.34;        \n  inline double g_max_height = 1.8;      \n\n  /* 无人机固有属性 */\n  inline double g_MaxVel = 1.5;\n  inline double g_MaxAcc = 1.0;\n  inline double g_MaxYawRate = M_PI;\n\n  void loadParams(ros::NodeHandle& nh) {\n    nh.param<bool>(\"/logs/save_logs\", g_save_logs, false);\n\n    nh.param<std::string>(\"/ros/gpt_srv_topic\", g_gpt_srv_topic, \"/gpt_srv\");\n    nh.param<std::string>(\"/ros/uav_odom_topic\", g_uav_odom_topic, \"/mavros/local_position/pose\");\n    nh.param<std::string>(\"/ros/global_map_topic\", g_global_map_topic, \"/visual/global_map\");\n\n    nh.param<std::string>(\"/plan_env/map_name\", g_map_name, \"pjlab.pcd\");\n\n    nh.param<double>(\"/motion_constraints/max_vel\", g_max_vel, 1.5);\n    nh.param<double>(\"/motion_constraints/max_w\", g_max_w, 0.34);\n    nh.param<double>(\"/motion_constraints/max_height\", g_max_height, 1.8);\n\n    nh.param<double>(\"/uav_para/MaxVel\", g_MaxVel, 1.5);\n    nh.param<double>(\"/uav_para/MaxAcc\", g_MaxAcc, 1.0);\n    nh.param<double>(\"/uav_para/MaxYawRate\", g_MaxYawRate, M_PI);\n  }\n\n  void loadParams(const std::string& file_path) {\n    YAML::Node config = YAML::LoadFile(file_path);\n    // logs\n    g_save_logs = config[\"logs\"][\"save_logs\"].as<bool>(false);\n\n    // ROS topic\n    g_gpt_srv_topic = config[\"ros\"][\"gpt_srv_topic\"].as<std::string>(\"/gpt_srv\");\n    g_uav_odom_topic = config[\"ros\"][\"uav_odom_topic\"].as<std::string>(\"/mavros/local_position/pose\");\n    g_global_map_topic = config[\"ros\"][\"global_map_topic\"].as<std::string>(\"/visual/global_map\");\n\n    // Plan env\n    g_map_name = config[\"plan_env\"][\"map_name\"].as<std::string>(\"pjlab.pcd\");\n\n    // motion constraints\n    g_max_vel = config[\"motion_constraints\"][\"max_vel\"].as<double>(1.5);\n    g_max_w = config[\"motion_constraints\"][\"max_w\"].as<double>(0.34);\n    g_max_height = config[\"motion_constraints\"][\"max_height\"].as<double>(1.8);\n\n    // UAV inherent properties\n    g_MaxVel = config[\"uav_para\"][\"MaxVel\"].as<double>(1.5);\n    g_MaxAcc = config[\"uav_para\"][\"MaxAcc\"].as<double>(1.0);\n    g_MaxYawRate = config[\"uav_para\"][\"MaxYawRate\"].as<double>(M_PI);\n  }\n}\n\n\n// run time --> RT\nnamespace RT{\n  inline bool FLAG_EXIT = false;\n};\n\n#endif"
  },
  {
    "path": "tool_ws/src/traj_gen/include/base/tcpserver.hpp",
    "content": "#include <iostream>\n#include <string>\n#include <cstring>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <thread>\n#include <sstream>\n#include <rclcpp/rclcpp.hpp>\n\nclass TCPServer : public rclcpp::Node {\nprivate:\n    std::string ip_address;\n    int send_port;\n    int listen_port;\n\npublic:\n    std::string msg_get;\n\n    TCPServer(std::string ip, int send, int listen)\n        : Node(\"tcp_server\"), ip_address(ip), send_port(send), listen_port(listen) {\n        // 创建一个线程用于监听接收数据\n        std::thread(&TCPServer::receiveData, this).detach();\n    }\n\n    std::string getmsg() {\n        return msg_get;\n    }\n\n    void sendString(const std::string& message) {\n        int sock = socket(AF_INET, SOCK_STREAM, 0);\n        if (sock == -1) {\n            RCLCPP_ERROR(this->get_logger(), \"Failed to create socket.\");\n            return;\n        }\n\n        // 设置服务器地址\n        sockaddr_in serverAddress{};\n        serverAddress.sin_family = AF_INET;\n        serverAddress.sin_addr.s_addr = inet_addr(ip_address.c_str());\n        serverAddress.sin_port = htons(send_port);\n\n        // 尝试连接到服务器\n        while (connect(sock, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(serverAddress)) < 0 && rclcpp::ok()) {\n            RCLCPP_WARN(this->get_logger(), \"Failed to connect to server, retrying...\");\n            std::this_thread::sleep_for(std::chrono::milliseconds(100));\n        }\n\n        // 发送数据\n        if (send(sock, message.c_str(), message.size(), 0) < 0) {\n            RCLCPP_ERROR(this->get_logger(), \"Failed to send data.\");\n        } else {\n            // RCLCPP_INFO(this->get_logger(), \"Data sent: %s\", message.c_str());\n        }\n\n        close(sock);\n    }\n\n    void sendCameraPose(float x, float y, float z, float pitch, float yaw, float roll) {\n        std::stringstream pose_stream;\n        pose_stream << x << \",\" << y << \",\" << z << \",\" << pitch << \",\" << yaw << \",\" << roll;\n        std::string pose_str = pose_stream.str();\n\n        sendString(pose_str);\n        // RCLCPP_INFO(this->get_logger(), \"Sent camera pose: %s\", pose_str.c_str());\n    }\n\n    void receiveData() {\n        int serverSocket = socket(AF_INET, SOCK_STREAM, 0);\n        if (serverSocket == -1) {\n            RCLCPP_ERROR(this->get_logger(), \"Failed to create socket.\");\n            return;\n        }\n\n        sockaddr_in serverAddress{};\n        serverAddress.sin_family = AF_INET;\n        serverAddress.sin_addr.s_addr = INADDR_ANY;\n        serverAddress.sin_port = htons(listen_port);\n\n        if (bind(serverSocket, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(serverAddress)) < 0) {\n            RCLCPP_ERROR(this->get_logger(), \"Failed to bind socket.\");\n            close(serverSocket);\n            return;\n        }\n\n        listen(serverSocket, 1);\n\n        while (rclcpp::ok()) {\n            sockaddr_in clientAddress{};\n            socklen_t clientAddressLength = sizeof(clientAddress);\n            int clientSocket = accept(serverSocket, reinterpret_cast<struct sockaddr*>(&clientAddress), &clientAddressLength);\n            if (clientSocket < 0) {\n                RCLCPP_ERROR(this->get_logger(), \"Failed to accept connection.\");\n                continue;\n            }\n\n            char buffer[1024];\n            memset(buffer, 0, sizeof(buffer));\n            ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);\n            if (bytesRead < 0) {\n                RCLCPP_ERROR(this->get_logger(), \"Failed to receive data.\");\n            } else {\n                msg_get = buffer;\n                RCLCPP_INFO(this->get_logger(), \"Received data: %s\", msg_get.c_str());\n            }\n\n            close(clientSocket);\n            std::this_thread::sleep_for(std::chrono::seconds(1));\n        }\n\n        close(serverSocket);\n    }\n};\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/common.h",
    "content": "#ifndef OBJECT_HPP  \n#define OBJECT_HPP \n\n#include <string>       \n#include <Eigen/Dense>  \n#include <nlohmann/json.hpp>\n\n\nvoid normalYaw_degree(double& yaw){\n    if(yaw > 180) yaw -= 360;\n    if(yaw < -180) yaw += 360;\n}\n    double calculateYaw(const Eigen::Vector3d& vector) {\n        double ans_yaw = std::atan2(vector.y(), vector.x());\n        if (ans_yaw > M_PI){\n            ans_yaw -= 2* M_PI;\n        }\n        if (ans_yaw <= -M_PI){\n            ans_yaw += 2* M_PI;\n        }\n        return ans_yaw;\n    }\n\n\n    double calculateYawDegree(const Eigen::Vector3d& vector) {\n        double ans_yaw = std::atan2(vector.y(), vector.x());\n        if (ans_yaw > M_PI){\n            ans_yaw -= 2* M_PI;\n        }\n        if (ans_yaw <= -M_PI){\n            ans_yaw += 2* M_PI;\n        }\n        return ans_yaw * 180 / M_PI;\n    }\n\nstruct Object {\n    std::string type;\n    std::string color;\n    std::string size;\n    std::string shape;\n    std::string feature;\n    Eigen::Vector3d pos;\n    std::string relateinfov = \"NONE\";\n    std::vector<Eigen::Vector3d> valid_points;\n\n    Object() : type(\"\"), color(\"\"), size(\"\"), shape(\"\"), feature(\"\"), pos(0.0, 0.0, 0.0) {}\n\n    Object(const std::string& t, const std::string& c, const std::string& s,\n            const std::string& sh, const std::string& f, const Eigen::Vector3d& p)\n        : type(t), color(c), size(s), shape(sh), feature(f), pos(p) {}\n    \n    nlohmann::json toJson() const {\n        nlohmann::json j;\n        j[\"type\"] = type;\n        j[\"color\"] = color;\n        j[\"size\"] = size;\n        j[\"shape\"] = shape;\n        j[\"feature\"] = feature;\n        j[\"position\"] = { pos.x(), pos.y(), pos.z() }; \n        if(relateinfov != \"NONE\"){ j[\"relateinfov\"] = relateinfov;}\n        return j;\n    }\n\n    void addRelateinFov(const Eigen::Vector2d& observer_pos, double fov_in, double in_yaw) {\n\n        // std::cout << \"in_ yaw\" << in_yaw << std::endl;\n        double yaw = in_yaw * 180.0 / M_PI;\n        Eigen::Vector2d direction = Eigen::Vector2d(pos.x(), pos.y()) - Eigen::Vector2d(observer_pos.x(), observer_pos.y());\n        double angle_to_object = std::atan2(direction.y(), direction.x()) * 180.0 / M_PI;  \n\n\n        double yaw_left = yaw + fov_in / 6.0;  \n        double yaw_right = yaw - fov_in / 6.0; \n        \n        normalYaw_degree(yaw_left);\n        normalYaw_degree(yaw_right);\n\n\n\n        if (angle_to_object <= yaw_left && angle_to_object >= yaw_right) {\n            relateinfov = \"CENTER\";\n        } else if (angle_to_object > yaw_left) {\n            relateinfov = \"LEFT\";   \n        } else {\n            relateinfov = \"RIGHT\";   \n        }\n    }\n\n\n\n    bool operator==(const Object& other) const {\n        return type == other.type && color == other.color && size == other.size && \n               shape == other.shape && feature == other.feature && pos == other.pos;\n    }\n\n\n};\n\n\nstruct TrajRecodData {\n\n    int que_size;\n\n    std::vector<Eigen::Vector4d> record_list;\n    \n    std::vector<std::pair<std::pair<std::string, int>, Eigen::Vector3d>> action_list;\n    \n    std::vector<std::vector<std::pair<Eigen::Vector2d, std::vector<Object>>>> seginfov_list;\n\n    double start_yaw;\n\n    double end_yaw;\n\n    double traj_yaw;\n    \n    Object aim_obj;\n    TrajRecodData(\n        const std::vector<Eigen::Vector4d>& records,\n        const std::vector<std::pair<std::pair<std::string, int>, Eigen::Vector3d>>& actions,\n        Object obj\n    ) : record_list(records), action_list(actions) ,aim_obj(obj){\n        que_size = record_list.size();\n    }\n    TrajRecodData() = default;\n    \n};\n\n#endif // OBJECT_HPP\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/map/loadpcdmap.hpp",
    "content": "#ifndef LOADPCDMAP\n#define LOADPCDMAP\n\n#include \"rclcpp/rclcpp.hpp\" \n#include <pcl/io/pcd_io.h>\n#include <pcl/point_cloud.h>\n#include <pcl_conversions/pcl_conversions.h>\n#include <pcl/filters/voxel_grid.h>     \n\n#include <pcl/filters/statistical_outlier_removal.h>\n#include <sensor_msgs/msg/point_cloud2.hpp>\n\nsensor_msgs::msg::PointCloud2 loadpcdfile(const std::string& pcdfilename) {\n    // RCLCPP_INFO(rclcpp::get_logger(\"loadpcdfile\"), \"Loading PCD file, please wait...\");\n    \n    sensor_msgs::msg::PointCloud2 cloud_msg;\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    \n    if (pcl::io::loadPCDFile(pcdfilename, *cloud) == -1) {\n        RCLCPP_ERROR(rclcpp::get_logger(\"loadpcdfile\"), \"Failed to load PCD file: %s\", pcdfilename.c_str());\n        return cloud_msg;\n    }\n    \n    pcl::toROSMsg(*cloud, cloud_msg);\n    cloud_msg.header.frame_id = \"map\";  \n    // RCLCPP_INFO(rclcpp::get_logger(\"loadpcdfile\"), \"PCD file loaded successfully.\");\n    \n    return cloud_msg;\n}\n\nsensor_msgs::msg::PointCloud2 filterPointCloud(const sensor_msgs::msg::PointCloud2& input_cloud, float leaf_size) {\n    pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n    pcl::fromROSMsg(input_cloud, *pcl_cloud);\n\n    pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;\n    voxel_filter.setInputCloud(pcl_cloud);\n    voxel_filter.setLeafSize(leaf_size, leaf_size, leaf_size);\n    voxel_filter.filter(*filtered_cloud);\n\n    sensor_msgs::msg::PointCloud2 output_cloud;\n    pcl::toROSMsg(*filtered_cloud, output_cloud);\n    output_cloud.header = input_cloud.header; \n\n    return output_cloud;\n}\n\n\n\nsensor_msgs::msg::PointCloud2 removeOutliersWithStatisticalFilter(\n    const sensor_msgs::msg::PointCloud2& input_cloud, int mean_k, double stddev_mul_thresh) {\n\n    pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n    pcl::fromROSMsg(input_cloud, *pcl_cloud);\n\n    pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;\n    sor.setInputCloud(pcl_cloud);\n    sor.setMeanK(mean_k);   \n    sor.setStddevMulThresh(stddev_mul_thresh); \n    sor.filter(*filtered_cloud); \n\n    sensor_msgs::msg::PointCloud2 output_cloud;\n    pcl::toROSMsg(*filtered_cloud, output_cloud);\n    output_cloud.header = input_cloud.header;\n\n    return output_cloud;\n}\n\n\nsensor_msgs::msg::PointCloud2 scalePointCloud(const sensor_msgs::msg::PointCloud2& input_cloud, double scale) {\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n    pcl::fromROSMsg(input_cloud, *cloud);\n\n    for (auto& point : cloud->points) {\n        point.x *= scale;\n        point.y *= scale;\n        point.z *= scale;\n    }\n\n    sensor_msgs::msg::PointCloud2 output_cloud;\n    pcl::toROSMsg(*cloud, output_cloud);\n    output_cloud.header = input_cloud.header;\n\n    return output_cloud;\n}\n\nsensor_msgs::msg::PointCloud2 loadPlyFile(const std::string& plyFilename) {\n    RCLCPP_INFO(rclcpp::get_logger(\"loadPlyFile\"), \"Loading PLY file, please wait...\");\n\n    sensor_msgs::msg::PointCloud2 cloud_msg;\n    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());\n\n    std::ifstream file(plyFilename);\n    if (!file.is_open()) {\n        RCLCPP_ERROR(rclcpp::get_logger(\"loadPlyFile\"), \"Failed to open PLY file: %s\", plyFilename.c_str());\n        return cloud_msg;\n    }\n\n    std::string line;\n    bool isHeader = true;\n    size_t vertexCount = 0;\n\n    while (std::getline(file, line) && isHeader) {\n        if (line.find(\"end_header\") != std::string::npos) {\n            isHeader = false;\n        } else if (line.find(\"element vertex\") != std::string::npos) {\n            std::istringstream iss(line);\n            std::string element;\n            iss >> element >> element >> vertexCount;\n        }\n    }\n\n    if (vertexCount == 0) {\n        RCLCPP_ERROR(rclcpp::get_logger(\"loadPlyFile\"), \"No vertices found in PLY file.\");\n        return cloud_msg;\n    }\n\n    cloud->resize(vertexCount);\n    for (size_t i = 0; i < vertexCount; ++i) {\n        if (!std::getline(file, line)) {\n            RCLCPP_ERROR(rclcpp::get_logger(\"loadPlyFile\"), \"Failed to read vertex data.\");\n            return cloud_msg; \n        }\n        std::istringstream iss(line);\n        pcl::PointXYZ point;\n        iss >> point.x >> point.y >> point.z; \n        cloud->points[i] = point;\n    }\n\n    file.close();\n    pcl::toROSMsg(*cloud, cloud_msg);\n    cloud_msg.header.frame_id = \"map\";  \n    RCLCPP_INFO(rclcpp::get_logger(\"loadPlyFile\"), \"PLY file loaded successfully.\");\n    \n    return cloud_msg;\n}\n\n\n#endif\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/map/seggridmap.hpp",
    "content": "#include <iostream>\n#include <vector>\n#include <unordered_map>\n#include <Eigen/Dense>\n#include \"common.h\"\n\nclass SegGridMap {\n\nprivate:\n    int map_width_;                \n    int map_height_;               \n    double grid_size_;             \n    Eigen::Vector2d origin_;       \n    std::vector<std::vector<std::vector<Object>>> grid_map_; \n\npublic:\n    SegGridMap() = default;\n    SegGridMap(int width, int height, double grid_size, const Eigen::Vector2d& origin = Eigen::Vector2d(0.0, 0.0))\n        : map_width_(width), map_height_(height), grid_size_(grid_size), origin_(origin) {\n        grid_map_.resize(map_width_, std::vector<std::vector<Object>>(map_height_));\n    }\n\n    Eigen::Vector2i worldToGrid(const Eigen::Vector2d& world_pos) const {\n        Eigen::Vector2d local_pos = world_pos - origin_; \n        int x = static_cast<int>(local_pos.x() / grid_size_);\n        int y = static_cast<int>(local_pos.y() / grid_size_);\n        return Eigen::Vector2i(x, y);\n    }\n\n    Eigen::Vector2d gridToWorld(const Eigen::Vector2i& grid_pos) const {\n        double x = grid_pos.x() * grid_size_ + origin_.x();\n        double y = grid_pos.y() * grid_size_ + origin_.y();\n        return Eigen::Vector2d(x, y);\n    }\n\n    void addObjectToGrid(const Eigen::Vector2i& grid_pos, const Object& obj) {\n        if (grid_pos.x() >= 0 && grid_pos.x() < map_width_ &&\n            grid_pos.y() >= 0 && grid_pos.y() < map_height_) {\n            grid_map_[grid_pos.x()][grid_pos.y()].push_back(obj);\n        } else {\n            std::cerr << \"Grid position out of bounds!\" << std::endl;\n        }\n    }\n\n    void addObjectToGrid(const Eigen::Vector2d& world_pos, const Object& obj) {\n        Eigen::Vector2i grid_pos = worldToGrid(world_pos);\n        if (grid_pos.x() >= 0 && grid_pos.x() < map_width_ &&\n            grid_pos.y() >= 0 && grid_pos.y() < map_height_) {\n            grid_map_[grid_pos.x()][grid_pos.y()].push_back(obj);\n        } else {\n            std::cerr << \"Grid position out of bounds!\" << std::endl;\n        }\n    }\n\n    void printGridInfo(const Eigen::Vector2i& grid_pos) const {\n        if (grid_pos.x() >= 0 && grid_pos.x() < map_width_ &&\n            grid_pos.y() >= 0 && grid_pos.y() < map_height_) {\n            const auto& objects = grid_map_[grid_pos.x()][grid_pos.y()];\n            std::cout << \"Grid (\" << grid_pos.x() << \", \" << grid_pos.y() << \") contains \"\n                      << objects.size() << \" object(s):\" << std::endl;\n            for (const auto& obj : objects) {\n                std::cout << \"Type: \" << obj.type << \", Color: \" << obj.color\n                          << \", Size: \" << obj.size << \", Shape: \" << obj.shape\n                          << \", Feature: \" << obj.feature << \", Position: (\"\n                          << obj.pos.x() << \", \" << obj.pos.y() << \", \" << obj.pos.z()\n                          << \")\" << std::endl;\n            }\n        } else {\n            std::cerr << \"Grid position out of bounds!\" << std::endl;\n        }\n    }\n\n    std::vector<Object> queryObjectsByWorldCoord(const Eigen::Vector2d& world_pos) const {\n        Eigen::Vector2i grid_pos = worldToGrid(world_pos);\n        return queryObjectsByGridCoord(grid_pos);\n    }\n\n    std::vector<Object> queryObjectsByGridCoord(const Eigen::Vector2i& grid_pos) const {\n        std::vector<Object> objects;\n        if (grid_pos.x() >= 0 && grid_pos.x() < map_width_ && grid_pos.y() >= 0 && grid_pos.y() < map_height_) {\n            objects = grid_map_[grid_pos.x()][grid_pos.y()];\n        }\n        return objects;\n    }\n\n\n    std::vector<std::pair<Eigen::Vector2d, std::vector<Object>>> getVisibleObjects(\n        const Eigen::Vector2d& position, double yaw_rad, double fov, double max_distance, int obj_num) {\n\n        double half_fov = fov / 2.0;\n        double left_angle = yaw_rad - half_fov * M_PI / 180.0;\n        double right_angle = yaw_rad + half_fov * M_PI / 180.0;\n\n        Eigen::Vector2i observer_grid = worldToGrid(position);\n        std::vector<std::pair<Eigen::Vector2d, std::vector<Object>>> visible_objects;\n\n        int search_radius = std::ceil(max_distance / grid_size_); \n\n        for (int dx = -search_radius; dx <= search_radius; ++dx) {\n            for (int dy = -search_radius; dy <= search_radius; ++dy) {\n                int target_x = observer_grid.x() + dx;\n                int target_y = observer_grid.y() + dy;\n\n                if (target_x < 0 || target_x >= map_width_ || target_y < 0 || target_y >= map_height_) {\n                    continue;\n                }\n                Eigen::Vector2d grid_pos = gridToWorld(Eigen::Vector2i(target_x, target_y));\n                double distance = (grid_pos - position).norm(); \n\n                if (distance > max_distance) continue;\n\n                Eigen::Vector2d direction = grid_pos - position;\n                double angle = std::atan2(direction.y(), direction.x());\n\n                if (angle >= left_angle && angle <= right_angle) {\n                    std::vector<Object> objects_in_grid = grid_map_[target_x][target_y];\n                    if (!objects_in_grid.empty()) {\n                        for(int i = 0; i < objects_in_grid.size(); i++){\n                            objects_in_grid[i].addRelateinFov(position, fov, yaw_rad);\n                        }\n                        visible_objects.push_back(std::make_pair(gridToWorld(Eigen::Vector2i(target_x, target_y)), objects_in_grid));\n                    }\n                }\n            }\n        }\n        std::sort(visible_objects.begin(), visible_objects.end(), \n            [&position](const std::pair<Eigen::Vector2d, std::vector<Object>>& a, \n                    const std::pair<Eigen::Vector2d, std::vector<Object>>& b) {\n                double dist_a = (a.first - position).norm();\n                double dist_b = (b.first - position).norm();\n                return dist_a < dist_b;\n            });\n        if (visible_objects.size() > obj_num) {\n            visible_objects.resize(obj_num);\n        }\n        return visible_objects;\n    }\n\n    std::vector<std::vector<std::pair<Eigen::Vector2d, std::vector<Object>>>> getSeginFovbyRecod(\n        std::vector<Eigen::Vector4d> record_list, double fov_in, double max_depth){\n            std::vector<std::vector<std::pair<Eigen::Vector2d, std::vector<Object>>>>  ans_segfov_list;\n            std::vector<std::pair<Eigen::Vector2d, std::vector<Object>>> tmp_objects;\n            for(auto recod_p : record_list){\n                Eigen::Vector2d tmp_pos(recod_p[0], recod_p[1]);\n                double yaw(recod_p[3]);\n                tmp_objects = getVisibleObjects(tmp_pos, yaw, fov_in, max_depth, 4);\n                ans_segfov_list.push_back(tmp_objects);\n            }\n        return ans_segfov_list;\n    }\n    \n};\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/map/voxel_dilater.hpp",
    "content": "/*\n    MIT License\n\n    Copyright (c) 2021 Zhepei Wang (wangzhepei@live.com)\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE.\n*/\n\n#ifndef VOXEL_DILATER\n#define VOXEL_DILATER(i, j, k, x, y, z, sy, sz, bx, by, bz, ck, ogm, ofst, val, fdl)                                                                                                                                                      \\\n(ck) = (x) == 0 || (x) == (bx) || (y) == 0 || (y) == (by) || (z) == 0 || (z) == (bz);                                                                                                                                                   \\\n(i) = (x) - 1; (j) = (y) - (sy); (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0    && (j) >= 0      && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y) - (sy); (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0    && (j) >= 0                    )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y) - (sy); (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0    && (j) >= 0      && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y);        (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0                     && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y);        (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0                                   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y);        (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0                     && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y) + (sy); (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0    && (j) <= (by)   && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y) + (sy); (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0    && (j) <= (by)                 )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) - 1; (j) = (y) + (sy); (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) >= 0    && (j) <= (by)   && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y) - (sy); (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                && (j) >= 0      && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y) - (sy); (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                && (j) >= 0                    )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y) - (sy); (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                && (j) >= 0      && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y);        (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                                 && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y);        (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                                 && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y) + (sy); (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                && (j) <= (by)   && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y) + (sy); (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                && (j) <= (by)                 )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x);     (j) = (y) + (sy); (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck)                && (j) <= (by)   && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y) - (sy); (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx) && (j) >= 0      && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y) - (sy); (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx) && (j) >= 0                    )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y) - (sy); (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx) && (j) >= 0      && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y);        (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx)                  && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y);        (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx)                                )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y);        (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx)                  && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y) + (sy); (k) = (z) - (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx) && (j) <= (by)   && (k) >= 0   )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y) + (sy); (k) = (z);        (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx) && (j) <= (by)                 )) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }  \\\n(i) = (x) + 1; (j) = (y) + (sy); (k) = (z) + (sz); (ofst) = (i) + (j) + (k); if ((!(ck) || ((ck) && (i) <= (bx) && (j) <= (by)   && (k) <= (bz))) && (ogm)[(ofst)] == 0) { (ogm)[(ofst)] = (val); (fdl).emplace_back((i), (j), (k)); }\n#endif\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/map/voxel_map.hpp",
    "content": "/*\n    MIT License\n\n    Copyright (c) 2021 Zhepei Wang (wangzhepei@live.com)\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE.\n*/\n\n#ifndef VOXEL_MAP_HPP\n#define VOXEL_MAP_HPP\n\n#include \"voxel_dilater.hpp\"\n#include <memory>\n#include <vector>\n#include <Eigen/Eigen>\n\nnamespace voxel_map\n{\n    constexpr uint8_t Unoccupied = 0;\n    constexpr uint8_t Occupied = 1;\n    constexpr uint8_t Dilated = 2;\n\n    class VoxelMap\n    {\n    public:\n        VoxelMap() = default;\n        VoxelMap(const Eigen::Vector3i &size,\n                 const Eigen::Vector3d &origin,\n                 const double &voxScale)\n            : mapSize(size),\n              o(origin),\n              scale(voxScale),\n              voxNum(mapSize.prod()),\n              step(1, mapSize(0), mapSize(1) * mapSize(0)),\n              oc(o + Eigen::Vector3d::Constant(0.5 * scale)),\n              bounds((mapSize.array() - 1) * step.array()),\n              stepScale(step.cast<double>().cwiseInverse() * scale),\n              voxels(voxNum, Unoccupied) {}\n\n    private:\n        Eigen::Vector3i mapSize;\n        Eigen::Vector3d o;\n        double scale;\n        int voxNum;\n        Eigen::Vector3i step;\n        Eigen::Vector3d oc;\n        Eigen::Vector3i bounds;\n        Eigen::Vector3d stepScale;\n        std::vector<uint8_t> voxels;\n        std::vector<Eigen::Vector3i> surf;\n\n    public:\n        inline Eigen::Vector3i getSize(void) const\n        {\n            return mapSize;\n        }\n\n        inline double getScale(void) const\n        {\n            return scale;\n        }\n\n        inline Eigen::Vector3d getOrigin(void) const\n        {\n            return o;\n        }\n\n        inline Eigen::Vector3d getCorner(void) const\n        {\n            return mapSize.cast<double>() * scale + o;\n        }\n\n        inline const std::vector<uint8_t> &getVoxels(void) const\n        {\n            return voxels;\n        }\n\n        inline void setOccupied(const Eigen::Vector3i &id)\n        {\n            if (id(0) >= 0 && id(1) >= 0 && id(2) >= 0 &&\n                id(0) < mapSize(0) && id(1) < mapSize(1) && id(2) < mapSize(2))\n            {\n                voxels[id.dot(step)] = Occupied;\n            }\n        }\n\n\n        inline void setOccupied(const Eigen::Vector3d &pos)\n        {\n            const Eigen::Vector3i id = ((pos - o) / scale).cast<int>();\n            if (id(0) >= 0 && id(1) >= 0 && id(2) >= 0 &&\n                id(0) < mapSize(0) && id(1) < mapSize(1) && id(2) < mapSize(2))\n            {\n                voxels[id.dot(step)] = Occupied;\n            }\n        }\n\n\n        inline void setFree(const Eigen::Vector3d &pos, double range)\n        {\n            for(double i = pos[0] - range; i < pos[0] + range; i += scale){\n                for(double j = pos[1] - range; j < pos[1] + range; j += scale){\n                    for(double k = pos[2] - range; k < pos[2] + range; k += scale){\n                        std::cout << \"test !\" << std::endl;\n                        Eigen::Vector3d tmp_pos = {i , j , k};\n                        Eigen::Vector3i id = ((tmp_pos - o) / scale).cast<int>();\n                        if (id(0) >= 0 && id(1) >= 0 && id(2) >= 0 &&\n                            id(0) < mapSize(0) && id(1) < mapSize(1) && id(2) < mapSize(2))\n                        {\n                            voxels[id.dot(step)] = Unoccupied;\n                        }\n                    }\n                \n                }\n            }\n        }\n\n        inline void resetOcc(const Eigen::Vector3d &pos, double range)\n        {\n            for(double i = pos[0] - range; i < pos[0] + range; i += scale){\n                for(double j = pos[1] - range; i < pos[1] + range; i += scale){\n                    for(double k = pos[2] - range; i < pos[2] + range; i += scale){\n                        Eigen::Vector3d tmp_pos = {i , j , k};\n                        Eigen::Vector3i id = ((tmp_pos - o) / scale).cast<int>();\n                        if (id(0) >= 0 && id(1) >= 0 && id(2) >= 0 &&\n                            id(0) < mapSize(0) && id(1) < mapSize(1) && id(2) < mapSize(2))\n                        {\n                            voxels[id.dot(step)] = Occupied;\n                        }\n                    }\n                \n                }\n            }\n        }\n\n        inline void dilate(const int &r)\n        {\n            if (r <= 0)\n            {\n                return;\n            }\n            else\n            {\n                std::vector<Eigen::Vector3i> lvec, cvec;\n                lvec.reserve(voxNum);\n                cvec.reserve(voxNum);\n                int i, j, k, idx;\n                bool check;\n                for (int x = 0; x <= bounds(0); x++)\n                {\n                    for (int y = 0; y <= bounds(1); y += step(1))\n                    {\n                        for (int z = 0; z <= bounds(2); z += step(2))\n                        {\n                            if (voxels[x + y + z] == Occupied)\n                            {\n                                VOXEL_DILATER(i, j, k,\n                                              x, y, z,\n                                              step(1), step(2),\n                                              bounds(0), bounds(1), bounds(2),\n                                              check, voxels, idx, Dilated, cvec)\n                            }\n                        }\n                    }\n                }\n                for (int loop = 1; loop < r; loop++)\n                {\n                    std::swap(cvec, lvec);\n                    for (const Eigen::Vector3i &id : lvec)\n                    {\n                        VOXEL_DILATER(i, j, k,\n                                      id(0), id(1), id(2),\n                                      step(1), step(2),\n                                      bounds(0), bounds(1), bounds(2),\n                                      check, voxels, idx, Dilated, cvec)\n                    }\n                    lvec.clear();\n                }\n                surf = cvec;\n            }\n        }\n\n        inline void getSurfInBox(const Eigen::Vector3i &center,\n                                 const int &halfWidth,\n                                 std::vector<Eigen::Vector3d> &points) const\n        {\n            for (const Eigen::Vector3i &id : surf)\n            {\n                if (std::abs(id(0) - center(0)) <= halfWidth &&\n                    std::abs(id(1) / step(1) - center(1)) <= halfWidth &&\n                    std::abs(id(2) / step(2) - center(2)) <= halfWidth)\n                {\n                    points.push_back(id.cast<double>().cwiseProduct(stepScale) + oc);\n                }\n            }\n            return;\n        }\n\n        inline void getSurf(std::vector<Eigen::Vector3d> &points) const\n        {\n            points.reserve(surf.size());\n            for (const Eigen::Vector3i &id : surf)\n            {\n                points.push_back(id.cast<double>().cwiseProduct(stepScale) + oc);\n            }\n            return;\n        }\n\n        inline bool query(const Eigen::Vector3d &pos) const\n        {\n            const Eigen::Vector3i id = ((pos - o) / scale).cast<int>();\n            if (id(0) >= 0 && id(1) >= 0 && id(2) >= 0 &&\n                id(0) < mapSize(0) && id(1) < mapSize(1) && id(2) < mapSize(2))\n            {\n                return voxels[id.dot(step)];\n            }\n            else\n            {\n                return true;\n            }\n        }\n\n        inline bool query(const Eigen::Vector3i &id) const\n        {\n            if (id(0) >= 0 && id(1) >= 0 && id(2) >= 0 &&\n                id(0) < mapSize(0) && id(1) < mapSize(1) && id(2) < mapSize(2))\n            {\n                return voxels[id.dot(step)];\n            }\n            else\n            {\n                return true;\n            }\n        }\n\n        inline Eigen::Vector3d posI2D(const Eigen::Vector3i &id) const\n        {\n            return id.cast<double>() * scale + oc;\n        }\n\n        inline Eigen::Vector3i posD2I(const Eigen::Vector3d &pos) const\n        {\n            return ((pos - o) / scale).cast<int>();\n        }\n    };\n}\n\n#endif\n"
  },
  {
    "path": "tool_ws/src/traj_gen/include/navi/pathsearch.hpp",
    "content": "#include <iostream>\n#include <queue>\n#include <unordered_map>\n#include <vector>\n#include <cmath>\n#include <numeric> \n#include <Eigen/Eigen>\n#include <rclcpp/rclcpp.hpp>\n\n#include \"map/voxel_map.hpp\"\n#include \"common.h\"\n\nclass PathSearch {\nprivate:\n    Eigen::Vector3d end_;\n    voxel_map::VoxelMap global_map_;\n    std::vector<Eigen::Vector4d> record_list_;\n    std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> action_list_;  \n\n    struct Node {\n        Eigen::Vector3d pos;\n        double yaw;\n        double g_cost;\n        double h_cost;\n        std::shared_ptr<Node> parent;\n\n        Node(Eigen::Vector3d pos_, double yaw_, double g, double h, std::shared_ptr<Node> p = nullptr)\n            : pos(pos_), yaw(yaw_), g_cost(g), h_cost(h), parent(p) {}\n\n        double f_cost() const {\n            return g_cost + h_cost; // f(x) = g(x) + h(x)\n        }\n    };\n\n    struct CompareNode {\n        bool operator()(std::shared_ptr<Node> n1, std::shared_ptr<Node> n2) {\n            return n1->f_cost() > n2->f_cost();  \n        }\n    };\n    \n    double cal_dis(std::shared_ptr<Node> node, Eigen::Vector3d end) {\n        return (node->pos - end).norm();\n    }\n\n\n    double cal_dis(Node* node, Eigen::Vector3d end) {\n        return (node->pos - end).norm();\n    }\n\n    double cal_dis(Eigen::Vector3d start, Eigen::Vector3d end) {\n        return (start - end).norm();\n    }\n\n    double cal_dis(Node* node1, Node* node2) {\n        return (node1->pos - node2->pos).norm();\n    }\n\n    double cal_dis(std::shared_ptr<Node> node1, std::shared_ptr<Node> node2) {\n        return (node1->pos - node2->pos).norm();\n    }\n\npublic:\n    PathSearch(voxel_map::VoxelMap global_map, std::shared_ptr<rclcpp::Node> node)\n        : global_map_(global_map) {\n    }\n\n    double Manhattan(Eigen::Vector3d v1, Eigen::Vector3d v2) {\n        return (v1 - v2).cwiseAbs().sum();\n    }\n\n    // 计算启发函数：欧几里得距离\n    double heuristic(Eigen::Vector3d v1, Eigen::Vector3d v2) {\n        return (v1 - v2).norm();\n    }\n\n    bool isocc(Eigen::Vector3d pos) {\n        return global_map_.query(pos);\n    }\n\n    std::vector<std::shared_ptr<Node>> getNeighbors(std::shared_ptr<Node> current) {\n        std::vector<std::shared_ptr<Node>> neighbors;\n        Eigen::Vector3d cur_node_pos = current->pos;\n        double cur_yaw = current->yaw;\n\n        std::vector<Eigen::Vector3d> motions;\n        std::vector<double> angles = {0, 30, 60, 90, 120, 150, 180, -30, -60, -90, -120, -150};\n        int step_size = 3;\n\n        for (const double angle : angles) {\n            double rad = angle * M_PI / 180.0;\n            motions.push_back(Eigen::Vector3d{step_size * cos(rad), step_size * sin(rad), 0});\n        }\n        motions.push_back(Eigen::Vector3d{0, 0, 3}); \n        motions.push_back(Eigen::Vector3d{0, 0, -3});\n\n        for (int i = 0; i < motions.size(); ++i) {\n            const auto& motion = motions[i];\n            Eigen::Vector3d tmp_pos = cur_node_pos + motion;\n            double angle_cost = (i < angles.size() && angles[i] != cur_yaw) ? 2.0 : 0;\n            if (!isocc(tmp_pos)) {\n                double new_yaw = angles[i];\n                double tmp_gcost = heuristic(cur_node_pos, tmp_pos);\n                neighbors.push_back(std::make_shared<Node>(tmp_pos, new_yaw, current->g_cost + tmp_gcost + angle_cost, heuristic(tmp_pos, end_), current));\n            }\n        }\n        return neighbors;\n    }\n\n\nstd::vector<Eigen::Vector3d> hybridAStar(Eigen::Vector3d start, Eigen::Vector3d end) {\n    end_ = end;\n    std::priority_queue<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>>, CompareNode> open_list;\n    std::unordered_map<int, std::shared_ptr<Node>> closed_list;\n    double thr = 3.1;\n\n    std::shared_ptr<Node> start_node = std::make_shared<Node>(start, 0, 0, heuristic(start, end));\n    open_list.push(start_node);\n\n    if (isocc(start_node->pos)) {\n        std::cerr << \"Start point is occupied!\" << std::endl;\n        return {}; \n    }\n\n    auto start_time = rclcpp::Clock().now();\n    auto timeout_duration = std::chrono::minutes(2);  \n\n    while (!open_list.empty() && rclcpp::ok()) {\n\n        if (rclcpp::Clock().now() - start_time > timeout_duration) {\n            std::cout << \"\\033[33m\" << \"Search timeout exceeded 2 minutes.\" << \"\\033[0m\" << std::endl;\n            return {}; \n        }\n\n\n        std::shared_ptr<Node> current = open_list.top();\n        open_list.pop();\n\n        if (heuristic(current->pos, end) < thr) {\n            std::vector<Eigen::Vector3d> path;\n            while (current != nullptr) {\n                path.push_back(current->pos);\n                current = current->parent;\n            }\n            std::reverse(path.begin(), path.end());\n            return path;\n        }\n\n        std::vector<std::shared_ptr<Node>> neighbors = getNeighbors(current);\n        for (std::shared_ptr<Node> neighbor : neighbors) {\n            int neighbor_hash = static_cast<int>(neighbor->pos[0]) * 1000000 + \n                                 static_cast<int>(neighbor->pos[1]) * 1000 + \n                                 static_cast<int>(neighbor->pos[2]);\n\n            if (closed_list.find(neighbor_hash) != closed_list.end()) {\n                continue;\n            }\n\n            neighbor->h_cost = heuristic(neighbor->pos, end);\n            open_list.push(neighbor);\n            closed_list[neighbor_hash] = neighbor;\n        }\n    }\n    return {};\n}\n\n    std::vector<Eigen::Vector4d> caluavrecord(Eigen::Vector3d cur_p, Eigen::Vector3d next_p, double in_yaw, double out_yaw) {\n        std::vector<Eigen::Vector4d> record_list;\n        double yaw_error = std::round((out_yaw - in_yaw) / M_PI * 180);\n        if(yaw_error < -180) yaw_error += 360;\n        if(yaw_error > 180) yaw_error -= 360;\n\n\n        int turn_nums = std::abs(std::round(yaw_error / 30.0));\n        double step_yaw = (M_PI) / 180 * 30;\n        if (yaw_error != 0) {\n            if (yaw_error > 0) {\n                for(int i = 0 ; i <= turn_nums; i ++){\n                    record_list.emplace_back(cur_p[0], cur_p[1], cur_p[2], in_yaw + i * step_yaw);\n                }\n\n            } else {\n                for(int i = 0 ; i <= turn_nums; i ++){\n                    record_list.emplace_back(cur_p[0], cur_p[1], cur_p[2], in_yaw - i * step_yaw);\n                }\n            }\n        }\n        else{\n            record_list.emplace_back(cur_p[0], cur_p[1], cur_p[2], in_yaw);\n        }\n\n        return record_list;\n    }\n    \n\n    std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> caluavaction(Eigen::Vector3d start, Eigen::Vector3d end, double in_yaw, double out_yaw) {\n        std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> action_list;\n        double yaw_error = std::round((out_yaw - in_yaw) / M_PI * 180);  // yaw error in degrees\n        \n        if (yaw_error < -180) yaw_error += 360;  // Normalize to [-180, 180]\n        if (yaw_error > 180) yaw_error -= 360;\n\n        int turn_nums = std::abs(std::round(yaw_error / 30.0));  // Number of 30-degree turns required\n\n        if(end[2] - start[2] > 1){\n            action_list.emplace_back(std::make_pair(\"go up\", std::round(cal_dis(start, end))), start);\n            return action_list;  \n        }\n        if(end[2] - start[2] < -1){\n            action_list.emplace_back(std::make_pair(\"go down\", std::round(cal_dis(start, end))), start);  \n            return action_list;\n        }\n        \n\n        if (yaw_error != 0) {\n            if (yaw_error > 0) {\n                // std::cout << \"turn_nums: \" << turn_nums << std::endl;\n                for (int i = 0; i < turn_nums; i++) {\n                    action_list.emplace_back(std::make_pair(\"turn left\", 30), start);  // Turning left\n                }\n            } else {\n                // std::cout << \"turn_nums: \" << turn_nums << std::endl;\n                for (int i = 0; i < turn_nums; i++) {\n                    action_list.emplace_back(std::make_pair(\"turn right\", 30), start);  // Turning right\n                }\n            }\n            \n            // Move straight after turning\n            action_list.emplace_back(std::make_pair(\"go straight\", std::round(cal_dis(start, end))), start);  \n        } else {\n            // If no yaw error, just go straight\n            action_list.emplace_back(std::make_pair(\"go straight\", std::round(cal_dis(start, end))), start);\n        }\n\n        return action_list;\n    }\n\n\n\n\n    void backtrackpath(const std::vector<Eigen::Vector3d>& path, bool with_stop) {\n        record_list_.clear();\n        action_list_.clear();\n        if (path.size() <= 2) {\n            // RCLCPP_WARN(node_->get_logger(), \"Path error: Path size is too small.\");\n            return;\n        }\n        double start_yaw = calculateYaw(path[1] - path[0]);\n        for (int i = 0; i < path.size() - 1; ++i) {\n            double in_yaw = 0;\n            double out_yaw = calculateYaw(path[i + 1] - path[i]);\n            if(i == 0){\n                in_yaw = start_yaw;\n            }\n            else{\n                in_yaw = calculateYaw(path[i] - path[i - 1]);\n                if(abs(path[i + 1][2] - path[i][2]) > 1){\n                    in_yaw = record_list_[record_list_.size() -1][3];\n                    out_yaw = in_yaw;\n                }\n                else if(abs(path[i][2] - path[i - 1][2]) > 1 ){\n                    in_yaw = record_list_[record_list_.size() -1][3];\n                    // out_yaw = in_yaw;\n                }\n            }\n\n\n            auto tmp_act_list = caluavaction(path[i], path[i + 1], in_yaw, out_yaw);\n            auto tmp_rec_list = caluavrecord(path[i], path[i + 1], in_yaw, out_yaw);\n\n            action_list_.insert(action_list_.end(), tmp_act_list.begin(), tmp_act_list.end());\n            record_list_.insert(record_list_.end(), tmp_rec_list.begin(), tmp_rec_list.end());\n        }\n        double end_yaw = record_list_.back()[3];\n        if(with_stop){\n            record_list_.emplace_back(path.back()[0], path.back()[1], path.back()[2], end_yaw);\n            action_list_.emplace_back(std::make_pair(\"stop\",0), path.back());\n        }\n    }\n\n    std::vector<Eigen::Vector4d> getrecordlist() {\n        return record_list_;\n    }\n\n     std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> getactionlist() {\n        return action_list_;\n    }\n};\n"
  },
  {
    "path": "tool_ws/src/traj_gen/launch/traj_gen_launch.py",
    "content": "from launch import LaunchDescription\nfrom launch.actions import TimerAction\nfrom launch_ros.actions import Node\nimport os\nfrom launch.actions import DeclareLaunchArgument, LogInfo\nfrom launch.substitutions import LaunchConfiguration\nfrom ament_index_python.packages import get_package_share_directory\nfrom launch.launch_service import LaunchService\nfrom pathlib import Path\n\nimport sys\nsys.executable = \"/usr/bin/python3\"  # 替换成系统默认的Python路径\n\ndef generate_launch_description():\n    cur_path = Path(__file__).resolve()\n    package_path = cur_path.parent.parent\n    rviz_config = os.path.join(package_path, 'rviz', 'traj_gen.rviz')\n    \n    return LaunchDescription([\n        DeclareLaunchArgument('env', default_value='env_airsim_16.yaml', description='环境配置文件'),\n        \n        # 修改RViz节点配置，设置日志级别为WARN\n        Node(\n            package='rviz2',\n            executable='rviz2',\n            name='rviz',\n            output='screen',\n            arguments=['-d', rviz_config, '--ros-args', '--log-level', 'WARN']  # 添加日志级别参数\n        ),\n\n        Node(\n            package='traj_gen',\n            executable='traj_gen_node',\n            name='traj_gen_node',\n            output='screen',\n            parameters=[{'env': LaunchConfiguration('env')}],\n        ),\n    ])\n\ndef main():\n    launch_description = generate_launch_description()\n    launch_service = LaunchService()\n    launch_service.include_launch_description(launch_description)\n    print(\"启动ROS 2 launch文件...\")\n    launch_service.run()\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tool_ws/src/traj_gen/package.xml",
    "content": "<?xml version=\"1.0\"?>\n<package format=\"3\">\n  <name>traj_gen</name>\n  <version>0.0.0</version>\n  <description>The ue_dage_gen package for ROS2.</description>\n\n  <!-- Maintainer information -->\n  <maintainer email=\"kermit@todo.todo\">kermit</maintainer>\n\n  <!-- License information -->\n  <license>Apache-2.0</license>\n\n  <!-- Dependencies for building and running the package -->\n  <buildtool_depend>ament_cmake</buildtool_depend>\n\n  <!-- Build dependencies -->\n  <build_depend>rclcpp</build_depend>\n  <build_depend>std_msgs</build_depend>\n  <build_depend>geometry_msgs</build_depend>\n  <build_depend>sensor_msgs</build_depend>\n  <build_depend>nav_msgs</build_depend>\n  <build_depend>visualization_msgs</build_depend>\n  <build_depend>airsim_ros_pkgs</build_depend>\n\n  <!-- Execution dependencies -->\n  <exec_depend>rclcpp</exec_depend>\n  <exec_depend>std_msgs</exec_depend>\n  <exec_depend>geometry_msgs</exec_depend>\n  <exec_depend>sensor_msgs</exec_depend>\n  <exec_depend>nav_msgs</exec_depend>\n  <exec_depend>visualization_msgs</exec_depend>\n  <exec_depend>airsim_ros_pkgs</exec_depend>\n  <exec_depend>pcl_ros</exec_depend>\n\n  <!-- Test dependencies (optional) -->\n  <test_depend>ament_lint_auto</test_depend>\n  <test_depend>ament_lint_common</test_depend>\n\n  <!-- Export build type -->\n  <export>\n    <build_type>ament_cmake</build_type>\n  </export>\n</package>\n"
  },
  {
    "path": "tool_ws/src/traj_gen/rviz/traj_gen.rviz",
    "content": "Panels:\n  - Class: rviz_common/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded:\n        - /Global Options1\n        - /Status1\n        - /PointCloud21\n        - /Path1\n        - /Axes1\n      Splitter Ratio: 0.5\n    Tree Height: 549\n  - Class: rviz_common/Selection\n    Name: Selection\n  - Class: rviz_common/Tool Properties\n    Expanded:\n      - /2D Goal Pose1\n      - /Publish Point1\n    Name: Tool Properties\n    Splitter Ratio: 0.5886790156364441\n  - Class: rviz_common/Views\n    Expanded:\n      - /Current View1\n    Name: Views\n    Splitter Ratio: 0.5\n  - Class: rviz_common/Time\n    Experimental: false\n    Name: Time\n    SyncMode: 0\n    SyncSource: \"\"\nVisualization Manager:\n  Class: \"\"\n  Displays:\n    - Alpha: 0.5\n      Cell Size: 1\n      Class: rviz_default_plugins/Grid\n      Color: 160; 160; 164\n      Enabled: true\n      Line Style:\n        Line Width: 0.029999999329447746\n        Value: Lines\n      Name: Grid\n      Normal Cell Count: 0\n      Offset:\n        X: 0\n        Y: 0\n        Z: 0\n      Plane: XY\n      Plane Cell Count: 10\n      Reference Frame: <Fixed Frame>\n      Value: true\n    - Alpha: 0.5\n      Autocompute Intensity Bounds: true\n      Autocompute Value Bounds:\n        Max Value: -10.53083610534668\n        Min Value: -100.81780242919922\n        Value: true\n      Axis: Z\n      Channel Name: intensity\n      Class: rviz_default_plugins/PointCloud2\n      Color: 255; 255; 255\n      Color Transformer: AxisColor\n      Decay Time: 0\n      Enabled: true\n      Invert Rainbow: false\n      Max Color: 255; 255; 255\n      Max Intensity: 4096\n      Min Color: 0; 0; 0\n      Min Intensity: 0\n      Name: PointCloud2\n      Position Transformer: XYZ\n      Selectable: true\n      Size (Pixels): 3\n      Size (m): 1\n      Style: Flat Squares\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        Filter size: 10\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /thread_1/trajgen/show/global_map\n      Use Fixed Frame: true\n      Use rainbow: true\n      Value: true\n    - Alpha: 1\n      Buffer Length: 1\n      Class: rviz_default_plugins/Path\n      Color: 192; 28; 40\n      Enabled: true\n      Head Diameter: 0.30000001192092896\n      Head Length: 0.20000000298023224\n      Length: 0.30000001192092896\n      Line Style: Lines\n      Line Width: 0.029999999329447746\n      Name: Path\n      Offset:\n        X: 0\n        Y: 0\n        Z: 0\n      Pose Color: 255; 85; 255\n      Pose Style: None\n      Radius: 0.029999999329447746\n      Shaft Diameter: 0.10000000149011612\n      Shaft Length: 0.10000000149011612\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        Filter size: 10\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /thread_1/trajgen/show/search_path\n      Value: true\n    - Class: rviz_default_plugins/Axes\n      Enabled: true\n      Length: 1000\n      Name: Axes\n      Radius: 1\n      Reference Frame: <Fixed Frame>\n      Value: true\n    - Class: rviz_default_plugins/MarkerArray\n      Enabled: true\n      Name: MarkerArray\n      Namespaces:\n        {}\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /planner1/planner/show/points\n      Value: true\n  Enabled: true\n  Global Options:\n    Background Color: 48; 48; 48\n    Fixed Frame: map\n    Frame Rate: 30\n  Name: root\n  Tools:\n    - Class: rviz_default_plugins/Interact\n      Hide Inactive Objects: true\n    - Class: rviz_default_plugins/MoveCamera\n    - Class: rviz_default_plugins/Select\n    - Class: rviz_default_plugins/FocusCamera\n    - Class: rviz_default_plugins/Measure\n      Line color: 128; 128; 0\n    - Class: rviz_default_plugins/SetInitialPose\n      Covariance x: 0.25\n      Covariance y: 0.25\n      Covariance yaw: 0.06853891909122467\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /initialpose\n    - Class: rviz_default_plugins/SetGoal\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /goal_pose\n    - Class: rviz_default_plugins/PublishPoint\n      Single click: true\n      Topic:\n        Depth: 5\n        Durability Policy: Volatile\n        History Policy: Keep Last\n        Reliability Policy: Reliable\n        Value: /clicked_point\n  Transformation:\n    Current:\n      Class: rviz_default_plugins/TF\n  Value: true\n  Views:\n    Current:\n      Class: rviz_default_plugins/Orbit\n      Distance: 1315.884033203125\n      Enable Stereo Rendering:\n        Stereo Eye Separation: 0.05999999865889549\n        Stereo Focal Distance: 1\n        Swap Stereo Eyes: false\n        Value: false\n      Focal Point:\n        X: 21.930570602416992\n        Y: 497.54791259765625\n        Z: 480.8567810058594\n      Focal Shape Fixed Size: true\n      Focal Shape Size: 0.05000000074505806\n      Invert Z Axis: false\n      Name: Current View\n      Near Clip Distance: 0.009999999776482582\n      Pitch: 0.7497975826263428\n      Target Frame: <Fixed Frame>\n      Value: Orbit (rviz)\n      Yaw: 2.649092435836792\n    Saved: ~\nWindow Geometry:\n  Displays:\n    collapsed: false\n  Height: 846\n  Hide Left Dock: false\n  Hide Right Dock: true\n  QMainWindow State: 000000ff00000000fd000000040000000000000230000002b0fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002b0000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002b0fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d000002b0000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000053c0000003efc0100000002fb0000000800540069006d006501000000000000053c000002fb00fffffffb0000000800540069006d0065010000000000000450000000000000000000000306000002b000000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000\n  Selection:\n    collapsed: false\n  Time:\n    collapsed: false\n  Tool Properties:\n    collapsed: false\n  Views:\n    collapsed: true\n  Width: 1340\n  X: 882\n  Y: 324\n"
  },
  {
    "path": "tool_ws/src/traj_gen/src/traj_gen.cc",
    "content": "#include <iostream>\n#include <thread>\n#include <chrono>\n#include <map>\n#include <queue>\n#include <string>\n#include <unordered_map>\n#include <random>\n#include <cstdlib>\n\n// ROS2 headers\n#include <rclcpp/rclcpp.hpp>\n#include <rclcpp/qos.hpp>\n\n// ROS2 msgs\n#include <nav_msgs/msg/path.hpp>\n#include <nav_msgs/msg/odometry.hpp>\n#include <geometry_msgs/msg/pose_stamped.hpp>\n#include <sensor_msgs/msg/point_cloud2.hpp>\n#include <std_msgs/msg/float64_multi_array.hpp>\n#include <geometry_msgs/msg/twist_stamped.hpp>\n#include <visualization_msgs/msg/marker.hpp>\n#include <visualization_msgs/msg/marker_array.hpp>\n\n// ROS2 package\n#include <ament_index_cpp/get_package_share_directory.hpp>\n\n// Eigen\n#include <Eigen/Eigen>\n\n// sys\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <yaml-cpp/yaml.h>\n#include <json/json.h>\n#include <nlohmann/json.hpp>\n\n// PCL\n#include <pcl/common/transforms.h>\n\n//lib\n#include \"map/voxel_map.hpp\"\n#include \"map/loadpcdmap.hpp\"\n#include \"navi/pathsearch.hpp\"\n#include \"base/tcpserver.hpp\"\n#include \"map/seggridmap.hpp\"\n#include \"common.h\"\n\n\nnamespace fs = std::filesystem;\n\nclass TrajGen{\nprivate:\n    //ros\n    std::shared_ptr<rclcpp::Node> node_; \n    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr global_map_Pub_;\n    rclcpp::Publisher<nav_msgs::msg::Path>::SharedPtr search_path_Pub_;\n    rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr show_point_Pub_;\n\n    //navi\n    std::vector<Eigen::Vector3d> global_route_;\n    nav_msgs::msg::Path true_path_;\n    Eigen::Vector3d target_;\n    PathSearch* pathsearch_;\n    double flyheight_;\n\n    //map\n    voxel_map::VoxelMap global_voxelmap_;\n    voxel_map::VoxelMap bev_voxelmap_;\n    double dilateRadius_;\n    double voxelWidth_;\n    std::vector<double> mapBound_;\n    sensor_msgs::msg::PointCloud2 cloud_msg_;\n    bool mapInitialized_;\n    std::vector<Eigen::Vector3d> eigen_pc_;\n\n\n    //tcp\n    TCPServer* tcpserver_;\n    int listen_port_;\n    int send_port_; \n    std::string sim_ip_;\n\n\n    // Visualizer visualizer;\n    std::string data_record_path_ = \"\";\n    std::string pose_record_file_name_;\n    int image_frame_ = 0;\n\n    //scene data\n    std::string env_;\n    std::string data_type_;\n    std::string pr_dir_;\n    double send_freq_;\n    double env_scale_ratio_;    \n    double traj_scale_ratio_;\n    double map_elevation_;\n    double min_height_thresh_;\n    double objinfov_height_thr_ = 15;\n    double map_height_thr_ = 0;\n\n    std::vector<Object> obj_list_;\n    Object cur_obj_;\n\npublic:\n    TrajGen(std::shared_ptr<rclcpp::Node> node, int send_p, int listen_p, std::string sim_ip,\n                    const std::string& node_name, const std::string& env_name)\n        : node_(node), \n        mapInitialized_(false), \n        send_port_(send_p), \n        listen_port_(listen_p),\n        sim_ip_(sim_ip), \n        env_(env_name)\n    {\n        global_map_Pub_ = node_->create_publisher<sensor_msgs::msg::PointCloud2>(node_name + \"/trajgen/show/global_map\", 10);\n        search_path_Pub_ = node_->create_publisher<nav_msgs::msg::Path>(node_name + \"/trajgen/show/search_path\", 10);\n        show_point_Pub_ = node_->create_publisher<visualization_msgs::msg::MarkerArray>(node_name + \"/trajgen/show/points\", 10);\n        pr_dir_ = getProjectDir();\n        std::string yaml_path = pr_dir_ +  \"/configs/\" + env_ + \".yaml\";\n        std::cout << \"\\033[33m\"  << \"The Root Directory of OpenFly Project: \" << pr_dir_  << \"\\033[0m\" << std::endl;\n        std::cout << \"\\033[33m\" << \"The YAML file path: \" << yaml_path << \"\\033[0m\" << std::endl;\n        // load YAML configuration\n        loadYaml(yaml_path);\n        data_record_path_ = pr_dir_ + \"/uav_vln_data/\" + env_ + \"/\" + data_type_ + \"/\";\n        mapBound_[4] = map_elevation_;\n        const Eigen::Vector3i xyz(\n            (mapBound_[1] - mapBound_[0]) / voxelWidth_,\n            (mapBound_[3] - mapBound_[2]) / voxelWidth_,\n            (mapBound_[5] - mapBound_[4]) / voxelWidth_\n        );\n        const Eigen::Vector3d offset(mapBound_[0], mapBound_[2], mapBound_[4]);\n        const Eigen::Vector3i bev_xyz(\n            (mapBound_[1] - mapBound_[0]) / voxelWidth_,\n            (mapBound_[3] - mapBound_[2]) / voxelWidth_,\n            voxelWidth_ / voxelWidth_\n        );\n        const Eigen::Vector3d bev_offset(mapBound_[0], mapBound_[2], -0.5 * voxelWidth_);\n        global_voxelmap_ = voxel_map::VoxelMap(xyz, offset, voxelWidth_);\n        bev_voxelmap_ = voxel_map::VoxelMap(bev_xyz, bev_offset, voxelWidth_);\n        // Build the global and BEV maps, and initialize the graph\n        globalMapBulid();\n        bevMapBulid();\n        initGraph();\n\n        pathsearch_ = new PathSearch(global_voxelmap_, node_);\n        tcpserver_ = new TCPServer(sim_ip_, send_port_, listen_port_);\n\n        std::cout << \"Initialization complete.\" << std::endl;\n    }\n\n    std::string getProjectDir() {\n        fs::path currentPath(__FILE__);\n        fs::path rosDir = currentPath.parent_path().parent_path().parent_path().parent_path().parent_path();\n        return rosDir.string(); \n    }\n\n    void trajDataGen(int k, int r, int R, int h, int H, int landmark_nums = 1, bool add_takeoff_land = false, bool with_turn = false) {\n        if (env_.find(\"gs\") != std::string::npos) {\n            add_takeoff_land = false;\n            with_turn = false;\n            std::cout << \"\\033[33m\" << \"GS env cannot add takeoff and landing\" << \"\\033[0m\"<< std::endl;\n            std::cout << \"\\033[33m\" << \"GS env cannot forcefully navigate obstacles on a single trajectory\" << \"\\033[0m\"<< std::endl;\n        }\n        std::cout << \"\\033[32m\" << \"***************************************\"                                                                            << \"\\033[0m\" << std::endl;\n        std::cout << \"\\033[32m\" << \"Plan to collect \" << k << \" trajectories\"                                                                           << \"\\033[0m\" << std::endl;\n        std::cout << \"\\033[32m\" << \"Each trajectory has \" << landmark_nums << \" landmarks\"                                                              << \"\\033[0m\" << std::endl;\n        std::cout << \"\\033[32m\" << (add_takeoff_land ? \"with takeoff and landing\" : \" without takeoff and landing\")                                     << \"\\033[0m\" << std::endl;\n        std::cout << \"\\033[32m\" << (with_turn ? \"There must be a turn in the trajectory\" : \"There may not necessarily be a turn in the trajectory\")     << \"\\033[0m\" << std::endl;\n        std::cout << \"\\033[32m\" << \"***************************************\"                                                                            << \"\\033[0m\" << std::endl;\n        if(landmark_nums == 1){\n            genTrajbyOneLandmark(k, r, R, h, H, add_takeoff_land, with_turn);\n        }\n        else if (landmark_nums >= 1)\n        {\n            genTrajbyMultiLandmark(k, r, R, h, H, landmark_nums , with_turn, add_takeoff_land);\n        }\n    }\n\n    void globalMapBulid() {\n        cloud_msg_ = loadpcdfile(pr_dir_ + \"/scene_data/pcd_map/\" + env_ + \".pcd\");\n        cloud_msg_ = scalePointCloud(cloud_msg_, env_scale_ratio_);\n        cloud_msg_ = filterPointCloud(cloud_msg_, voxelWidth_);\n        cloud_msg_ = removeOutliersWithStatisticalFilter(cloud_msg_, 60, 2.0);  \n\n        cloud_msg_.header.frame_id = \"map\";\n        cloud_msg_.header.stamp = node_->get_clock()->now();\n        rclcpp::sleep_for(std::chrono::seconds(2));\n        global_map_Pub_->publish(cloud_msg_);\n        std::cout << \"\\033[32m\" <<\"Global map loaded successfully.\"  << \"\\033[0m\" << std::endl;\n\n        if (!mapInitialized_) {\n            size_t cur = 0;\n            const size_t total = cloud_msg_.data.size() / cloud_msg_.point_step;\n            float *fdata = reinterpret_cast<float*>(&cloud_msg_.data[0]);\n            for (size_t i = 0; i < total; i++) {\n                cur = cloud_msg_.point_step / sizeof(float) * i;\n                if (std::isnan(fdata[cur + 0]) || std::isinf(fdata[cur + 0]) ||\n                    std::isnan(fdata[cur + 1]) || std::isinf(fdata[cur + 1]) ||\n                    std::isnan(fdata[cur + 2]) || std::isinf(fdata[cur + 2])) {\n                    continue;\n                }\n                eigen_pc_.push_back(Eigen::Vector3d(fdata[cur + 0], fdata[cur + 1], fdata[cur + 2]));\n                global_voxelmap_.setOccupied(Eigen::Vector3d(fdata[cur + 0],\n                                                            fdata[cur + 1],\n                                                            fdata[cur + 2]));\n            }\n            global_voxelmap_.dilate(std::ceil(dilateRadius_ / global_voxelmap_.getScale()));\n            mapInitialized_ = true;\n        }\n    }\n\n    void bevMapBulid() {\n        size_t cur = 0;\n        const size_t total = cloud_msg_.data.size() / cloud_msg_.point_step;\n        float *fdata = reinterpret_cast<float*>(&cloud_msg_.data[0]);\n        for (size_t i = 0; i < total; i++) {\n            cur = cloud_msg_.point_step / sizeof(float) * i;\n            if (std::isnan(fdata[cur + 0]) || std::isinf(fdata[cur + 0]) ||\n                std::isnan(fdata[cur + 1]) || std::isinf(fdata[cur + 1]) ||\n                std::isnan(fdata[cur + 2]) || std::isinf(fdata[cur + 2])) {\n                continue;\n            }\n            if(fdata[cur + 2]  < map_elevation_ + min_height_thresh_) continue;\n            bev_voxelmap_.setOccupied(Eigen::Vector3d(fdata[cur + 0],\n                                                            fdata[cur + 1],\n                                                            0));\n        }\n        bev_voxelmap_.dilate(std::ceil(dilateRadius_ / bev_voxelmap_.getScale()));\n        mapInitialized_ = true;   \n    }\n\n    bool bevQuery(Eigen::Vector3d pos){\n        pos.z() = 0;\n        if(bev_voxelmap_.query(pos)){\n            return true;\n        }\n        else return false;\n    }\n\n    void initGraph(){\n        std::string graph_json = pr_dir_ + \"/scene_data/seg_map/\" + env_ +  \".jsonl\";\n        obj_list_ = loadObjectsFromFile(graph_json);\n    }\n\n    Object parseObject(const nlohmann::json& jsonObject) {\n\n        std::string type = jsonObject.at(\"type\").get<std::string>();\n        std::string color = jsonObject.at(\"color\").get<std::string>();\n        std::string size = jsonObject.at(\"size\").get<std::string>();\n        std::string shape = jsonObject.at(\"shape\").get<std::string>();\n        std::string feature = jsonObject.at(\"feature\").get<std::string>();\n        std::string filename = jsonObject.at(\"filename\").get<std::string>();\n\n        double x, y, z;\n        sscanf(filename.c_str(), \"X=%lfY=%lfZ=%lf\", &x, &y, &z);\n        Eigen::Vector3d pos(x, y, z);\n        if(env_ == \"env_ue_bigcity\" || env_ ==\"env_ue_smallcity\"){\n            pos = pos/100; \n            pos[1] = -pos[1];\n        }\n        else{\n            pos = pos * env_scale_ratio_;\n        }\n        return Object(type, color, size, shape, feature, pos);\n    }\n\n\n    std::vector<Object> loadObjectsFromFile(const std::string& filePath) {\n        std::vector<Object> objects;\n        std::ifstream file(filePath);\n        if (!file.is_open()) {\n            std::cerr << \"Failed to open JSON file: \" << filePath << std::endl;\n            return objects;\n        }\n        std::string line;\n        while (std::getline(file, line)) {\n            try {\n                nlohmann::json jsonObject = nlohmann::json::parse(line);\n                objects.push_back(parseObject(jsonObject));\n            } catch (const nlohmann::json::exception& e) {\n                std::cerr << \"JSON parsing error: \" << e.what() << std::endl;\n            }\n        }\n        file.close();\n        return objects;\n    }\n\n\n    void loadYaml(std::string yaml_file){\n        std::ifstream ifile(yaml_file);\n        if (!ifile) {\n            std::cerr << \"YAML file not found: \" << yaml_file << std::endl;\n            return;\n        }\n            auto yaml = YAML::LoadFile(yaml_file);\n            env_ = yaml[\"datagen\"][\"env\"].as<std::string>();\n            data_type_ = yaml[\"datagen\"][\"data_type\"].as<std::string>();\n            send_freq_ = yaml[\"datagen\"][\"freq\"].as<double>();\n            dilateRadius_ = yaml[\"traj_map\"][\"DilateRadius\"].as<double>();\n            voxelWidth_ = yaml[\"traj_map\"][\"VoxelWidth\"].as<double>();\n            mapBound_ = {\n                yaml[\"traj_map\"][\"MapBound\"][0].as<double>(),\n                yaml[\"traj_map\"][\"MapBound\"][1].as<double>(),\n                yaml[\"traj_map\"][\"MapBound\"][2].as<double>(),\n                yaml[\"traj_map\"][\"MapBound\"][3].as<double>(),\n                yaml[\"traj_map\"][\"MapBound\"][4].as<double>(),\n                yaml[\"traj_map\"][\"MapBound\"][5].as<double>()\n            };\n            env_scale_ratio_ = yaml[\"traj_map\"][\"pcd_scale_ratio\"].as<double>();\n            traj_scale_ratio_ = yaml[\"traj_map\"][\"traj_scale_ratio\"].as<double>();\n            map_elevation_ = yaml[\"traj_map\"][\"map_elevation\"].as<double>();\n            min_height_thresh_ = yaml[\"traj_map\"][\"min_height_thresh\"].as<double>();\n            map_height_thr_ = map_elevation_ + min_height_thresh_;\n\n    }\n \n    std::string getCurrentTimeString() {\n        std::time_t now = std::time(nullptr);\n        std::tm *ltm = std::localtime(&now);\n        std::ostringstream time_stream;\n        time_stream << 1900 + ltm->tm_year << \"-\"\n                    << 1 + ltm->tm_mon << \"-\"\n                    << ltm->tm_mday << \"_\"\n                    << ltm->tm_hour << \"-\"\n                    << ltm->tm_min << \"-\"\n                    << ltm->tm_sec;\n        int random_number = std::rand();\n        time_stream << \"_\" << random_number; \n        return time_stream.str();\n    }\n\n    void initRecord(const std::string& filename) {\n        std::string time_path = getCurrentTimeString();\n        pose_record_file_name_ = filename + time_path + \"/\";\n        try {\n            fs::create_directories(pose_record_file_name_);\n            std::cout << \"Directory created for record pose : \" << pose_record_file_name_ << std::endl;\n        } catch (const fs::filesystem_error& e) {\n            std::cerr << \"Error creating directories: \" << e.what() << std::endl;\n        }\n    }\n\n\n    void astartSearchPath(Eigen::Vector3d start, Eigen::Vector3d end, bool with_stop = false){ \n        true_path_.poses.clear();\n        std::vector<Eigen::Vector3d> v3d_path;\n        sleep(2);\n        v3d_path = pathsearch_->hybridAStar(start, end);\n        nav_msgs::msg::Path showpath;\n        showpath.header.frame_id = \"map\";\n        showpath.header.stamp = node_->now();\n\n        for (const auto& point : v3d_path) {\n            geometry_msgs::msg::PoseStamped tmp_pose;\n            tmp_pose.pose.position.x = point[0];\n            tmp_pose.pose.position.y = point[1];\n            tmp_pose.pose.position.z = point[2];\n            showpath.poses.push_back(tmp_pose);\n        }\n        rclcpp::Rate looprate(10);\n        for (int i = 0; i < 10; i++) {\n            search_path_Pub_->publish(showpath);\n            looprate.sleep();\n        }\n        pathsearch_->backtrackpath(v3d_path, with_stop);\n    }\n\n\n    void saveTrajListToJson(std::vector<TrajRecodData> traj_list, const std::string& filename){\n        std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> action_list;\n        std::vector<Eigen::Vector4d> record_list;\n        using json = nlohmann::json;\n        std::ofstream outputFile(filename);\n        if (!outputFile.is_open()) {\n            std::cerr << \"\\033[31m\" << \"Failed to open file for writing : \" << filename  << \"\\033[0m\"<< std::endl;\n            return;\n        }\n        int image_id = 0;\n\n        for(int i = 0 ; i < traj_list.size(); i ++){\n            action_list = traj_list[i].action_list;\n            record_list = traj_list[i].record_list;\n\n            for(size_t i = 0 ; i < action_list.size(); i++){\n                json j;\n\n                json actionj;\n                actionj[\"imageid\"] = image_id;\n                actionj[\"type\"] = action_list[i].first.first;\n                actionj[\"value\"] = action_list[i].first.second;\n                actionj[\"pos\"] = action_list[i].second;\n                actionj[\"yaw\"] = record_list[i][3];\n                j[\"action\"] = actionj;\n                outputFile << j.dump(4) << std::endl; \n                image_id ++;\n            }\n            Object obj = traj_list[i].aim_obj;\n            json jendobj;\n            jendobj[\"type\"] = obj.type;\n            jendobj[\"color\"] = obj.color;\n            jendobj[\"size\"] = obj.size;\n            jendobj[\"shape\"] = obj.shape;\n            jendobj[\"feature\"] = obj.feature;\n            jendobj[\"position\"] = {obj.pos.x(), obj.pos.y(), obj.pos.z()};  // Convert Eigen::Vector3d to array\n            json aim_landmark_j;\n            aim_landmark_j[\"aim_landmark\"] = jendobj;\n            outputFile << aim_landmark_j.dump(4) << std::endl;  // Pretty print with indent of 4\n        }\n        outputFile.close();\n        \n    }\n\n    void saveActionsToJson(const std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>>& action_list, \n                            std::vector<Eigen::Vector4d> record_list,\n                            const std::string& filename) {\n        using json = nlohmann::json;\n        std::ofstream outputFile(filename);\n        if (!outputFile.is_open()) {\n            std::cerr << \"\\033[31m\" << \"Failed to open file for writing : \" << filename  << \"\\033[0m\"<< std::endl;\n            return;\n        }\n        for(size_t i = 0 ; i < action_list.size(); i++){\n            json j;\n\n            json actionj;\n            actionj[\"imageid\"] = i;\n            actionj[\"type\"] = action_list[i].first.first;\n            actionj[\"value\"] = action_list[i].first.second;\n            actionj[\"pos\"] = action_list[i].second;\n            actionj[\"yaw\"] = record_list[i][3];\n            j[\"action\"] = actionj;\n            outputFile << j.dump(4) << std::endl; // 每个 JSON 对象占一行\n        }\n\n        Object obj = cur_obj_;\n        json jendobj;\n        jendobj[\"type\"] = obj.type;\n        jendobj[\"color\"] = obj.color;\n        jendobj[\"size\"] = obj.size;\n        jendobj[\"shape\"] = obj.shape;\n        jendobj[\"feature\"] = obj.feature;\n        jendobj[\"position\"] = {obj.pos.x(), obj.pos.y(), obj.pos.z()};  // Convert Eigen::Vector3d to array\n        \n        json aim_landmark_j;\n        aim_landmark_j[\"aim_landmark\"] = jendobj;\n        outputFile << aim_landmark_j.dump(4) << std::endl;  // Pretty print with indent of 4\n        outputFile.close();\n    }\n\n\n    void recordPathbyTCP(std::vector<TrajRecodData> traj_list, bool add_takeoff_land){    \n        image_frame_ = 1;\n        initRecord(data_record_path_);\n        tcpserver_->sendString(\"path:\"+ pose_record_file_name_);\n        sleep(1.0);\n        std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> action_list;\n        std::vector<Eigen::Vector4d> record_list;\n        for(int i = 0 ; i < traj_list.size() - 1; i++){\n            double yaw_error = traj_list[i + 1].record_list[0][3] - traj_list[i].record_list.back()[3];\n            if(abs(yaw_error) < 0.01){continue;}\n            if(yaw_error > M_PI) yaw_error -=  2 * M_PI;\n            else if(yaw_error < -M_PI) yaw_error += 2 * M_PI;\n            int turn_nums = abs(yaw_error) / (M_PI/6);\n            double step_yaw = (M_PI) / 6;\n            double in_yaw = traj_list[i].record_list.back()[3];\n            Eigen::Vector3d cur_p(traj_list[i].record_list.back().head<3>());\n            if (yaw_error != 0) {\n                if (yaw_error > 0) {\n                    traj_list[i].action_list.back().first.first = \"turn left\";\n                    for(int j = 1 ; j < turn_nums; j ++){\n                        action_list.emplace_back(std::make_pair(std::make_pair(\"turn left\", 30), cur_p));\n                        record_list.emplace_back(Eigen::Vector4d(cur_p[0], cur_p[1], cur_p[2], in_yaw + j * step_yaw));\n                    }\n                } else {\n                    traj_list[i].action_list.back().first.first = \"turn right\";\n                    for(int j = 1 ; j < turn_nums; j ++){\n                        action_list.emplace_back(std::make_pair(std::make_pair(\"turn right\", 30), cur_p));\n                        record_list.emplace_back(Eigen::Vector4d(cur_p[0], cur_p[1], cur_p[2], in_yaw - j * step_yaw));\n                    }\n                }\n            }\n            traj_list[i + 1].action_list.insert(traj_list[i + 1].action_list.begin() , action_list.begin(), action_list.end());\n            traj_list[i + 1].record_list.insert(traj_list[i + 1].record_list.begin(), record_list.begin(), record_list.end());\n        }\n        if(add_takeoff_land){\n            addTakeoffLanding(traj_list);\n        }\n        for(int i = 0 ; i < traj_list.size(); i++){\n            std::vector<Eigen::Vector4d> record_list = traj_list[i].record_list;\n            if(record_list.size() < 1) continue;    \n            for(int j = 0 ; j < record_list.size(); j ++){\n                double yaw = record_list[j][3];\n                Eigen::Vector3d pos = record_list[j].head<3>();\n                pos = pos * (traj_scale_ratio_ /env_scale_ratio_);\n                yaw = yaw  / M_PI * 180;\n                if(abs(yaw) < 0.1) yaw = 0;\n                tcpserver_->sendCameraPose(pos[0], pos[1], pos[2], 0 ,yaw ,0);\n                sleep(1.0/send_freq_);\n                if(j == 0 && i == 0){\n                    if(env_.find(\"gs\") != std::string::npos){\n                        tcpserver_->sendCameraPose(pos[0], pos[1], pos[2], 0 , yaw ,0);\n                    }\n                    sleep(1);\n                }\n            }\n        }\n        saveTrajListToJson(traj_list, pose_record_file_name_ + \"pose.jsonl\");\n    }\n\n    double getMaxZinRange(const std::vector<Eigen::Vector3d>& filtered_points) {\n        double max_z = std::numeric_limits<double>::lowest();  \n        for (const auto& point : filtered_points) {\n            double z = point[2];\n            if (z > max_z) {\n                max_z = z;\n            }\n        }\n        return max_z;\n    }\n\n    double getMaxZinP(double input_x , double input_y, double range){\n        double min_x = input_x - range;\n        double max_x = input_x + range;\n        double min_y = input_y - range;\n        double max_y = input_y + range;\n        const std::vector<Eigen::Vector3d> point_cloud = eigen_pc_;\n        std::vector<Eigen::Vector3d> filtered_points = filterPointsinRange(point_cloud, min_x, max_x, min_y, max_y);\n        double max_z = std::numeric_limits<double>::lowest();  \n        for (const auto& point : filtered_points) {\n            double z = point[2];\n            if (z > max_z) {\n                max_z = z;\n            }\n        }\n        return max_z;\n    }\n\n    double calDis(const Eigen::Vector3d& p1, const Eigen::Vector3d& p2) {\n        return std::sqrt(std::pow(p2[0] - p1[0], 2) + std::pow(p2[1] - p1[1], 2) + std::pow(p2[2] - p1[2], 2));\n    }\n\n    void addTakeoffLanding(\n        std::vector<TrajRecodData>& traj_list,\n        double range_radius = 2.0, \n        double distance_interval = 3.0) \n    {\n        std::vector<Eigen::Vector3d> point_cloud = eigen_pc_;\n        Eigen::Vector4d start_point = traj_list.front().record_list.front();  \n        Eigen::Vector4d end_point = traj_list.back().record_list.back();   \n        \n        double min_x_takeoff = start_point[0] - range_radius;\n        double max_x_takeoff = start_point[0] + range_radius;\n        double min_y_takeoff = start_point[1] - range_radius;\n        double max_y_takeoff = start_point[1] + range_radius;\n\n        double min_x_landing = end_point[0] - range_radius;\n        double max_x_landing = end_point[0] + range_radius;\n        double min_y_landing = end_point[1] - range_radius;\n        double max_y_landing = end_point[1] + range_radius;\n\n        std::vector<Eigen::Vector3d> filtered_takeoff_points = filterPointsinRange(point_cloud, min_x_takeoff, max_x_takeoff, min_y_takeoff, max_y_takeoff);\n        double takeoff_height = getMaxZinRange(filtered_takeoff_points);  \n        std::vector<Eigen::Vector3d> filtered_landing_points = filterPointsinRange(point_cloud, min_x_landing, max_x_landing, min_y_landing, max_y_landing);\n        double landing_height = getMaxZinRange(filtered_landing_points);  \n\n        if(landing_height < map_elevation_){\n            landing_height = map_elevation_;\n        }\n        if(takeoff_height < map_elevation_){\n            takeoff_height = map_elevation_;\n        }\n        if(landing_height > end_point[2]){\n            landing_height = end_point[2];\n        }\n        if(takeoff_height > start_point[2]){\n            takeoff_height = start_point[2];\n        }\n\n        Eigen::Vector3d takeoff_start(start_point[0], start_point[1], takeoff_height);\n        Eigen::Vector3d takeoff_end(start_point[0], start_point[1], start_point[2]);\n        double distance_to_travel = calDis(takeoff_start, takeoff_end);\n        int num_points_to_insert_takeoff = static_cast<int>(std::floor(distance_to_travel / distance_interval));\n        \n        for (int i = 1; i <= num_points_to_insert_takeoff; ++i) {\n            double new_height = start_point[2] - i * distance_interval;\n            Eigen::Vector4d takeoff_point(start_point[0], start_point[1], new_height, start_point[3]);\n            traj_list.front().record_list.insert(traj_list.front().record_list.begin(), takeoff_point);  // 插入到起始位置\n            traj_list.front().action_list.insert(traj_list.front().action_list.begin(), {{\"up\", 3}, Eigen::Vector3d(takeoff_start[0], takeoff_start[1], new_height)});\n        }\n\n        Eigen::Vector3d landing_start(end_point[0], end_point[1], landing_height);\n        Eigen::Vector3d landing_end(end_point[0], end_point[1], end_point[2]);\n        distance_to_travel = calDis(landing_start, landing_end);\n        int num_points_to_insert_landing = static_cast<int>(std::floor(distance_to_travel / distance_interval));\n\n        traj_list.back().action_list.back().first.first = \"down\";\n        for (int i = 1; i <= num_points_to_insert_landing; ++i) {\n            double new_height = end_point[2] - i * distance_interval;\n            Eigen::Vector4d landing_point(end_point[0], end_point[1], new_height, end_point[3]);\n            traj_list.back().record_list.push_back(landing_point);  \n            traj_list.back().action_list.push_back({{\"down\", 3}, Eigen::Vector3d(landing_start[0], landing_start[1], new_height)});\n        }\n        traj_list.back().action_list.back().first.first = \"stop\";\n    }\n\n    void addTakeoffLanding(\n        std::vector<Eigen::Vector4d>& record_list,\n        std::vector<std::pair<std::pair<std::string, int>, Eigen::Vector3d>>& action_list,\n        double range_radius = 2.0, \n        double distance_interval = 3.0) {\n\n        std::vector<Eigen::Vector3d> point_cloud = eigen_pc_;\n        Eigen::Vector4d start_point = record_list.front();  \n        Eigen::Vector4d end_point = record_list.back();   \n        \n        double min_x_takeoff = start_point[0] - range_radius;\n        double max_x_takeoff = start_point[0] + range_radius;\n        double min_y_takeoff = start_point[1] - range_radius;\n        double max_y_takeoff = start_point[1] + range_radius;\n\n        double min_x_landing = end_point[0] - range_radius;\n        double max_x_landing = end_point[0] + range_radius;\n        double min_y_landing = end_point[1] - range_radius;\n        double max_y_landing = end_point[1] + range_radius;\n\n        std::vector<Eigen::Vector3d> filtered_takeoff_points = filterPointsinRange(point_cloud, min_x_takeoff, max_x_takeoff, min_y_takeoff, max_y_takeoff);\n        double takeoff_height = getMaxZinRange(filtered_takeoff_points);  \n        std::vector<Eigen::Vector3d> filtered_landing_points = filterPointsinRange(point_cloud, min_x_landing, max_x_landing, min_y_landing, max_y_landing);\n        double landing_height = getMaxZinRange(filtered_landing_points); \n\n        if(landing_height < map_elevation_){\n            landing_height = map_elevation_;\n        }\n        if(takeoff_height < map_elevation_){\n            takeoff_height = map_elevation_;\n        }\n        if(landing_height > end_point[2]){\n            landing_height = end_point[2];\n        }\n        if(takeoff_height > start_point[2]){\n            takeoff_height = start_point[2];\n        }\n\n        Eigen::Vector3d takeoff_start(start_point[0], start_point[1], takeoff_height);\n        Eigen::Vector3d takeoff_end(start_point[0], start_point[1], start_point[2]);\n\n        double distance_to_travel = calDis(takeoff_start, takeoff_end);\n        int num_points_to_insert_takeoff = static_cast<int>(std::floor(distance_to_travel / distance_interval));\n        \n        for (int i = 1; i <= num_points_to_insert_takeoff; ++i) {\n            double new_height = start_point[2] - i * distance_interval;\n            Eigen::Vector4d takeoff_point(start_point[0], start_point[1], new_height, start_point[3]);\n            record_list.insert(record_list.begin(), takeoff_point); \n            action_list.insert(action_list.begin(), {{\"up\", 3}, Eigen::Vector3d(takeoff_start[0], takeoff_start[1], new_height)});\n        }\n        Eigen::Vector3d landing_start(end_point[0], end_point[1], landing_height);\n        Eigen::Vector3d landing_end(end_point[0], end_point[1], end_point[2]);\n        distance_to_travel = calDis(landing_start, landing_end);\n        int num_points_to_insert_landing = static_cast<int>(std::floor(distance_to_travel / distance_interval));\n\n        action_list.back().first.first = \"down\";\n        for (int i = 1; i <= num_points_to_insert_landing; ++i) {\n            double new_height = end_point[2] - i * distance_interval;\n            Eigen::Vector4d takeoff_point(end_point[0], end_point[1], new_height, end_point[3]);\n            record_list.push_back(takeoff_point); \n            action_list.push_back({{\"down\", 3}, Eigen::Vector3d(landing_start[0], landing_start[1], new_height)});\n        }\n        action_list.back().first.first = \"stop\";\n    }\n\n\n    void recordPathbyTCP(bool is_takeoff_landing){\n        std::vector<Eigen::Vector4d> record_list;\n        std::vector<std::pair<std::pair<std::string,int>, Eigen::Vector3d>> action_list;\n        \n        record_list = pathsearch_->getrecordlist();\n        action_list = pathsearch_->getactionlist();\n\n        image_frame_ = 1;\n        if(record_list.size() < 1) return;\n        initRecord(data_record_path_);\n        tcpserver_->sendString(\"path:\"+ pose_record_file_name_);\n        sleep(0.2);\n        if(is_takeoff_landing){\n            addTakeoffLanding(record_list, action_list);\n        }\n        for(int i = 0 ; i < record_list.size(); i ++){\n            double yaw = record_list[i][3];\n            Eigen::Vector3d pos = record_list[i].head<3>();\n            pos = pos * (traj_scale_ratio_ /env_scale_ratio_);\n            yaw = yaw  / M_PI * 180;\n            tcpserver_->sendCameraPose(pos[0], pos[1], pos[2], 0 , yaw ,0);\n            sleep(1 / send_freq_);\n            if(i == 0){\n                sleep(0.5);\n                if(env_.find(\"gs\") != std::string::npos){\n                    tcpserver_->sendCameraPose(pos[0], pos[1], pos[2], 0 , yaw ,0);\n                }\n            }\n        }\n        saveActionsToJson(action_list, record_list,  pose_record_file_name_ + \"pose.jsonl\");\n    }\n\n    Object selectRandomObject(const std::vector<Object>& obj_list) {\n        if (obj_list.empty()) {\n            throw std::runtime_error(\"Object list is empty.\");\n        }\n        std::random_device rd;\n        std::mt19937 gen(rd());\n        std::uniform_int_distribution<> dis(0, obj_list.size() - 1);\n\n        int index = dis(gen);\n        return obj_list[index];\n    }\n\n    Object selectRandomObjectwithHeight(const std::vector<Object>& obj_list) {\n        int trynums = 3000;\n        if (obj_list.empty()) {\n            throw std::runtime_error(\"Object list is empty.\");\n        }\n        std::random_device rd;\n        std::mt19937 gen(rd());\n        std::uniform_int_distribution<> dis(0, obj_list.size() - 1);\n        int index = dis(gen);\n        for(int i = 0; i < trynums; i ++){\n            index = dis(gen);\n            if( obj_list[index].pos.z() > flyheight_ - objinfov_height_thr_){\n                return obj_list[index];\n            }\n        }\n        return obj_list[index];\n    }\n\n    double selectRandomFlyheight(double h , double H){\n        std::random_device rd;\n        std::mt19937 gen(rd());\n        std::uniform_real_distribution<> height_dist(h, H);\n        return height_dist(gen);\n    }\n\n    Eigen::Vector3d selectRandomPointInShell(const Eigen::Vector3d& center, double r, double R, double h, double H) {\n        std::random_device rd;\n        std::mt19937 gen(rd());\n        std::uniform_real_distribution<> radius_dist(r, R);\n        std::uniform_real_distribution<> height_dist(h, H);\n        std::uniform_real_distribution<> angle_dist(0, 2 * M_PI); \n\n        double radius = radius_dist(gen);\n        double height = height_dist(gen);\n        double angle = angle_dist(gen);\n\n        double x = center.x() + radius * cos(angle);\n        double y = center.y() + radius * sin(angle);\n        double z = height;\n\n        return Eigen::Vector3d(x, y, z);\n    }\n\n    Eigen::Vector3d selectRandomPointInShell(const Eigen::Vector3d& center, double r, double R) {\n        std::random_device rd;\n        std::mt19937 gen(rd());\n        std::uniform_real_distribution<> radius_dist(r, R);\n        std::uniform_real_distribution<> angle_dist(0, 2 * M_PI);\n\n        double radius = radius_dist(gen);\n        double angle = angle_dist(gen);\n\n        double x = center.x() + radius * cos(angle);\n        double y = center.y() + radius * sin(angle);\n        double z = flyheight_;\n\n        return Eigen::Vector3d(x, y, z);\n    }\n\n\n    Object selectRandomObjectInRange(const Eigen::Vector3d& center, double r, double R) {\n        std::random_device rd;\n        std::mt19937 gen(rd());\n        Object nullobj;\n        \n        std::uniform_int_distribution<> dist(0, obj_list_.size() - 1);\n        int trynum = 0;\n        Object closest_obj = nullobj;\n        double closest_distance = std::numeric_limits<double>::infinity();\n\n        while (true) {\n            const Object& obj = obj_list_[dist(gen)];\n            double distance = (obj.pos - center).norm();\n            if (distance >= r && distance <= R) {\n                return obj; \n            }\n            if (distance < closest_distance) {\n                closest_distance = distance;\n                closest_obj = obj;\n            }\n            \n            trynum += 1;\n            if (trynum >= 1000) {\n                return closest_obj; \n            }\n        }\n    }\n    \n    void publishPointsInRViz(const std::vector<Eigen::Vector3d>& points, const std::string& frame_id, const std::string& ns)\n        {\n        visualization_msgs::msg::MarkerArray marker_array;\n        std::vector<std::array<float, 4>> colors = {\n            {1.0, 0.0, 0.0, 1.0}, \n            {0.0, 1.0, 0.0, 1.0}, \n            {0.0, 0.0, 1.0, 1.0}  \n        };\n        for (size_t i = 0; i < points.size(); ++i) {\n            visualization_msgs::msg::Marker marker;\n            marker.header.frame_id = frame_id; \n            marker.header.stamp = node_->get_clock()->now();\n            marker.ns = ns;\n            marker.id = static_cast<int>(i);\n            marker.type = visualization_msgs::msg::Marker::SPHERE;\n            marker.action = visualization_msgs::msg::Marker::ADD;\n\n            marker.scale.x = 2.0; \n            marker.scale.y = 2.0; \n            marker.scale.z = 2.0; \n\n            marker.color.r = colors[i % colors.size()][0];\n            marker.color.g = colors[i % colors.size()][1];\n            marker.color.b = colors[i % colors.size()][2];\n            marker.color.a = colors[i % colors.size()][3]; \n\n            marker.pose.position.x = points[i].x();\n            marker.pose.position.y = points[i].y();\n            marker.pose.position.z = points[i].z();\n\n            marker_array.markers.push_back(marker);\n        }\n        show_point_Pub_->publish(marker_array);\n    }\n\n    Eigen::Vector3d findAimingPoint(const Eigen::Vector3d& start_p, const Eigen::Vector3d& end_p, double d) {\n        Eigen::Vector3d direction = (start_p - end_p).normalized();\n        Eigen::Vector3d current_point = end_p;\n        \n        while ((current_point - start_p).norm() > d) {\n            current_point += direction * d; \n            if (!bevQuery(current_point)) {\n                return current_point; \n            }\n        }\n        return start_p;\n    }\n\n\n    std::vector<geometry_msgs::msg::Pose> readPosesFromFile(const std::string& filename) {\n        std::ifstream file(filename);\n        std::vector<geometry_msgs::msg::Pose> poses;\n        std::string line;\n\n        while (std::getline(file, line)) {\n            nlohmann::json j = nlohmann::json::parse(line);\n            geometry_msgs::msg::Pose pose;\n            pose.position.x = j[\"position\"][\"x\"];\n            pose.position.y = j[\"position\"][\"y\"];\n            pose.position.z = j[\"position\"][\"z\"];\n            pose.orientation.w = j[\"orientation\"][\"w\"];\n            pose.orientation.x = j[\"orientation\"][\"x\"];\n            pose.orientation.y = j[\"orientation\"][\"y\"];\n            pose.orientation.z = j[\"orientation\"][\"z\"];\n\n            poses.push_back(pose);\n        }\n        std::cout << \"poses:\" << poses.size() <<std::endl;\n        return poses;\n    }\n\n    std::vector<Eigen::Vector3d> filterPointsinRange(const std::vector<Eigen::Vector3d>& point_cloud, double min_x, double max_x, double min_y, double max_y) {\n        std::vector<Eigen::Vector3d> filtered_points;\n        for (const auto& point : point_cloud) {\n            double x = point[0];\n            double y = point[1];\n            if (x >= min_x && x <= max_x && y >= min_y && y <= max_y) {\n                filtered_points.push_back(point);\n            }\n        }\n        return filtered_points;\n    }\n\n    bool isPShotP(Eigen::Vector3d start_p, Eigen::Vector3d end_p) {\n        if (start_p == end_p) {\n            std::cout << \"The start point and end point are the same.\" << std::endl;\n            return false;\n        }\n        Eigen::Vector3d direction = end_p - start_p;\n        double distance = direction.norm(); \n        Eigen::Vector3d step = direction.normalized() * global_voxelmap_.getScale(); \n        Eigen::Vector3d current_point = start_p;\n        while ((current_point - start_p).norm() < distance) {\n            if (global_voxelmap_.query(current_point)) {\n                return false;\n            }\n            current_point += step;\n        }\n        return true;\n\n    }\n\n    TrajRecodData genTrajRecordafterAstar(){\n        return TrajRecodData(pathsearch_->getrecordlist(), \n                            pathsearch_->getactionlist() ,\n                            cur_obj_);\n    }\n\n    bool isObjectInCloseList(const Object& obj, const std::vector<Object>& closeobj_list) {\n        auto it = std::find(closeobj_list.begin(), closeobj_list.end(), obj);\n        return it != closeobj_list.end();\n    }\n\n    void genTrajbyOneLandmark(int k, int r, int R, int h, int H, bool is_takeoff_landing, bool with_turn){\n        // Select a random flight height within the range [h, H]\n        flyheight_ = selectRandomFlyheight(map_height_thr_ + h, map_height_thr_ + H);\n        // Generate k trajectories\n        for(int i = 0; i < k; ) {\n            std::cout << \"Round: \" << i << std::endl;  // Print current round number\n            // Randomly select a target object and get its position\n            Object target = selectRandomObjectwithHeight(obj_list_);\n            cur_obj_ = target;  \n            Eigen::Vector3d target_pos, end_point, start_point;\n            target_pos = target.pos;  \n            // Generate multiple candidate start points around the target object\n            for(int j = 0; j < 20; j++) {\n                // Randomly select a start point within a shell around the target (radius r to R)\n                start_point = selectRandomPointInShell(target_pos, r, R);\n                // Get the maximum Z value (height) at the start point, adjust based on the flight height\n                double min_z = getMaxZinP(start_point[0], start_point[1], 2);\n                if (flyheight_ < min_z) {\n                    start_point.z() = min_z + 6;  // If flight height is below the minimum Z, adjust the start point height\n                }\n                // Update the target position's Z-coordinate to match the start point's height\n                target_pos.z() = start_point.z();\n                // Check if the start point is valid, continue if not\n                if (bevQuery(start_point) == true or global_voxelmap_.query(start_point) == true) {\n                    // std::cout << \"Invalid start point, finding new start point\"<< std::endl;\n                    continue;  // If the start point is invalid, skip and try again\n                }\n                // Calculate the aiming point (target point) based on the start and target positions\n                end_point = findAimingPoint(start_point, target_pos, 1.0);\n        \n                // If a turn is required, check if the path from start to end is valid\n                if(with_turn) {\n                    if(isPShotP(start_point, end_point)) {\n                        // std::cout << \"There are no obstacles between the start and end point\" << std::endl;\n                        continue;\n                    } \n                }\n                // Create a vector of points to represent the trajectory (target, end, and start points)\n                std::vector<Eigen::Vector3d> points;\n                points.push_back(target_pos);\n                points.push_back(end_point);\n                points.push_back(start_point);\n\n                // Publish the points to RViz for visualization\n                publishPointsInRViz(points, \"map\", \"points_namespace\");\n                // Perform a path search from start to end point using A* algorithm\n                astartSearchPath(start_point, end_point, true);\n                // Record the trajectory path (including takeoff and landing if specified)\n                recordPathbyTCP(is_takeoff_landing);\n                i++;  // Increment trajectory counter\n                break;  // Exit inner loop and try generating a new trajectory\n            }\n        }\n    }\n\n    void genTrajbyMultiLandmark(int k, int r, int R, int h, int H, int land_nums, bool with_turn, bool is_takeoff_landing){\n        // Declare containers to store trajectory records and close objects encountered\n        std::vector<TrajRecodData> traj_list;\n        std::vector<Object> closeobj_list;\n        double traj_yaw = 0;\n        flyheight_ = selectRandomFlyheight(map_height_thr_ + h, map_height_thr_ + H);\n         // Generate k trajectories\n        for(int i = 0 ; i < k ;){\n            closeobj_list.clear();\n            traj_list.clear();\n            std::cout << \"Round: \" << i << std::endl;\n             // Select a random object as the starting target\n            Object init_target = selectRandomObject(obj_list_);\n            cur_obj_ = init_target;\n            closeobj_list.push_back(cur_obj_);\n            Eigen::Vector3d init_target_pos, init_end_point, init_start_point;\n            init_target_pos = init_target.pos;\n            bool init_flag = false;\n            // std::cout <<\"Target name:\" << init_target.type << \"  Target Position: \" << init_target_pos.transpose() << std::endl;\n            // Try to find a valid start and end point for the trajectory\n            for(int j = 0 ; j < 20; j ++){\n                init_start_point = selectRandomPointInShell(init_target_pos, r, R);\n                double min_z = getMaxZinP(init_start_point[0], init_start_point[1], 2);\n                if (flyheight_ < min_z) {\n                    init_start_point.z() = min_z + 6;  // If flight height is below the minimum Z, adjust the start point height\n                }\n                init_target_pos.z() = init_start_point.z();\n                if(bevQuery(init_start_point) == true or global_voxelmap_.query(init_start_point) == true){\n                    // std::cout << \"Invalid start point, finding new start point\"<< std::endl;\n                    continue;\n                }\n                init_end_point = findAimingPoint(init_start_point, init_target_pos, 1.0);\n                if(with_turn){\n                    if(isPShotP(init_start_point, init_end_point)){\n                        // std::cout << \"There are no obstacles between the start and end point\" << std::endl;\n                        continue;\n                    } \n                }\n                // std::cout << \"init start point: \" << init_start_point.transpose() <<  \"init end point: \" << init_end_point.transpose() << std::endl;\n                astartSearchPath(init_start_point, init_end_point, true);\n\n                traj_yaw = calculateYawDegree(init_end_point - init_start_point);\n                TrajRecodData tmp_traj = genTrajRecordafterAstar();\n                if(tmp_traj.que_size != 0){         \n                    init_flag = true;\n                    traj_list.push_back(tmp_traj);\n                }\n                break;\n            }\n\n            if(!init_flag) continue;\n            Eigen::Vector3d target_pos, end_point, start_point;\n            start_point = traj_list[0].record_list.back().head<3>();\n            for(int land_i = 0 ; land_i < land_nums -1 ; land_i ++){\n                for(int j = 0 ; j < 20 ; j ++){\n                    cur_obj_ = selectRandomObjectInRange(start_point, r, R);\n                    if(isObjectInCloseList(cur_obj_, closeobj_list )){\n                        // std::cout << \"Object already in close list, finding new object\" << std::endl;\n                        continue;\n                    } \n                    closeobj_list.push_back(cur_obj_);\n                    target_pos = cur_obj_.pos;\n                    target_pos.z() = start_point.z();\n                    end_point = findAimingPoint(start_point, target_pos, 1.0);\n                    if(with_turn){\n                        if(isPShotP(start_point, end_point)){\n                            // std::cout << \"There are no obstacles between the start and end point\" << std::endl;\n                            continue;\n                        } \n                    }\n                    // if(!isPShotP(start_point, end_point)) continue;\n                    double new_yaw  = calculateYawDegree(end_point - start_point );\n                    if(abs(new_yaw - traj_yaw) < 30 || abs(new_yaw - traj_yaw) > 150) {\n                        // std::cout << \"Yaw error too small, finding new object\" << std::endl;\n                        continue;\n                    }\n                    // std::cout << \" start point: \" << start_point.transpose() <<  \" end point: \" << end_point.transpose() << std::endl;\n                    if( land_i == land_nums -2){\n                        astartSearchPath(start_point, end_point, true);\n                    }\n                    astartSearchPath(start_point, end_point, true);\n                    TrajRecodData tmp_traj = genTrajRecordafterAstar();\n                    if(tmp_traj.que_size != 0){\n                        traj_list.push_back(tmp_traj);\n                    }\n                    start_point = end_point;\n                    break;\n                }\n            }\n            if(traj_list.size() != land_nums) continue;\n            recordPathbyTCP(traj_list, is_takeoff_landing);\n            i++;\n        }\n    }\n\n};\n\n\n"
  },
  {
    "path": "tool_ws/src/traj_gen/src/traj_gen_node.cc",
    "content": "#include <rclcpp/rclcpp.hpp>\n#include <nav_msgs/msg/path.hpp>\n#include <nav_msgs/msg/odometry.hpp>\n#include <geometry_msgs/msg/pose_stamped.hpp>\n#include <geometry_msgs/msg/point_stamped.hpp>\n#include <geometry_msgs/msg/pose_with_covariance_stamped.hpp>\n#include <ament_index_cpp/get_package_share_directory.hpp>\n#include <future>\n\n#include \"traj_gen.cc\"\n// #include \"uavs_net/sendcmd.h\"\n\nusing namespace std;\n\nbool doFlag = false;\nstd::string gptcmd_;\nEigen::Vector3d endpoint_ = {0, 0, 0};\nbool mission_flag_ = false;\nstd::vector<Eigen::Vector3d> end_list_;\nstd::vector<Eigen::Vector3d> mutil_end_list_;\nbool mutil_pose_flag_ = false;\nbool mutil_flag_ = false;\n\nstruct PlannerConfig {\n    std::string name;\n    int nums;\n    double min_dis;\n    double max_dis;\n    double height_min;\n    double height_max;\n    int aim_port;\n    int listen_port;\n    std::string sim_ip;\n    int aimlandmark_nums;\n    bool add_takeoff_land;\n    bool with_turn;\n\n};\n\nstd::vector<PlannerConfig> loadthreadconfig(const std::string& config_file){\n\n    YAML::Node config = YAML::LoadFile(config_file);\n    std::vector<PlannerConfig> configs;\n    if (config[\"thread_params\"]) {\n        for (const auto& param : config[\"thread_params\"]) {\n            PlannerConfig planner_config;\n            planner_config.name = param[\"name\"].as<std::string>();\n            planner_config.nums = param[\"nums\"].as<int>();\n            planner_config.min_dis = param[\"min_dis\"].as<double>();\n            planner_config.max_dis = param[\"max_dis\"].as<double>();\n            planner_config.height_min = param[\"height_min\"].as<double>();\n            planner_config.height_max = param[\"height_max\"].as<double>();\n            planner_config.sim_ip = param[\"sim_ip\"].as<std::string>();\n            planner_config.aim_port = param[\"aim_port\"].as<double>();\n            planner_config.listen_port = param[\"listen_port\"].as<double>();\n            planner_config.aimlandmark_nums = param[\"aimlandmark_nums\"].as<int>();\n            planner_config.add_takeoff_land = param[\"add_takeoff_land\"].as<bool>();\n            planner_config.with_turn = param[\"with_turn\"].as<bool>();\n            configs.push_back(planner_config);\n        }\n    }\n    return configs;\n}\n\n\nvoid runTrajgenThread(const PlannerConfig& config, std::shared_ptr<rclcpp::Node> node, std::string env_name) {\n    TrajGen trajgen(node, config.aim_port, config.listen_port, config.sim_ip, config.name, env_name);\n    trajgen.trajDataGen(config.nums, config.min_dis, config.max_dis, config.height_min, config.height_max, config.aimlandmark_nums, config.add_takeoff_land, config.with_turn);\n}\n\nstd::string getProjectDirectory() {\n    std::filesystem::path currentPath(__FILE__);\n\n    std::filesystem::path rosDir = currentPath.parent_path().parent_path().parent_path().parent_path().parent_path();\n    \n    return rosDir.string(); \n}\n\n\n\nint main(int argc, char** argv) {\n    rclcpp::init(argc, argv);  // 初始化 ROS 2\n    \n\n    std::string config_path_ = getProjectDirectory() + \"/configs/\";\n\n    std::string env_name = \"env_airsim_16\";\n    if(const char* env = std::getenv(\"ENV\")) {\n        env_name = (string)env;\n        std::cout << \"\\033[32m\" << \"Your ENV is: \" << env_name << \"\\033[0m\" << std::endl;;\n    }\n    else{\n        std::cout << \"\\033[33m\" << \"Use default environment:\" << env_name << \"\\033[0m\" << std::endl;;\n    }\n\n    std::string config_path = config_path_  + env_name + \".yaml\";\n\n    std::vector<PlannerConfig> configs = loadthreadconfig(config_path);\n    std::vector<std::shared_ptr<rclcpp::Node>> nodes;\n    for (size_t i = 0; i < configs.size(); ++i) {\n        nodes.push_back(std::make_shared<rclcpp::Node>(configs[i].name));\n    }\n    std::vector<std::thread> threads;\n    for (size_t i = 0; i < configs.size(); ++i) {\n        threads.emplace_back(runTrajgenThread, configs[i], nodes[i], env_name);\n    }\n    for (auto& t : threads) {\n        t.join();\n    }\n    rclcpp::shutdown();\n    return 0;\n\n}"
  },
  {
    "path": "train/common.py",
    "content": "import math\nimport numpy as np\nfrom math import atan2, asin, degrees, cos, sin, sqrt\n\ndef euler_to_quaternion(roll, pitch, yaw):\n    \"\"\"\n    Convert Euler angles (roll, pitch, yaw) to quaternion.\n    The order of Euler angles is yaw-pitch-roll (Z-Y-X axis rotation order).\n    \"\"\"\n    cy = math.cos(yaw * 0.5)\n    sy = math.sin(yaw * 0.5)\n    cp = math.cos(pitch * 0.5)\n    sp = math.sin(pitch * 0.5)\n    cr = math.cos(roll * 0.5)\n    sr = math.sin(roll * 0.5)\n\n    w = cy * cp * cr + sy * sp * sr\n    x = cy * cp * sr - sy * sp * cr\n    y = sy * cp * cr + cy * sp * sr\n    z = sy * cp * sr - cy * sp * cr\n    \n    return [x, y, z, w]\n\ndef quaternion_to_rotation_matrix(QW, QX, QY, QZ):\n    \"\"\"\n    Convert a quaternion to a rotation matrix.\n    \"\"\"\n    R = np.array([\n        [1 - 2*(QY**2 + QZ**2), 2*(QX*QY - QZ*QW), 2*(QX*QZ + QY*QW)],\n        [2*(QX*QY + QZ*QW), 1 - 2*(QX**2 + QZ**2), 2*(QY*QZ - QX*QW)],\n        [2*(QX*QZ - QY*QW), 2*(QY*QZ + QX*QW), 1 - 2*(QX**2 + QY**2)]\n    ])\n    return R\n\ndef calculate_camera_position(QW, QX, QY, QZ, TX, TY, TZ):\n    \"\"\"\n    Calculate the camera position in the world coordinate system.\n    \"\"\"\n    R = quaternion_to_rotation_matrix(QW, QX, QY, QZ)\n    R_t = R.T\n    T = np.array([TX, TY, TZ])\n    camera_position_world = -R_t.dot(T)\n    return camera_position_world\n\ndef rotation_matrix_to_euler_angles(R):\n    \"\"\"\n    Convert rotation matrix to Euler angles (roll, pitch, yaw).\n    \"\"\"\n    pitch = asin(-R[2, 0])\n    if abs(R[2, 0]) != 1:\n        roll = atan2(R[2, 1], R[2, 2])\n        yaw = atan2(R[1, 0], R[0, 0])\n    else:\n        roll = atan2(-R[1, 2], R[1, 1])\n        yaw = 0\n    \n    roll = degrees(roll)\n    pitch = degrees(pitch)\n    yaw = degrees(yaw)\n    \n    return roll, pitch, yaw\n\ndef cam2world(QW, QX, QY, QZ, TX, TY, TZ):\n    camera_position = calculate_camera_position(QW, QX, QY, QZ, TX, TY, TZ)\n    formatted_position = [f\"{x:.8f}\" for x in camera_position]\n    print(f\"Camera position in world coordinates: {', '.join(formatted_position)}\")\n\n    R = quaternion_to_rotation_matrix(QW, QX, QY, QZ)\n    roll, pitch, yaw = rotation_matrix_to_euler_angles(R)\n    print(f\"Camera's orientation (Roll, Pitch, Yaw): {roll}, {pitch}, {yaw}\")\n    rpy = [roll, pitch, yaw]\n\n    return camera_position, rpy\n\ndef euler_to_rotation_matrix(roll, pitch, yaw):\n    \"\"\"\n    Convert Euler angles (roll, pitch, yaw) to rotation matrix.\n    \"\"\"\n    roll = np.radians(roll)\n    pitch = np.radians(pitch)\n    yaw = np.radians(yaw)\n    \n    R_x = np.array([\n        [1, 0, 0],\n        [0, cos(roll), -sin(roll)],\n        [0, sin(roll), cos(roll)]\n    ])\n    \n    R_y = np.array([\n        [cos(pitch), 0, sin(pitch)],\n        [0, 1, 0],\n        [-sin(pitch), 0, cos(pitch)]\n    ])\n    \n    R_z = np.array([\n        [cos(yaw), -sin(yaw), 0],\n        [sin(yaw), cos(yaw), 0],\n        [0, 0, 1]\n    ])\n    \n    R = np.dot(R_z, np.dot(R_y, R_x))\n    return R\n\ndef rotation_matrix_to_quaternion(R):\n    \"\"\"\n    Convert rotation matrix to quaternion (QW, QX, QY, QZ).\n    \"\"\"\n    trace = R[0, 0] + R[1, 1] + R[2, 2]\n    \n    if trace > 0:\n        S = sqrt(trace + 1.0) * 2\n        QW = 0.25 * S\n        QX = (R[2, 1] - R[1, 2]) / S\n        QY = (R[0, 2] - R[2, 0]) / S\n        QZ = (R[1, 0] - R[0, 1]) / S\n    else:\n        i = np.argmax([R[0, 0], R[1, 1], R[2, 2]])\n        if i == 0:\n            S = sqrt(1.0 + R[0, 0] - R[1, 1] - R[2, 2]) * 2\n            QW = (R[2, 1] - R[1, 2]) / S\n            QX = 0.25 * S\n            QY = (R[0, 1] + R[1, 0]) / S\n            QZ = (R[0, 2] + R[2, 0]) / S\n        elif i == 1:\n            S = sqrt(1.0 + R[1, 1] - R[0, 0] - R[2, 2]) * 2\n            QW = (R[0, 2] - R[2, 0]) / S\n            QX = (R[0, 1] + R[1, 0]) / S\n            QY = 0.25 * S\n            QZ = (R[1, 2] + R[2, 1]) / S\n        else:\n            S = sqrt(1.0 + R[2, 2] - R[0, 0] - R[1, 1]) * 2\n            QW = (R[1, 0] - R[0, 1]) / S\n            QX = (R[0, 2] + R[2, 0]) / S\n            QY = (R[1, 2] + R[2, 1]) / S\n            QZ = 0.25 * S\n    \n    return QW, QX, QY, QZ\n\ndef calculate_camera_position_from_world(R_t, camera_position):\n    \"\"\"\n    Given camera_position_world and the transpose of the rotation matrix R_t,\n    calculate the translation vector T.\n    \"\"\"\n    T = -R_t.dot(camera_position)\n    return T\n\ndef world2cam(x, y, z, roll, pitch, yaw):\n    R = euler_to_rotation_matrix(roll, pitch, yaw)\n    QW, QX, QY, QZ = rotation_matrix_to_quaternion(R)\n    print(f\"Quaternion (QW, QX, QY, QZ): {QW}, {QX}, {QY}, {QZ}\")\n\n    T = calculate_camera_position_from_world(R, np.array([x, y, z]))\n    formatted_T = [f\"{t:.8f}\" for t in T]\n    print(f\"Camera position in camera coordinates (TX, TY, TZ): {', '.join(formatted_T)}\")\n\n    return QW, QX, QY, QZ, T[0], T[1], T[2]\n\ndef world2cam_WXYZ(x, y, z, QW, QX, QY, QZ):\n    \"\"\"\n    Converts world coordinates and quaternion to camera coordinates (position and orientation).\n    \"\"\"\n    R = quaternion_to_rotation_matrix(QW, QX, QY, QZ)\n    position_world = np.array([x, y, z])\n    camera_position = calculate_camera_position_from_world(R, position_world)\n    formatted_camera_position = [f\"{t:.8f}\" for t in camera_position]\n    print(f\"Camera position in camera coordinates (TX, TY, TZ): {', '.join(formatted_camera_position)}\")\n\n    return camera_position"
  },
  {
    "path": "train/dataset_builder/vln/__init__.py",
    "content": "\n"
  },
  {
    "path": "train/dataset_builder/vln/vln_dataset_builder.py",
    "content": "\"\"\"bridge_dataset dataset.\"\"\"\n\nimport tensorflow_datasets as tfds\nimport tensorflow as tf\nimport numpy as np\nfrom pathlib import Path\nimport glob\nimport os\nfrom PIL import Image\nimport numpy as np\nimport json\nimport random\n\n\n# 记录起始帧、拐点帧以及当前帧\n\nclass Builder(tfds.core.GeneratorBasedBuilder):\n    \"\"\"DatasetBuilder for trajectory dataset.\"\"\"\n\n    VERSION = tfds.core.Version('1.0.0')\n    RELEASE_NOTES = {\n      '1.0.0': 'Initial release.',\n    }\n    \n    def _info(self) -> tfds.core.DatasetInfo:\n        \"\"\"Returns the dataset metadata.\"\"\"\n        return tfds.core.DatasetInfo(\n            builder=self,\n            description=\"Dataset of trajectories where each step has the following fields: steps, episode_metadata.\",\n            features=tfds.features.FeaturesDict({\n                'steps': tfds.features.Dataset({\n                    'action': tfds.features.Tensor(shape=(8,), dtype=tf.float32),\n                    'history': tfds.features.Text(),\n                    'is_terminal': tfds.features.Scalar(dtype=tf.bool),\n                    'is_last': tfds.features.Scalar(dtype=tf.bool),\n                    'language_instruction': tfds.features.Text(),\n                    'observation': {\n                        'image_1': tfds.features.Image(shape=(224, 224, 3), encoding_format='png'),\n                        'image_2': tfds.features.Image(shape=(224, 224, 3), encoding_format='png'),\n                        'image_3': tfds.features.Image(shape=(224, 224, 3), encoding_format='png'),\n                    },\n                    'is_first': tfds.features.Scalar(dtype=tf.bool),\n                    'discount': tfds.features.Scalar(dtype=tf.float32),\n                    'reward': tfds.features.Scalar(dtype=tf.float32),\n                }),\n                'episode_metadata': {\n                    'has_image_2': tfds.features.Scalar(dtype=tf.bool),\n                    'has_image_3': tfds.features.Scalar(dtype=tf.bool),\n                    'file_path': tfds.features.Text(),\n                    'has_language': tfds.features.Scalar(dtype=tf.bool),\n                    'has_image_1': tfds.features.Scalar(dtype=tf.bool),\n                    'has_image_0': tfds.features.Scalar(dtype=tf.bool),\n                    'episode_id': tfds.features.Scalar(dtype=tf.int32),\n                },\n            }),\n            supervised_keys=None,\n            homepage='https://dataset-homepage/',\n            citation=r\"\"\"@misc{vln_2024, title={VLN Dataset}, year={2024}}\"\"\",\n        )\n\n    def _split_generators(self, dl_manager: tfds.download.DownloadManager):\n        \"\"\"Returns SplitGenerators.\"\"\"\n        # path = dl_manager.download_and_extract('https://todo-data-url')\n        json_path = \"YOUR_JSONDATA_PATH\"\n        return {\n            'train': self._generate_examples(json_path),\n        }\n\n    def _generate_examples(self, json_path):\n        \"\"\"Yields examples.\"\"\"\n        f = open(json_path, 'r')\n        js = json.load(f)['episodes']\n        data_len = len(js)  \n                \n        action_dict = {\n            \"0\":np.array([1,0,0,0,0,0,0,0]).astype(np.float32), # stop\n            \"1\":np.array([0,3,0,0,0,0,0,0]).astype(np.float32), # move forward\n            \"2\":np.array([0,0,15,0,0,0,0,0]).astype(np.float32), #turn left\n            \"3\":np.array([0,0,0,15,0,0,0,0]).astype(np.float32), # turn right\n            \"4\":np.array([0,0,0,0,2,0,0,0]).astype(np.float32), # go up\n            \"5\":np.array([0,0,0,0,0,2,0,0]).astype(np.float32), # go down\n            \"6\":np.array([0,0,0,0,0,0,5,0]).astype(np.float32), # move left\n            \"7\":np.array([0,0,0,0,0,0,0,5]).astype(np.float32), # move right\n            \"8\":np.array([0,6,0,0,0,0,0,0]).astype(np.float32), # move forward \n            \"9\":np.array([0,9,0,0,0,0,0,0]).astype(np.float32), # move forward \n        }\n                             \n        \n        def history_recorder(action_list): \n            if not action_list:\n                return \"\"\n            # Create a summary of actions with reduced consecutive duplicates\n            summary = [action_list[0]]\n            for action in action_list[1:]:\n                if action != summary[-1]:\n                    summary.append(action)\n\n            # Join the actions into a history string\n            history = ' then '.join(summary)\n            return history\n        \n        exp_name = \"vln_norm\"\n        for episode_id in range(data_len // 8, data_len // 8 * 2):\n            img_path = \"YOUR_IMAGE_PATH\"\n            if not os.path.exists(f\"{img_path}/{episode_id}\"):\n                continue\n            historys = [\" \"]\n            data_dict = js[episode_id]\n            if len(data_dict[\"actions\"]) > 400:\n                continue\n            actions = [action_dict[str(x)] for x in data_dict[\"actions\"][:-2]] + [action_dict[\"0\"]] * 2\n            instruction = data_dict['instruction']['instruction_text']\n\n            image_array = []\n            try:\n                for idx in range(len(data_dict[\"actions\"])-2):\n                    image_array.append(np.array(Image.open(img_path + \"/\" + str(episode_id) + \"/\" + str(idx) + \".png\"), dtype=np.uint8))\n\n                image_array.append(np.array(Image.open(img_path + \"/\" + str(episode_id) + \"/\" + str(len(data_dict[\"actions\"])-2) + \".png\"), dtype=np.uint8))\n                image_array.append(np.array(Image.open(img_path + \"/\" + str(episode_id) + \"/\" + str(len(data_dict[\"actions\"])-2) + \".png\"), dtype=np.uint8))\n            except:\n                continue\n            \n            total_steps = len(actions)\n            steps = []\n            \n            actions_mapped = [action_map[str(item)] for item in data_dict[\"actions\"][:-2]]\n            for index in range(1, total_steps):\n                historys.append(history_recorder(actions_mapped[:index]))\n            historys.append(historys[-1])\n            historys.append(historys[-1])            \n            \n            for idx in range(total_steps):\n                image_1 = image_array[idx]  # 当前帧\n                image_4 = image_array[0]\n#                 if idx < 4:\n#                     image_3 = image_4 = image_5 = image_2 = image_array[idx-2]\n#                 elif idx < 6:\n#                     image_2 = image_array[idx-2]\n#                     image_3 = image_4 = image_5 = image_array[idx-4]\n#                 elif idx < 8:\n#                     image_2 = image_array[idx-2]\n#                     image_3 = image_array[idx-4]\n#                     image_4 = image_5 = image_array[idx-6]\n#                 else:\n#                     image_2 = image_array[idx-2]\n#                     image_3 = image_array[idx-4]\n#                     image_4 = image_array[idx-6]\n#                     image_5 = image_array[idx-8]\n                keypoint = 0\n                try:\n                    keypoint = next(i for i in range(1, len(data_dict[\"actions\"])) if data_dict[\"actions\"][i] != data_dict[\"actions\"][i-1])\n                except:\n                    keypoint = 0\n    \n                if idx == 0:\n                    image_2 = image_3 = image_array[0]\n                elif idx == 1: \n                    image_2 = image_array[-1]\n                    image_3 = image_array[-2]\n                    # image_4 = image_array[-2]\n                elif keypoint == idx - 1:\n                    image_2 = image_array[keypoint]\n                    image_3 = image_array[idx-2]\n                elif keypoint != 0:\n                    image_2 = image_array[idx-1]\n                    image_3 = image_array[keypoint]\n                else:\n                    image_2 = image_array[-2]\n                    image_3 = image_array[-3]\n\n                steps.append(\n                    {\n                      'action': actions[idx],\n                      'history': historys[idx],\n                      'is_terminal': False if idx < total_steps - 2 else True,\n                      'is_last': False if idx < total_steps - 2 else True,\n                      'language_instruction': instruction,\n                      'observation': {\n                          'image_1': image_1,\n                          'image_2': image_2,\n                          'image_3': image_3,\n                      },\n                      'is_first': True if idx == 0 else False,\n                      'discount': 1.0,\n                      'reward': 0.0,\n                    }\n                )\n            \n            yield f\"{exp_name}/{episode_id}\" , {\n              'steps': steps,\n              'episode_metadata': {\n                  'has_image_2': True,\n                  'has_image_3': True,\n                  'file_path': img_path,\n                  'has_language': True,\n                  'has_image_1': True,\n                  'has_image_0': False,\n                  'episode_id': episode_id,\n              },\n          }\n\n"
  },
  {
    "path": "train/dataset_builder/vln/vln_dataset_builder_test.py",
    "content": "\"\"\"vln dataset.\"\"\"\n\nfrom . import vln_dataset_builder\nimport tensorflow_datasets as tfds\n\nclass VlnTest(tfds.testing.DatasetBuilderTestCase):\n  \"\"\"Tests for vln dataset.\"\"\"\n  # TODO(vln):\n  DATASET_CLASS = vln_dataset_builder.Builder\n  SPLITS = {\n      'train': 3,  # Number of fake train example\n      'test': 1,  # Number of fake test example\n  }\n\n  # If you are calling `download/download_and_extract` with a dict, like:\n  #   dl_manager.download({'some_key': 'http://a.org/out.txt', ...})\n  # then the tests needs to provide the fake output paths relative to the\n  # fake data directory\n  # DL_EXTRACT_RESULT = {'some_key': 'output_file1.txt', ...}\n\n\nif __name__ == '__main__':\n  tfds.testing.test_main()\n"
  },
  {
    "path": "train/datasets/__init__.py",
    "content": "from .dataset import RLDSBatchTransform, RLDSDataset, make_interleaved_dataset, get_oxe_dataset_kwargs_and_weights, make_dataset_from_rlds\nfrom .data_collector import get_vla_dataset_and_collator\nfrom .data_utils import *\n"
  },
  {
    "path": "train/datasets/data_collector.py",
    "content": "from pathlib import Path\nfrom typing import Any, Dict, Tuple, Type, Union, List, Optional, Callable\nfrom torch.utils.data import Dataset\nfrom transformers import PreTrainedTokenizerBase\nimport dlimp as dl\nfrom functools import partial\nfrom model import DinoSigLIPImageTransform \nfrom datasets.data_utils import PaddedCollatorForActionPrediction\nfrom model import ActionTokenizer\nfrom datasets.dataset import RLDSBatchTransform, RLDSDataset\nimport tensorflow as tf\n\nfrom datasets.data_utils import decode_and_resize, augment, chunk_act_obs, add_pad_mask_dict, subsample\n\ndef apply_trajectory_transforms(\n    dataset: dl.DLataset,\n    *,\n    train: bool,\n    window_size: int = 1,\n    future_action_window_size: int = 0,\n    subsample_length: Optional[int] = None,\n    skip_unlabeled: bool = False,\n    max_action: Optional[float] = None,\n    max_proprio: Optional[float] = None,\n    num_parallel_calls: int = tf.data.AUTOTUNE,\n) -> dl.DLataset:\n    \"\"\"\n    Transforms in this function should have the following properties:\n        - They require access to an entire trajectory (i.e., they cannot be applied frame-wise).\n        - They are generally not CPU-intensive, mostly involving moving and copying data.\n        - They do not require decoded images.\n\n    Args:\n        dataset (dl.DLataset): The dataset to transform.\n        train (bool): Whether the dataset is for training (affects subsampling).\n        window_size (int, optional): The length of the snippets that trajectories are chunked into.\n        future_action_window_size (int, optional): The number of future actions beyond window_size to include\n            in the chunked actions.\n        subsample_length (int, optional): If provided, trajectories longer than this will be subsampled to\n            this length\n        skip_unlabeled (bool, optional): Whether to skip trajectories with no language labels.\n        max_action: (float, optional): If provided, trajectories in which *any* action dimension\n            of *any* transition has an absolute value larger than this will be skipped.\n        max_proprio: (float, optional): If provided, trajectories in which *any* proprio dimension\n            of *any* transition has an absolute value larger than this will be skipped.\n        num_parallel_calls (int, optional): number of parallel calls for map operations. Default to AUTOTUNE.\n    \"\"\"\n    if skip_unlabeled:\n        if \"language_instruction\" not in dataset.element_spec[\"task\"]:\n            raise ValueError(\"skip_unlabeled=True but dataset does not have language labels.\")\n\n        dataset = dataset.filter(lambda x: tf.math.reduce_any(x[\"task\"][\"language_instruction\"] != \"\"))\n\n    if max_action is not None:\n        dataset = dataset.filter(lambda x: tf.math.reduce_all(tf.math.abs(x[\"action\"]) <= max_action))\n\n    if max_proprio is not None and \"proprio\" in dataset.element_spec[\"observation\"]:\n        dataset = dataset.filter(lambda x: tf.math.reduce_all(tf.math.abs(x[\"observation\"][\"proprio\"]) <= max_proprio))     \n\n    # marks which entires of the observation and task dicts are padding\n    dataset = dataset.traj_map(add_pad_mask_dict, num_parallel_calls)\n\n    # chunks observations and actions, giving them a new axis at index 1 of size `window_size` and\n    # `window_size + future_action_window_size`, respectively\n    dataset = dataset.traj_map(\n        partial(\n            chunk_act_obs,\n            window_size=window_size,\n            future_action_window_size=future_action_window_size,\n        ),\n        num_parallel_calls,\n    )\n    \n\n    if train and subsample_length is not None:\n        dataset = dataset.traj_map(\n            partial(subsample, subsample_length=subsample_length),\n            num_parallel_calls,\n        )\n\n\n    return dataset\n\n\ndef apply_per_dataset_frame_transforms(\n    dataset: dl.DLataset,\n    chunk_filter_fn: Optional[Callable] = None,\n):\n    \"\"\"\n    Optionally applied *per-dataset* transforms that happen at a frame level.\n\n    Args:\n        chunk_filter_fn (callable, optional): Filter function for chunks.\n    \"\"\"\n    if chunk_filter_fn:\n        dataset = dataset.filter(chunk_filter_fn)\n    return dataset\n\n\ndef apply_frame_transforms(\n    dataset: dl.DLataset,\n    *,\n    train: bool,\n    image_augment_kwargs: Union[Dict, Dict[str, Dict]] = {},\n    resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]] = {},\n    depth_resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]] = {},\n    num_parallel_calls: int = tf.data.AUTOTUNE,\n) -> dl.DLataset:\n    \"\"\"\n    Applies common transforms that happen at a frame level. These transforms are usually more CPU-intensive, (e.g.,\n    decoding or resizing images).\n\n    Args:\n        train (bool): Whether the dataset is for training (affects image augmentation).\n        dataset (dl.DLataset): The dataset to transform.\n        image_augment_kwargs (dict|Mapping[str, dict]): Keyword arguments to pass to the image augmentation\n            function. See `dlimp.transforms.augment_image` for documentation of these kwargs. If a dict of\n            dicts is provided, then key \"k\" will be used for \"image_{k}\" (names determined by `image_obs_keys`\n            in `make_dataset_from_rlds`). Augmentation will be skipped for missing keys (so pass an empty dict\n            to skip augmentation for all images).\n        resize_size (Tuple[int, int]|Mapping[str, Tuple[int, int]]): If provided, images will be resized to\n            this size. If a dict of tuples is provided, then key \"k\" will be used for \"image_{k}\" (names\n            determined by `image_obs_keys` in `make_dataset_from_rlds`). Resizing will be skipped for missing\n            keys (so pass an empty dict to skip resizing for all images).\n        depth_resize_size (Tuple[int, int]|Mapping[str, Tuple[int, int]]): Same as resize_size, but for depth\n            images.\n        num_parallel_calls (int): number of parallel calls for frame_map operations. Default to AUTOTUNE.\n    \"\"\"\n\n    # Convenience wrapper that takes a function that operates on a non-chunked \"observation\" dict and applies\n    # it to the chunked \"observation\" dict as well as the non-chunked \"task\" dict\n    def apply_obs_transform(fn: Callable[[Dict], Dict], frame: Dict) -> Dict:\n        frame[\"task\"] = fn(frame[\"task\"])\n        frame[\"observation\"] = dl.vmap(fn)(frame[\"observation\"])\n        return frame\n\n    # Decode + resize images (and depth images)\n    dataset = dataset.frame_map(\n        partial(\n            apply_obs_transform,\n            partial(decode_and_resize, resize_size=resize_size, depth_resize_size=depth_resize_size),\n        ),\n        num_parallel_calls,\n    )\n\n    if train:\n        # Augment all images with the same seed, skipping padding images\n        def aug(frame: dict):\n            seed = tf.random.uniform([2], maxval=tf.dtypes.int32.max, dtype=tf.int32)\n            aug_fn = partial(augment, seed=seed, augment_kwargs=image_augment_kwargs)\n            return apply_obs_transform(aug_fn, frame)\n\n        dataset = dataset.frame_map(aug, num_parallel_calls)\n\n    return dataset\n\ndef get_vla_dataset_and_collator(\n    data_root_dir: Path,\n    data_mix: str,\n    image_transform: DinoSigLIPImageTransform,\n    tokenizer: PreTrainedTokenizerBase,\n    default_image_resolution: Tuple[int, int, int],\n    padding_side: str = \"right\",\n    predict_stop_token: bool = True,\n    shuffle_buffer_size: int = 100_000,\n    train: bool = True,\n    episodic: bool = False,\n    image_aug: bool = False,\n) -> Tuple[Dataset, ActionTokenizer, PaddedCollatorForActionPrediction]:\n    \"\"\"Initialize RLDS Dataset (wraps TFDS), ActionTokenizer, and initialize transform/collation functions.\"\"\"\n    action_tokenizer = ActionTokenizer(tokenizer)\n    batch_transform = RLDSBatchTransform(\n        action_tokenizer, tokenizer, image_transform, predict_stop_token=predict_stop_token\n    )\n    collator = PaddedCollatorForActionPrediction(\n        tokenizer.model_max_length, tokenizer.pad_token_id, padding_side=padding_side\n    )\n\n    # Build RLDS Iterable Dataset\n    dataset = RLDSDataset(\n        data_root_dir,\n        data_mix,\n        batch_transform,\n        resize_resolution=default_image_resolution[1:],\n        shuffle_buffer_size=shuffle_buffer_size,\n        train=train,\n        image_aug=image_aug,\n    )\n\n    return dataset, action_tokenizer, collator\n"
  },
  {
    "path": "train/datasets/data_utils.py",
    "content": "\"\"\"\ndata_utils.py\n\nGeneral utilities and classes for facilitating data loading and collation.\n\"\"\"\nimport os\nimport math\nimport json\nimport random\nimport logging\nimport hashlib\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing import Callable, Dict, Sequence, Tuple, Union, List, Optional, Type, Any, Iterator\nimport torch\nimport torch.distributed as dist\nfrom torch.nn.utils.rnn import pad_sequence\nfrom torch.utils.data import Dataset, Sampler\nimport dlimp as dl\nimport numpy as np\nimport tensorflow as tf\nfrom tqdm import tqdm\nfrom model.overwatch import initialize_overwatch\n\noverwatch = initialize_overwatch(__name__)\n\n# HuggingFace Default / LLaMa-2 IGNORE_INDEX (for labels)\nIGNORE_INDEX = -100\n\nclass NormalizationType(str, Enum):\n    # fmt: off\n    NORMAL = \"normal\"               # Normalize to Mean = 0, Stdev = 1\n    BOUNDS = \"bounds\"               # Normalize to Interval = [-1, 1]\n    BOUNDS_Q99 = \"bounds_q99\"       # Normalize [quantile_01, ..., quantile_99] --> [-1, ..., 1]\n    # fmt: on\n\n\ndef tree_map(fn: Callable, tree: dict) -> dict:\n    \"\"\"Maps a function over a nested dictionary.\"\"\"\n    return {k: tree_map(fn, v) if isinstance(v, dict) else fn(v) for k, v in tree.items()}\n\n\ndef tree_map_with_key(fn: Callable, tree: dict, keys: Sequence = ()) -> dict:\n    \"\"\"Maps a function over a nested dictionary.\"\"\"\n    return {\n        k: tree_map_with_key(fn, v, (*keys, k)) if isinstance(v, dict) else fn((*keys, k), v) for k, v in tree.items()\n    }\n\n\n@dataclass\nclass PaddedCollatorForLanguageModeling:\n    model_max_length: int\n    pad_token_id: int\n    default_image_resolution: Tuple[int, int, int]\n    padding_side: str = \"right\"\n    pixel_values_dtype: torch.dtype = torch.float32\n\n    def __post_init__(self) -> None:\n        self.dummy_pixel_values = torch.zeros(self.default_image_resolution, dtype=self.pixel_values_dtype)\n\n    def __call__(self, instances: Sequence[Dict[str, torch.Tensor]]) -> Dict[str, torch.Tensor]:\n        input_ids, labels = tuple([instance[key] for instance in instances] for key in (\"input_ids\", \"labels\"))\n        pixel_values = [instance[\"pixel_values\"] for instance in instances]\n\n        # For now, we only support Tokenizers with `padding_side = \"right\"` during Training (but plan to extend!)\n        #   => Handle padding via RNN Utils => `pad_sequence`\n        input_ids = pad_sequence(input_ids, batch_first=True, padding_value=self.pad_token_id)\n        labels = pad_sequence(labels, batch_first=True, padding_value=IGNORE_INDEX)\n\n        # Truncate (if necessary)\n        input_ids, labels = input_ids[:, : self.model_max_length], labels[:, : self.model_max_length]\n\n        # Get `attention_mask` by checking for `pad_token_id`\n        attention_mask = input_ids.ne(self.pad_token_id)\n\n        # === Handle \"unimodal\" (language-only) vs. \"multimodal\" ===\n\n        # Some examples are \"language-only\" --> build a Tensor of `multimodal_indices` that we can slice into easily\n        multimodal_indices = torch.tensor(\n            [idx for idx in range(len(pixel_values)) if pixel_values[idx] is not None], dtype=torch.long\n        )\n\n        # Stack all `pixel_values` --> depending on type (torch.Tensor, or Dict[str, torch.Tensor]) & presence of None\n        if len(multimodal_indices) == 0:\n            pixel_values = torch.stack([self.dummy_pixel_values for _ in range(len(input_ids))])\n        elif isinstance(pv_example := pixel_values[multimodal_indices[0]], torch.Tensor):\n            pixel_values = torch.stack(\n                [\n                    pixel_values[idx] if idx in multimodal_indices else self.dummy_pixel_values\n                    for idx in range(len(input_ids))\n                ]\n            )\n        elif isinstance(pv_example, dict):\n            pixel_values = {\n                k: torch.stack(\n                    [\n                        pixel_values[idx][k] if idx in multimodal_indices else self.dummy_pixel_values\n                        for idx in range(len(input_ids))\n                    ]\n                )\n                for k in pv_example\n            }\n        else:\n            raise ValueError(f\"Unsupported `pixel_values` type = {type(pixel_values)}\")\n\n        return dict(\n            pixel_values=pixel_values,\n            input_ids=input_ids,\n            attention_mask=attention_mask,\n            labels=labels,\n            multimodal_indices=multimodal_indices,\n        )\n\n\n@dataclass\nclass PaddedCollatorForActionPrediction:\n    model_max_length: int\n    pad_token_id: int\n    padding_side: str = \"right\"\n    pixel_values_dtype: torch.dtype = torch.float32\n\n    def __call__(self, instances: Sequence[Dict[str, torch.Tensor]]) -> Dict[str, torch.Tensor]:\n        input_ids, labels = tuple([instance[key] for instance in instances] for key in (\"input_ids\", \"labels\"))\n        pixel_values = [instance[\"pixel_values\"] for instance in instances]\n        if \"dataset_name\" in instances[0]:\n            dataset_names = [instance[\"dataset_name\"] for instance in instances]\n        else:\n            dataset_names = None\n\n        # For now, we only support Tokenizers with `padding_side = \"right\"` during training\n        #   => Handle padding via RNN Utils => `pad_sequence`\n        assert self.padding_side == \"right\", f\"Invalid Tokenizer `{self.padding_side = }`\"\n        input_ids = pad_sequence(input_ids, batch_first=True, padding_value=self.pad_token_id)\n        labels = pad_sequence(labels, batch_first=True, padding_value=IGNORE_INDEX)\n\n        # Truncate (if necessary)\n        input_ids, labels = input_ids[:, : self.model_max_length], labels[:, : self.model_max_length]\n\n        # Get `attention_mask` by checking for `pad_token_id`\n        attention_mask = input_ids.ne(self.pad_token_id)\n\n        # [Contract] For VLA Training =>> No \"Unimodal\" Data!\n        assert all([pv is not None for pv in pixel_values]), \"Invalid VLA Example with `pixel_values = None`!\"\n\n        # Stack all `pixel_values` --> depending on type is torch.Tensor or Dict[str, torch.Tensor]\n        if isinstance(pixel_values[0], torch.Tensor):\n            pixel_values = torch.stack(pixel_values)\n        elif isinstance(pixel_values[0], dict):\n            pixel_values = {\n                k: torch.stack([pixel_values[idx][k] for idx in range(len(input_ids))]) for k in pixel_values[0]\n            }\n        else:\n            raise ValueError(f\"Unsupported `pixel_values` type = {type(pixel_values)}\")\n\n        output = dict(\n            pixel_values=pixel_values,\n            input_ids=input_ids,\n            attention_mask=attention_mask,\n            labels=labels,\n        )\n        if dataset_names is not None:\n            output[\"dataset_names\"] = dataset_names\n        return output\n\ndef set_global_seed(seed: int, get_worker_init_fn: bool = False) -> Optional[Callable[[int], None]]:\n    \"\"\"Sets seed for all randomness libraries (mostly random, numpy, torch) and produces a `worker_init_fn`\"\"\"\n    assert np.iinfo(np.uint32).min < seed < np.iinfo(np.uint32).max, \"Seed outside the np.uint32 bounds!\"\n\n    # Set Seed as an Environment Variable\n    os.environ[\"EXPERIMENT_GLOBAL_SEED\"] = str(seed)\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n\n    return worker_init_function if get_worker_init_fn else None\n\n\ndef worker_init_function(worker_id: int) -> None:\n    \"\"\"\n    Borrowed directly from PyTorch-Lightning; inspired by this issue comment in the PyTorch repo:\n        > Ref: https://github.com/pytorch/pytorch/issues/5059#issuecomment-817392562\n\n    Intuition: You can think of the seed sequence spawn function as a \"janky\" torch.Generator() or jax.PRNGKey that\n    you can run iterative splitting on to get new (predictable) randomness.\n\n    :param worker_id: Identifier for the given worker [0, num_workers) for the Dataloader in question.\n    \"\"\"\n    # Get current `rank` (if running distributed) and `process_seed`\n    global_rank, process_seed = int(os.environ[\"LOCAL_RANK\"]), torch.initial_seed()\n\n    # Back out the \"base\" (original) seed - the per-worker seed is set in PyTorch:\n    #   > https://pytorch.org/docs/stable/data.html#data-loading-randomness\n    base_seed = process_seed - worker_id\n\n    # \"Magic\" code --> basically creates a seed sequence that mixes different \"sources\" and seeds every library...\n    seed_seq = np.random.SeedSequence([base_seed, worker_id, global_rank])\n\n    # Use 128 bits (4 x 32-bit words) to represent seed --> generate_state(k) produces a `k` element array!\n    np.random.seed(seed_seq.generate_state(4))\n\n    # Spawn distinct child sequences for PyTorch (reseed) and stdlib random\n    torch_seed_seq, random_seed_seq = seed_seq.spawn(2)\n\n    # Torch Manual seed takes 64 bits (so just specify a dtype of uint64\n    torch.manual_seed(torch_seed_seq.generate_state(1, dtype=np.uint64)[0])\n\n    # Use 128 Bits for `random`, but express as integer instead of as an array\n    random_seed = (random_seed_seq.generate_state(2, dtype=np.uint64).astype(list) * [1 << 64, 1]).sum()\n    random.seed(random_seed)\n\n\n# === BFloat16 Support ===\n\n\ndef check_bloat16_supported() -> bool:\n    try:\n        import packaging.version\n        import torch.cuda.nccl as nccl\n        import torch.distributed as dist\n\n        return (\n            (torch.version.cuda is not None)\n            and torch.cuda.is_bf16_supported()\n            and (packaging.version.parse(torch.version.cuda).release >= (11, 0))\n            and dist.is_nccl_available()\n            and (nccl.version() >= (2, 10))\n        )\n\n    except Exception:\n        return False\n\n\n# High-Fidelity Bitwise Reproduction of the LLaVa Codebase Sampler Strategy + Per-Rank Allocation Scheme (following\n#   the default batching behavior of HF's Trainer Class --> derived from `accelerate`).\n#\n#   =>> Reference: https://github.com/haotian-liu/LLaVA/blob/main/llava/train/llava_trainer.py#L60\n#   =>> Reference: https://github.com/huggingface/transformers/blob/main/src/transformers/trainer_pt_utils.py#L603\nclass SplitModalitySampler(Sampler):\n    def __init__(\n        self,\n        dataset: Dataset,\n        modality_lengths: List[Tuple[bool, int]],\n        global_batch_size: int,\n        num_replicas: Optional[int] = None,\n        rank: Optional[int] = None,\n        seed: int = 0,\n        drop_last: bool = False,\n    ) -> None:\n        super().__init__()\n        self.num_replicas = num_replicas if num_replicas is not None else dist.get_world_size()\n        self.rank = rank if rank is not None else dist.get_rank()\n        self.seed, self.epoch = seed, 0\n\n        # Custom Parameters\n        self.dataset, self.modality_lengths, self.drop_last = dataset, modality_lengths, drop_last\n        self.global_batch_size = global_batch_size\n\n        # For our purposes, `drop_last` is always False!\n        assert not self.drop_last, \"SplitModalitySampler must set `drop_last = False`!\"\n        self.total_size = math.ceil(len(self.dataset) / self.global_batch_size) * self.global_batch_size\n        self.num_samples = self.total_size // self.num_replicas\n\n    @staticmethod\n    def reindex_batch(batch_idxs: List[int], idx2lengths: List[int], n_buckets: int) -> List[List[int]]:\n        \"\"\"Re-indexes a batch in a way that is conducive to DistributedSampler + grouping by seqlen per rank.\"\"\"\n        assert len(batch_idxs) % n_buckets == 0, \"Batch length is not divisible by `num_replicas`!\"\n\n        # Establish initial buckets, capacities, and max number of elements per bucket\n        n_examples_per_bucket = len(batch_idxs) // n_buckets\n        bucket_indices = [[] for _ in range(n_buckets)]\n        bucket_lengths = [0 for _ in range(n_buckets)]\n\n        # Note that `batch_idxs` is already sorted by corresponding length (in descending order)\n        for idx in batch_idxs:\n            shortest_bucket_idx = bucket_lengths.index(min(bucket_lengths))\n            bucket_indices[shortest_bucket_idx].append(idx)\n\n            # Update `bucket_lengths` --> set length to infinity if at capacity!\n            bucket_lengths[shortest_bucket_idx] += idx2lengths[idx]\n            if len(bucket_indices[shortest_bucket_idx]) == n_examples_per_bucket:\n                bucket_lengths[shortest_bucket_idx] = float(\"inf\")\n\n        return bucket_indices\n\n    def get_modality_and_length_grouped_indices(self, generator: torch.Generator) -> List[int]:\n        \"\"\"\n        Returns a list of indices so that each slice of `global_batch_size` consecutive indices corresponds to elements\n        of the same modality with each sub-sequence of `per_replica_batch_size` (the batch size each unique device sees\n        during distributed training) is roughly grouped by sequence length (for training efficiency).\n        \"\"\"\n        multimodal_indices, multimodal_lengths = zip(\n            *[(idx, length) for idx, (is_multimodal, length) in enumerate(self.modality_lengths) if is_multimodal]\n        )\n\n        # Handle Special Case --> no \"unimodal\" inputs\n        unimodal_split = [\n            (idx, length) for idx, (is_multimodal, length) in enumerate(self.modality_lengths) if not is_multimodal\n        ]\n        if len(unimodal_split) == 0:\n            unimodal_indices, unimodal_lengths = [], []\n        else:\n            unimodal_indices, unimodal_lengths = zip(*unimodal_split)\n\n        # Create a permutation of indices for each of the multimodal and unimodal data\n        mm_shuffled_idxs = torch.randperm(len(multimodal_indices), generator=generator)\n        uni_shuffled_idxs = torch.randperm(len(unimodal_indices), generator=generator)\n\n        # We're going to be running sorting/grouping relative to `self.global_batch_size` and `self.num_replicas`\n        g_bsz = self.global_batch_size\n\n        # Break each of the permutations into batches of length `global_batch_size`\n        mm_batch_idxs = [mm_shuffled_idxs[i : i + g_bsz].tolist() for i in range(0, len(mm_shuffled_idxs), g_bsz)]\n        uni_batch_idxs = [uni_shuffled_idxs[i : i + g_bsz].tolist() for i in range(0, len(uni_shuffled_idxs), g_bsz)]\n\n        # If \"last\" batch is not of length `g_bsz` --> PAD by stealing indices from the first batch!\n        if len(mm_batch_idxs[-1]) < g_bsz:\n            n_missing = g_bsz - len(mm_batch_idxs[-1])\n            mm_batch_idxs[-1].extend(mm_batch_idxs[0][:n_missing])\n\n        if len(uni_batch_idxs) > 0 and len(uni_batch_idxs[-1]) < g_bsz:\n            n_missing = g_bsz - len(uni_batch_idxs[-1])\n            uni_batch_idxs[-1].extend(uni_batch_idxs[0][:n_missing])\n\n        # Now we're going to sort each batch by length --> this will aid in grouping by length by rank (efficiency!)\n        mm_sorted_batch_idxs = [sorted(b, key=lambda i: multimodal_lengths[i], reverse=True) for b in mm_batch_idxs]\n        uni_sorted_batch_idxs = [sorted(b, key=lambda i: unimodal_lengths[i], reverse=True) for b in uni_batch_idxs]\n\n        # IMPORTANT :: At this point, for each modality, we have a list of \"batches\" (made up of indices) where indices\n        # are sorted by example sequence length *within* each batch. To make this more concrete, consider the following:\n        #   => World Size (`num_replicas`) = 2\n        #   => Global Batch Size (`g_bsz`) = 4\n        #   => `multimodal_indices` = [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11]\n        #      `multimodal_lengths` = [20, 90, 21, 22, 91, 18, 89, 19, 93, 88, 92, 17]\n        #\n        # At this point in the code, `mm_sorted_batch_idxs` might then look like the following (length in parenthesis):\n        #   => `mm_sorted_batch_idxs`: [\n        #       [4  (91), 3  (21), 0  (20), 5  (18)]    => Batch 1\n        #       [6  (89), 9  (88), 7  (19), 11 (17)]    => Batch 2\n        #       [8  (93), 10 (92), 1  (90), 2  (21)]    => Batch 3\n        #   ]\n        #\n        # In practice: `g_bsz` is large (= 128), and for contiguous mini-batch \"slices\", length variance is low.\n\n        # PROBLEM :: We want to split these \"global batches\" into equal-sized pieces, so that each \"replica\" (GPU)\n        # sees a \"mini-batch\" of roughly the same sequence lengths; this is super useful for efficient training.\n\n        # HOWEVER :: The default \"access pattern\" for splitting a large batch into mini-batches by a DistributedSampler\n        # is akin to a \"take every k\" where `k` is equal to the number of replicas (GPUs) you're training on. Or, in\n        # Python notation --> `rank_k_indices = flatten(mm_sorted_batch_idxs)[k::num_replicas].\n        #\n        # Naively translating this our example means each GPU (in our world of 2 total) sees the following indices\n        # (grouped by \"mini-batch\" = `g_bsz / num_replicas` = 2 for convenience):\n        #   => `rank_0_indices`: [ [4 (91), 0 (20)] =>> [6 (89), 7  (19)] =>> [8  (93), 1 (90)] ]\n        #   => `rank_1_indices`: [ [3 (21), 5 (18)] =>> [9 (88), 11 (17)] =>> [10 (92), 2 (21)] ]\n        #\n        # We get lucky sometimes, but for the most part, each \"mini-batch\" has VASTLY DIFFERENT lengths! Bad!\n\n        # FIX :: If we \"undo\" the access pattern with the following code and re-arrange the way we allocate batches\n        # inside the __iter__ method below, we can allocate indices appropriately. Running the following code gives us\n        # the following indices (grouped by \"mini-batch\" again for convenience):\n        #   => `rank_0_indices`: [ [4 (91), 3 (21)] =>> [6  (89), 9 (88)] =>> [8 (93), 10 (92)] ]\n        #   => `rank_1_indices`: [ [5 (18), 0 (20)] =>> [11 (17), 7 (19)] =>> [2 (21),  1 (90)] ]\n        #\n        # Much better! As `g_bsz` and `dataset` grow, we're more often than not getting *decent* groupings!\n        mm_length_bucketed_idxs = [\n            self.reindex_batch(batch, multimodal_lengths, self.num_replicas) for batch in mm_sorted_batch_idxs\n        ]\n        uni_length_bucketed_idxs = [\n            self.reindex_batch(batch, unimodal_lengths, self.num_replicas) for batch in uni_sorted_batch_idxs\n        ]\n\n        # Note :: Because of the initial `randperm` --> we're indexing both sets from 0 (we're clobbering the range)\n        #   => Flatten indices --> index into original `{modality}_indices` then re-batch!\n        mm_output_idxs = [idx for batch in mm_length_bucketed_idxs for bucket in batch for idx in bucket]\n        mm_reindexed = [multimodal_indices[idx] for idx in mm_output_idxs]\n        mm_batches = [mm_reindexed[i : i + g_bsz] for i in range(0, len(mm_reindexed), g_bsz)]\n\n        uni_output_idxs = [idx for batch in uni_length_bucketed_idxs for bucket in batch for idx in bucket]\n        uni_reindexed = [unimodal_indices[idx] for idx in uni_output_idxs]\n        uni_batches = [uni_reindexed[i : i + g_bsz] for i in range(0, len(uni_reindexed), g_bsz)]\n\n        # Finally, randomly permute the multimodal & unimodal batches, merging into a single stream of indices\n        merged_batches = mm_batches + uni_batches\n        merge_idxs = torch.randperm(len(merged_batches), generator=generator)\n        all_batches = [merged_batches[idx] for idx in merge_idxs]\n\n        # [Quality of Life] Shift \"max length\" batch to index 0 --> if we OOM, it happens immediately!\n        all_lengths = [length + ((_n_patches := 24 * 24) if is_mm else 0) for is_mm, length in self.modality_lengths]\n        all_batches_max_lengths = []\n        for batch in all_batches:\n            all_batches_max_lengths.append(max([all_lengths[idx] for idx in batch]))\n\n        # Identify Batch with \"max length\" --> Swap into Index 0\n        longest_batch_idx = np.argmax(all_batches_max_lengths)\n        all_batches[0], all_batches[longest_batch_idx] = all_batches[longest_batch_idx], all_batches[0]\n\n        # Flatten & Return all Indices\n        indices = [idx for batch in all_batches for idx in batch]\n        return indices\n\n    def __iter__(self) -> Iterator:\n        \"\"\"Deterministically shuffle, then split indices by modality and length.\"\"\"\n        g = torch.Generator()\n        g.manual_seed(self.seed + self.epoch)\n        indices = self.get_modality_and_length_grouped_indices(g)\n        assert len(set(indices)) == len(self.modality_lengths) == len(self.dataset), \"Oops!\"\n        assert (len(indices) % self.global_batch_size == 0) and (len(indices) % self.num_replicas) == 0, \"Oops\"\n\n        # Note :: We compute per-replica batch size as a function of `global_batch` and `num_replicas` to ensure that\n        # gradient accumulation doesn't affect what indices are assigned a given rank.\n        per_replica_batch_size = self.global_batch_size // self.num_replicas\n\n        # Tensorize & Unravel --> rather than yielding via a `take_every` --> we want to partition a global batch\n        # across replicas by assigning each a contiguous sub-sequence.\n        indices_t = torch.as_tensor(indices)\n        per_replica_batch_indices_t = indices_t.reshape(-1, per_replica_batch_size)\n        replica_indices_t = per_replica_batch_indices_t[self.rank :: self.num_replicas]\n\n        replica_indices = replica_indices_t.flatten().tolist()\n        return iter(replica_indices)\n\n    def __len__(self) -> int:\n        return self.num_samples\n\n    def set_epoch(self, epoch: int) -> None:\n        \"\"\"To be called *between* epochs, prior to DataLoader instantiation; ensures random order across epochs.\"\"\"\n        self.epoch = epoch\n\n\n# ruff: noqa: B023\ndef augment(obs: Dict, seed: tf.Tensor, augment_kwargs: Union[Dict, Dict[str, Dict]]) -> Dict:\n    \"\"\"Augments images, skipping padding images.\"\"\"\n    image_names = {key[6:] for key in obs if key.startswith(\"image_\")}\n\n    # \"augment_order\" is required in augment_kwargs, so if it's there, we can assume that the user has passed\n    # in a single augmentation dict (otherwise, we assume that the user has passed in a mapping from image\n    # name to augmentation dict)\n    if \"augment_order\" in augment_kwargs:\n        augment_kwargs = {name: augment_kwargs for name in image_names}\n\n    for i, name in enumerate(image_names):\n        if name not in augment_kwargs:\n            continue\n        kwargs = augment_kwargs[name]\n        logging.debug(f\"Augmenting image_{name} with kwargs {kwargs}\")\n        obs[f\"image_{name}\"] = tf.cond(\n            obs[\"pad_mask_dict\"][f\"image_{name}\"],\n            lambda: dl.transforms.augment_image(\n                obs[f\"image_{name}\"],\n                **kwargs,\n                seed=seed + i,  # augment each image differently\n            ),\n            lambda: obs[f\"image_{name}\"],  # skip padding images\n        )\n\n    return obs\n\n\ndef decode_and_resize(\n    obs: Dict,\n    resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]],\n    depth_resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]],\n) -> Dict:\n    \"\"\"Decodes images and depth images, and then optionally resizes them.\"\"\"\n    image_names = {key[6:] for key in obs if key.startswith(\"image_\")}\n    depth_names = {key[6:] for key in obs if key.startswith(\"depth_\")}\n\n    if isinstance(resize_size, tuple):\n        resize_size = {name: resize_size for name in image_names}\n    if isinstance(depth_resize_size, tuple):\n        depth_resize_size = {name: depth_resize_size for name in depth_names}\n\n    for name in image_names:\n        if name not in resize_size:\n            logging.warning(\n                f\"No resize_size was provided for image_{name}. This will result in 1x1 \"\n                \"padding images, which may cause errors if you mix padding and non-padding images.\"\n            )\n        image = obs[f\"image_{name}\"]\n        if image.dtype == tf.string:\n            if tf.strings.length(image) == 0:\n                # this is a padding image\n                image = tf.zeros((*resize_size.get(name, (1, 1)), 3), dtype=tf.uint8)\n            else:\n                image = tf.io.decode_image(image, expand_animations=False, dtype=tf.uint8)\n        elif image.dtype != tf.uint8:\n            raise ValueError(f\"Unsupported image dtype: found image_{name} with dtype {image.dtype}\")\n        if name in resize_size:\n            image = dl.transforms.resize_image(image, size=resize_size[name])\n        obs[f\"image_{name}\"] = image\n\n    for name in depth_names:\n        if name not in depth_resize_size:\n            logging.warning(\n                f\"No depth_resize_size was provided for depth_{name}. This will result in 1x1 \"\n                \"padding depth images, which may cause errors if you mix padding and non-padding images.\"\n            )\n        depth = obs[f\"depth_{name}\"]\n\n        if depth.dtype == tf.string:\n            if tf.strings.length(depth) == 0:\n                depth = tf.zeros((*depth_resize_size.get(name, (1, 1)), 1), dtype=tf.float32)\n            else:\n                depth = tf.io.decode_image(depth, expand_animations=False, dtype=tf.float32)[..., 0]\n        elif depth.dtype != tf.float32:\n            raise ValueError(f\"Unsupported depth dtype: found depth_{name} with dtype {depth.dtype}\")\n\n        if name in depth_resize_size:\n            depth = dl.transforms.resize_depth_image(depth, size=depth_resize_size[name])\n\n        obs[f\"depth_{name}\"] = depth\n\n    return obs\n\n\n\ndef chunk_act_obs(traj: Dict, window_size: int, future_action_window_size: int = 0) -> Dict:\n    \"\"\"\n    Chunks actions and observations into the given window_size.\n\n    \"observation\" keys are given a new axis (at index 1) of size `window_size` containing `window_size - 1`\n    observations from the past and the current observation. \"action\" is given a new axis (at index 1) of size\n    `window_size + future_action_window_size` containing `window_size - 1` actions from the past, the current\n    action, and `future_action_window_size` actions from the future. \"pad_mask\" is added to \"observation\" and\n    indicates whether an observation should be considered padding (i.e. if it had come from a timestep\n    before the start of the trajectory).\n    \"\"\"\n    traj_len = tf.shape(traj[\"action\"])[0]\n    action_dim = traj[\"action\"].shape[-1]\n    chunk_indices = tf.broadcast_to(tf.range(-window_size + 1, 1), [traj_len, window_size]) + tf.broadcast_to(\n        tf.range(traj_len)[:, None], [traj_len, window_size]\n    )\n\n    action_chunk_indices = tf.broadcast_to(\n        tf.range(-window_size + 1, 1 + future_action_window_size),\n        [traj_len, window_size + future_action_window_size],\n    ) + tf.broadcast_to(\n        tf.range(traj_len)[:, None],\n        [traj_len, window_size + future_action_window_size],\n    )\n\n    floored_chunk_indices = tf.maximum(chunk_indices, 0)\n\n    if \"timestep\" in traj[\"task\"]:\n        goal_timestep = traj[\"task\"][\"timestep\"]\n    else:\n        goal_timestep = tf.fill([traj_len], traj_len - 1)\n\n    floored_action_chunk_indices = tf.minimum(tf.maximum(action_chunk_indices, 0), goal_timestep[:, None])\n\n    traj[\"observation\"] = tf.nest.map_structure(lambda x: tf.gather(x, floored_chunk_indices), traj[\"observation\"])\n    traj[\"action\"] = tf.gather(traj[\"action\"], floored_action_chunk_indices)\n\n    # indicates whether an entire observation is padding\n    traj[\"observation\"][\"pad_mask\"] = chunk_indices >= 0\n\n    # if no absolute_action_mask was provided, assume all actions are relative\n    if \"absolute_action_mask\" not in traj and future_action_window_size > 0:\n        logging.warning(\n            \"future_action_window_size > 0 but no absolute_action_mask was provided. \"\n            \"Assuming all actions are relative for the purpose of making neutral actions.\"\n        )\n    absolute_action_mask = traj.get(\"absolute_action_mask\", tf.zeros([traj_len, action_dim], dtype=tf.bool))\n    neutral_actions = tf.where(\n        absolute_action_mask[:, None, :],\n        traj[\"action\"],  # absolute actions are repeated (already done during chunking)\n        tf.zeros_like(traj[\"action\"]),  # relative actions are zeroed\n    )\n\n    # actions past the goal timestep become neutral\n    action_past_goal = action_chunk_indices > goal_timestep[:, None]\n    traj[\"action\"] = tf.where(action_past_goal[:, :, None], neutral_actions, traj[\"action\"])\n\n    return traj\n\n\ndef subsample(traj: Dict, subsample_length: int) -> Dict:\n    \"\"\"Subsamples trajectories to the given length.\"\"\"\n    traj_len = tf.shape(traj[\"action\"])[0]\n    if traj_len > subsample_length:\n        indices = tf.random.shuffle(tf.range(traj_len))[:subsample_length]\n        traj = tf.nest.map_structure(lambda x: tf.gather(x, indices), traj)\n\n    return traj\n\n\ndef add_pad_mask_dict(traj: Dict) -> Dict:\n    \"\"\"\n    Adds a dictionary indicating which elements of the observation/task should be treated as padding.\n        =>> traj[\"observation\"|\"task\"][\"pad_mask_dict\"] = {k: traj[\"observation\"|\"task\"][k] is not padding}\n    \"\"\"\n    traj_len = tf.shape(traj[\"action\"])[0]\n\n    for key in [\"observation\", \"task\"]:\n        pad_mask_dict = {}\n        for subkey in traj[key]:\n            # Handles \"language_instruction\", \"image_*\", and \"depth_*\"\n            if traj[key][subkey].dtype == tf.string:\n                pad_mask_dict[subkey] = tf.strings.length(traj[key][subkey]) != 0\n\n            # All other keys should not be treated as padding\n            else:\n                pad_mask_dict[subkey] = tf.ones([traj_len], dtype=tf.bool)\n\n        traj[key][\"pad_mask_dict\"] = pad_mask_dict\n\n    return traj\n\n\n# ruff: noqa: B023\ndef normalize_action_and_proprio(traj: Dict, metadata: Dict, normalization_type: NormalizationType):\n    \"\"\"Normalizes the action and proprio fields of a trajectory using the given metadata.\"\"\"\n    keys_to_normalize = {\"action\": \"action\", \"proprio\": \"observation/proprio\"}\n\n    if normalization_type == NormalizationType.NORMAL:\n        for key, traj_key in keys_to_normalize.items():\n            mask = metadata[key].get(\"mask\", tf.ones_like(metadata[key][\"mean\"], dtype=tf.bool))\n            traj = dl.transforms.selective_tree_map(\n                traj,\n                match=lambda k, _: k == traj_key,\n                map_fn=lambda x: tf.where(mask, (x - metadata[key][\"mean\"]) / (metadata[key][\"std\"] + 1e-8), x),\n            )\n\n        return traj\n\n    elif normalization_type in [NormalizationType.BOUNDS, NormalizationType.BOUNDS_Q99]:\n        for key, traj_key in keys_to_normalize.items():\n            if normalization_type == NormalizationType.BOUNDS:\n                low = metadata[key][\"min\"]\n                high = metadata[key][\"max\"]\n            elif normalization_type == NormalizationType.BOUNDS_Q99:\n                low = metadata[key][\"q01\"]\n                high = metadata[key][\"q99\"]\n            mask = metadata[key].get(\"mask\", tf.ones_like(metadata[key][\"min\"], dtype=tf.bool))\n            traj = dl.transforms.selective_tree_map(\n                traj,\n                match=lambda k, _: k == traj_key,\n                map_fn=lambda x: tf.where(\n                    mask,\n                    tf.clip_by_value(2 * (x - low) / (high - low + 1e-8) - 1, -1, 1),\n                    x,\n                ),\n            )\n\n            # Note (Moo Jin): Map unused action dimensions (i.e., dimensions where min == max) to all 0s.\n            zeros_mask = metadata[key][\"min\"] == metadata[key][\"max\"]\n            traj = dl.transforms.selective_tree_map(\n                traj, match=lambda k, _: k == traj_key, map_fn=lambda x: tf.where(zeros_mask, 0.0, x)\n            )\n\n        return traj\n\n    raise ValueError(f\"Unknown Normalization Type {normalization_type}\")\n\n\ndef binarize_gripper_actions(actions: tf.Tensor) -> tf.Tensor:\n    \"\"\"\n    Converts gripper actions from continuous to binary values (0 and 1).\n\n    We exploit that fact that most of the time, the gripper is fully open (near 1.0) or fully closed (near 0.0). As it\n    transitions between the two, it sometimes passes through a few intermediate values. We relabel those intermediate\n    values based on the state that is reached _after_ those intermediate values.\n\n    In the edge case that the trajectory ends with an intermediate value, we give up on binarizing and relabel that\n    chunk of intermediate values as the last action in the trajectory.\n\n    The `scan_fn` implements the following logic:\n        new_actions = np.empty_like(actions)\n        carry = actions[-1]\n        for i in reversed(range(actions.shape[0])):\n            if in_between_mask[i]:\n                carry = carry\n            else:\n                carry = float(open_mask[i])\n            new_actions[i] = carry\n    \"\"\"\n    open_mask, closed_mask = actions > 0.95, actions < 0.05\n    in_between_mask = tf.logical_not(tf.logical_or(open_mask, closed_mask))\n    is_open_float = tf.cast(open_mask, tf.float32)\n\n    def scan_fn(carry, i):\n        return tf.cond(in_between_mask[i], lambda: tf.cast(carry, tf.float32), lambda: is_open_float[i])\n\n    return tf.scan(scan_fn, tf.range(tf.shape(actions)[0]), actions[-1], reverse=True)\n\n\ndef invert_gripper_actions(actions: tf.Tensor) -> tf.Tensor:\n    return 1 - actions\n\n# === RLDS Dataset Initialization Utilities ===\ndef pprint_data_mixture(dataset_kwargs_list: List[Dict[str, Any]], dataset_weights: List[int]) -> None:\n    print(\"\\n######################################################################################\")\n    print(f\"# Loading the following {len(dataset_kwargs_list)} datasets (incl. sampling weight):{'': >24} #\")\n    for dataset_kwargs, weight in zip(dataset_kwargs_list, dataset_weights):\n        pad = 80 - len(dataset_kwargs[\"name\"])\n        print(f\"# {dataset_kwargs['name']}: {weight:=>{pad}f} #\")\n    print(\"######################################################################################\\n\")\n\n\ndef get_dataset_statistics(\n    dataset: dl.DLataset,\n    hash_dependencies: Tuple[str, ...],\n    save_dir: Optional[str] = None,\n) -> Dict:\n    \"\"\"\n    Either computes the statistics of a dataset or loads them from a cache file if this function has been called before\n    with the same `hash_dependencies`.\n\n    Currently, the statistics include the min/max/mean/std of the actions and proprio as well as the number of\n    transitions and trajectories in the dataset.\n    \"\"\"\n    unique_hash = hashlib.sha256(\"\".join(hash_dependencies).encode(\"utf-8\"), usedforsecurity=False).hexdigest()\n\n    # Fallback local path for when data_dir is not writable or not provided\n    local_path = os.path.expanduser(os.path.join(\"~\", \".cache\", \"orca\", f\"dataset_statistics_{unique_hash}.json\"))\n    if save_dir is not None:\n        path = tf.io.gfile.join(save_dir, f\"dataset_statistics_{unique_hash}.json\")\n    else:\n        path = local_path\n\n    # check if cache file exists and load\n    if tf.io.gfile.exists(path):\n        overwatch.info(f\"Loading existing dataset statistics from {path}.\")\n        with tf.io.gfile.GFile(path, \"r\") as f:\n            metadata = json.load(f)\n        return metadata\n\n    if os.path.exists(local_path):\n        overwatch.info(f\"Loading existing dataset statistics from {local_path}.\")\n        with open(local_path, \"r\") as f:\n            metadata = json.load(f)\n        return metadata\n\n    dataset = dataset.traj_map(\n        lambda traj: {\n            \"action\": traj[\"action\"],\n            \"proprio\": (\n                traj[\"observation\"][\"proprio\"] if \"proprio\" in traj[\"observation\"] else tf.zeros_like(traj[\"action\"])\n            ),\n        }\n    )\n\n    cardinality = dataset.cardinality().numpy()\n    if cardinality == tf.data.INFINITE_CARDINALITY:\n        raise ValueError(\"Cannot compute dataset statistics for infinite datasets.\")\n\n    overwatch.info(\"Computing dataset statistics. This may take a bit, but should only need to happen once.\")\n    actions, proprios, num_transitions, num_trajectories = [], [], 0, 0\n    for traj in tqdm(dataset.iterator(), total=cardinality if cardinality != tf.data.UNKNOWN_CARDINALITY else None):\n        actions.append(traj[\"action\"])\n        proprios.append(traj[\"proprio\"])\n        num_transitions += traj[\"action\"].shape[0]\n        num_trajectories += 1\n\n    actions, proprios = np.concatenate(actions), np.concatenate(proprios)\n    metadata = {\n        \"action\": {\n            \"mean\": actions.mean(0).tolist(),\n            \"std\": actions.std(0).tolist(),\n            \"max\": actions.max(0).tolist(),\n            \"min\": actions.min(0).tolist(),\n            \"q01\": np.quantile(actions, 0.01, axis=0).tolist(),\n            \"q99\": np.quantile(actions, 0.99, axis=0).tolist(),\n        },\n        \"proprio\": {\n            \"mean\": proprios.mean(0).tolist(),\n            \"std\": proprios.std(0).tolist(),\n            \"max\": proprios.max(0).tolist(),\n            \"min\": proprios.min(0).tolist(),\n            \"q01\": np.quantile(proprios, 0.01, axis=0).tolist(),\n            \"q99\": np.quantile(proprios, 0.99, axis=0).tolist(),\n        },\n        \"num_transitions\": num_transitions,\n        \"num_trajectories\": num_trajectories,\n    }\n\n    try:\n        with tf.io.gfile.GFile(path, \"w\") as f:\n            json.dump(metadata, f)\n    except tf.errors.PermissionDeniedError:\n        overwatch.warning(f\"Could not write dataset statistics to {path}. Writing to {local_path} instead.\")\n        os.makedirs(os.path.dirname(local_path), exist_ok=True)\n        with open(local_path, \"w\") as f:\n            json.dump(metadata, f)\n\n    return metadata\n\n\ndef save_dataset_statistics(dataset_statistics, run_dir):\n    \"\"\"Saves a `dataset_statistics.json` file.\"\"\"\n    out_path = run_dir / \"dataset_statistics.json\"\n    with open(out_path, \"w\") as f_json:\n        for _, stats in dataset_statistics.items():\n            for k in stats[\"action\"].keys():\n                if isinstance(stats[\"action\"][k], np.ndarray):\n                    stats[\"action\"][k] = stats[\"action\"][k].tolist()\n            if \"proprio\" in stats:\n                for k in stats[\"proprio\"].keys():\n                    if isinstance(stats[\"proprio\"][k], np.ndarray):\n                        stats[\"proprio\"][k] = stats[\"proprio\"][k].tolist()\n            if \"num_trajectories\" in stats:\n                if isinstance(stats[\"num_trajectories\"], np.ndarray):\n                    stats[\"num_trajectories\"] = stats[\"num_trajectories\"].item()\n            if \"num_transitions\" in stats:\n                if isinstance(stats[\"num_transitions\"], np.ndarray):\n                    stats[\"num_transitions\"] = stats[\"num_transitions\"].item()\n        json.dump(dataset_statistics, f_json, indent=2)\n    overwatch.info(f\"Saved dataset statistics file at path {out_path}\")\n\n\ndef allocate_threads(n: Optional[int], weights: np.ndarray):\n    \"\"\"\n    Allocates an integer number of threads across datasets based on weights.\n\n    The final array sums to `n`, but each element is no less than 1. If `n` is None, then every dataset is assigned a\n    value of AUTOTUNE.\n    \"\"\"\n    if n is None:\n        return np.array([tf.data.AUTOTUNE] * len(weights))\n\n    assert np.all(weights >= 0), \"Weights must be non-negative\"\n    assert len(weights) <= n, \"Number of threads must be at least as large as length of weights\"\n    weights = np.array(weights) / np.sum(weights)\n\n    allocation = np.zeros_like(weights, dtype=int)\n    while True:\n        # Give the remaining elements that would get less than 1 a 1\n        mask = (weights * n < 1) & (weights > 0)\n        if not mask.any():\n            break\n        n -= mask.sum()\n        allocation += mask.astype(int)\n\n        # Recompute the distribution over the remaining elements\n        weights[mask] = 0\n        weights = weights / weights.sum()\n\n    # Allocate the remaining elements\n    fractional, integral = np.modf(weights * n)\n    allocation += integral.astype(int)\n    n -= integral.sum()\n    for i in np.argsort(fractional)[::-1][: int(n)]:\n        allocation[i] += 1\n\n    return allocation\n\n\ndef decode_and_resize(\n    obs: Dict,\n    resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]],\n    depth_resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]],\n) -> Dict:\n    \"\"\"Decodes images and depth images, and then optionally resizes them.\"\"\"\n    image_names = {key[6:] for key in obs if key.startswith(\"image_\")}\n    depth_names = {key[6:] for key in obs if key.startswith(\"depth_\")}\n\n    if isinstance(resize_size, tuple):\n        resize_size = {name: resize_size for name in image_names}\n    if isinstance(depth_resize_size, tuple):\n        depth_resize_size = {name: depth_resize_size for name in depth_names}\n\n    for name in image_names:\n        if name not in resize_size:\n            logging.warning(\n                f\"No resize_size was provided for image_{name}. This will result in 1x1 \"\n                \"padding images, which may cause errors if you mix padding and non-padding images.\"\n            )\n        image = obs[f\"image_{name}\"]\n        if image.dtype == tf.string:\n            if tf.strings.length(image) == 0:\n                # this is a padding image\n                image = tf.zeros((*resize_size.get(name, (1, 1)), 3), dtype=tf.uint8)\n            else:\n                image = tf.io.decode_image(image, expand_animations=False, dtype=tf.uint8)\n        elif image.dtype != tf.uint8:\n            raise ValueError(f\"Unsupported image dtype: found image_{name} with dtype {image.dtype}\")\n        if name in resize_size:\n            image = dl.transforms.resize_image(image, size=resize_size[name])\n        obs[f\"image_{name}\"] = image\n\n    for name in depth_names:\n        if name not in depth_resize_size:\n            logging.warning(\n                f\"No depth_resize_size was provided for depth_{name}. This will result in 1x1 \"\n                \"padding depth images, which may cause errors if you mix padding and non-padding images.\"\n            )\n        depth = obs[f\"depth_{name}\"]\n\n        if depth.dtype == tf.string:\n            if tf.strings.length(depth) == 0:\n                depth = tf.zeros((*depth_resize_size.get(name, (1, 1)), 1), dtype=tf.float32)\n            else:\n                depth = tf.io.decode_image(depth, expand_animations=False, dtype=tf.float32)[..., 0]\n        elif depth.dtype != tf.float32:\n            raise ValueError(f\"Unsupported depth dtype: found depth_{name} with dtype {depth.dtype}\")\n\n        if name in depth_resize_size:\n            depth = dl.transforms.resize_depth_image(depth, size=depth_resize_size[name])\n\n        obs[f\"depth_{name}\"] = depth\n\n    return obs\n\n\ndef chunk_act_obs(traj: Dict, window_size: int, future_action_window_size: int = 0) -> Dict:\n    \"\"\"\n    Chunks actions and observations into the given window_size.\n\n    \"observation\" keys are given a new axis (at index 1) of size `window_size` containing `window_size - 1`\n    observations from the past and the current observation. \"action\" is given a new axis (at index 1) of size\n    `window_size + future_action_window_size` containing `window_size - 1` actions from the past, the current\n    action, and `future_action_window_size` actions from the future. \"pad_mask\" is added to \"observation\" and\n    indicates whether an observation should be considered padding (i.e. if it had come from a timestep\n    before the start of the trajectory).\n    \"\"\"\n    traj_len = tf.shape(traj[\"action\"])[0]\n    action_dim = traj[\"action\"].shape[-1]\n    chunk_indices = tf.broadcast_to(tf.range(-window_size + 1, 1), [traj_len, window_size]) + tf.broadcast_to(\n        tf.range(traj_len)[:, None], [traj_len, window_size]\n    )\n\n    action_chunk_indices = tf.broadcast_to(\n        tf.range(-window_size + 1, 1 + future_action_window_size),\n        [traj_len, window_size + future_action_window_size],\n    ) + tf.broadcast_to(\n        tf.range(traj_len)[:, None],\n        [traj_len, window_size + future_action_window_size],\n    )\n\n    floored_chunk_indices = tf.maximum(chunk_indices, 0)\n\n    if \"timestep\" in traj[\"task\"]:\n        goal_timestep = traj[\"task\"][\"timestep\"]\n    else:\n        goal_timestep = tf.fill([traj_len], traj_len - 1)\n\n    floored_action_chunk_indices = tf.minimum(tf.maximum(action_chunk_indices, 0), goal_timestep[:, None])\n\n    traj[\"observation\"] = tf.nest.map_structure(lambda x: tf.gather(x, floored_chunk_indices), traj[\"observation\"])\n    traj[\"action\"] = tf.gather(traj[\"action\"], floored_action_chunk_indices)\n\n    # indicates whether an entire observation is padding\n    traj[\"observation\"][\"pad_mask\"] = chunk_indices >= 0\n\n    # if no absolute_action_mask was provided, assume all actions are relative\n    if \"absolute_action_mask\" not in traj and future_action_window_size > 0:\n        logging.warning(\n            \"future_action_window_size > 0 but no absolute_action_mask was provided. \"\n            \"Assuming all actions are relative for the purpose of making neutral actions.\"\n        )\n    absolute_action_mask = traj.get(\"absolute_action_mask\", tf.zeros([traj_len, action_dim], dtype=tf.bool))\n    neutral_actions = tf.where(\n        absolute_action_mask[:, None, :],\n        traj[\"action\"],  # absolute actions are repeated (already done during chunking)\n        tf.zeros_like(traj[\"action\"]),  # relative actions are zeroed\n    )\n\n    # actions past the goal timestep become neutral\n    action_past_goal = action_chunk_indices > goal_timestep[:, None]\n    traj[\"action\"] = tf.where(action_past_goal[:, :, None], neutral_actions, traj[\"action\"])\n\n    return traj\n\n\ndef subsample(traj: Dict, subsample_length: int) -> Dict:\n    \"\"\"Subsamples trajectories to the given length.\"\"\"\n    traj_len = tf.shape(traj[\"action\"])[0]\n    if traj_len > subsample_length:\n        indices = tf.random.shuffle(tf.range(traj_len))[:subsample_length]\n        traj = tf.nest.map_structure(lambda x: tf.gather(x, indices), traj)\n\n    return traj\n\n\ndef add_pad_mask_dict(traj: Dict) -> Dict:\n    \"\"\"\n    Adds a dictionary indicating which elements of the observation/task should be treated as padding.\n        =>> traj[\"observation\"|\"task\"][\"pad_mask_dict\"] = {k: traj[\"observation\"|\"task\"][k] is not padding}\n    \"\"\"\n    traj_len = tf.shape(traj[\"action\"])[0]\n\n    for key in [\"observation\", \"task\"]:\n        pad_mask_dict = {}\n        for subkey in traj[key]:\n            # Handles \"language_instruction\", \"image_*\", and \"depth_*\"\n            if traj[key][subkey].dtype == tf.string:\n                pad_mask_dict[subkey] = tf.strings.length(traj[key][subkey]) != 0\n\n            # All other keys should not be treated as padding\n            else:\n                pad_mask_dict[subkey] = tf.ones([traj_len], dtype=tf.bool)\n\n        traj[key][\"pad_mask_dict\"] = pad_mask_dict\n\n    return traj\n"
  },
  {
    "path": "train/datasets/dataset.py",
    "content": "\"\"\"\ndatasets.py\n\nLightweight PyTorch Dataset Definition for wrapping RLDS TFDS Pipeline; just defines transform from RLDS default\nformat to OpenVLA, IterableDataset shim.\n\"\"\"\nimport copy\nfrom dataclasses import dataclass\nfrom pathlib import Path\nfrom typing import Any, Dict, Tuple, Union, List, Optional, Callable\nfrom copy import deepcopy\nimport inspect\nimport numpy as np\nimport torch\nimport dlimp as dl\nfrom functools import partial\nfrom PIL import Image\nfrom model import DinoSigLIPImageTransform\nfrom torch.utils.data import Dataset, IterableDataset\nfrom transformers import PreTrainedTokenizerBase\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nimport json\nfrom model.action_tokenizer import ActionTokenizer\nfrom datasets.data_utils import (\n    NormalizationType,\n    tree_map, \n    allocate_threads,\n    get_dataset_statistics,\n    normalize_action_and_proprio,\n    pprint_data_mixture,\n    decode_and_resize,\n    add_pad_mask_dict,\n    chunk_act_obs,\n    subsample,\n)\n\nfrom model.prompt_llama2 import LLaMa2ChatPromptBuilder\nfrom model.overwatch import initialize_overwatch\n\noverwatch = initialize_overwatch(__name__)\n\n# HuggingFace Default / LLaMa-2 IGNORE_INDEX (for labels)\nIGNORE_INDEX = -100\n\n\ndef make_dataset_from_rlds(\n    name: str,\n    data_dir: str,\n    *,\n    train: bool,\n    standardize_fn: Optional[Callable[[dict], dict]] = None,\n    shuffle: bool = True,\n    image_obs_keys: Dict[str, Optional[str]] = {},\n    depth_obs_keys: Dict[str, Optional[str]] = {},\n    state_obs_keys: List[Optional[str]] = (),\n    language_key: Optional[str] = None,\n    action_proprio_normalization_type: NormalizationType = NormalizationType.BOUNDS_Q99,\n    dataset_statistics: Optional[Union[dict, str]] = None,\n    absolute_action_mask: Optional[List[bool]] = None,\n    action_normalization_mask: Optional[List[bool]] = None,\n    num_parallel_reads: int = 1,\n    num_parallel_calls: int = 1,\n) -> Tuple[dl.DLataset, dict]:\n    \"\"\"\n    This function is responsible for loading a specific RLDS dataset from storage and getting it into a standardized\n    format. Yields a dataset of trajectories. Does not include CPU-intensive operations.\n\n    If `standardize_fn` is provided, it will be applied to each trajectory. This function should get the trajectory\n    into a standard format, which includes the keys \"observation\" and \"action\". Entry \"observation\" should be a\n    dictionary containing some number of additional keys, which will be extracted into an even more standardized format\n    according to the \"*_obs_keys\" arguments.\n\n    The `image_obs_keys` and `depth_obs_keys` arguments are mappings from new names to old names, or None in place of an\n    old name to insert padding. For example, if after `standardize_fn`, your \"observation\" dict has RGB images called\n    \"workspace\" and \"wrist\", and `image_obs_keys={\"primary\": \"workspace\", \"secondary\": None, \"wrist\": \"wrist\"}`, then\n    the resulting dataset will have an \"observation\" dict containing the keys \"image_primary\", \"image_secondary\", and\n    \"image_wrist\", where \"image_primary\" corresponds to \"workspace\", \"image_secondary\" is a padding image, and\n    \"image_wrist\" corresponds to \"wrist\".\n\n    Entry `state_obs_keys` is a list of 1-dimensional proprioceptive keys to concatenate into a single array, which will\n    be placed in the \"proprio\" key of the \"observation\" dict. A single padding element (zero) will be inserted for each\n    None entry.\n\n    The dataset will also include a \"task\" dict. If `language_key` is provided, then the \"task\" dict will contain the\n    key \"language_instruction\", extracted from `traj[language_key]`.\n\n    Args:\n        name (str): The name of the RLDS dataset (usually \"name\" or \"name:version\").\n        data_dir (str): The path to the data directory.\n        train (bool): Whether to use the training or validation split.\n        shuffle (bool, optional): Whether to shuffle the file read order (does NOT fully shuffle the dataset, since one\n            file usually contains many trajectories)!\n        standardize_fn (Callable[[dict], dict], optional): A function that, if provided, will be the first\n            thing applied to each trajectory.\n        image_obs_keys (Mapping[str, str|None]): Mapping from {new: old} indicating which RGB images to extract from the\n            \"observation\" dict. `new_obs = {f\"image_{new}\": old_obs[old] for new, old in image_obs_keys.items()}`.\n            If a value of `old` is None, inserts a padding image instead (empty string).\n        depth_obs_keys (Mapping[str, str|None]): Same as `image_obs_keys`, but for depth images. Keys will be\n            prefixed with \"depth_\" instead of \"image_\".\n        state_obs_keys (Sequence[str|None]): List of 1-dimensional proprioception keys to be extracted from the\n            \"observation\" dict, concatenated, and mapped to \"proprio\". Inserts 1 element of padding for each None entry.\n        language_key (str, optional): If provided, the \"task\" dict will contain the key \"language_instruction\",\n            extracted from `traj[language_key]`.\n        action_proprio_normalization_type (str, optional): The type of normalization to perform on the action,\n            proprio, or both. Can be \"normal\" (mean 0, std 1) or \"bounds\" (normalized to [-1, 1]).\n        dataset_statistics: (dict|str, optional): dict (or path to JSON file) that contains dataset statistics\n            for normalization. If `action_proprio_normalization_type` is \"normal\", this should contain \"mean\" and\n            \"std\" keys. If `action_proprio_normalization_type` is \"bounds\", this should contain \"min\" and \"max\"\n            keys. May also provide \"num_transitions\" and \"num_trajectories\" keys for downstream usage (e.g., for\n            `make_interleaved_dataset`). If not provided, the statistics will be computed on the fly.\n        absolute_action_mask (Sequence[bool], optional): By default, all action dimensions are assumed to be\n            relative. This is important for when `future_action_window_size > 0`: actions that are taken\n            from beyond the end of the trajectory need to be made \"neutral\" to indicate that the task has been completed.\n            For relative actions, \"neutral\" means zero, but for absolute actions, \"neutral\" means repeating the last valid action.\n            This mask, if provided, indicates which action dimensions are absolute.\n        action_normalization_mask (Sequence[bool], optional): If provided, indicates which action dimensions\n            should be normalized. For example, you might not want to normalize the gripper action dimension if\n            it's always exactly 0 or 1. By default, all action dimensions are normalized.\n        num_parallel_reads (int): number of parallel read workers. Default to AUTOTUNE.\n        num_parallel_calls (int): number of parallel calls for traj_map operations. Default to AUTOTUNE.\n    Returns:\n        Dataset of trajectories where each step has the following fields:\n        - observation:\n            - image_{name1, name2, ...} # RGB image observations\n            - depth_{name1, name2, ...} # depth image observations\n            - proprio                   # 1-dimensional array of proprioceptive observations\n            - timestep                  # timestep of each frame\n        - task:\n            - language_instruction      # language instruction, present if `language_key` is provided\n        - action                        # action vector\n        - dataset_name                  # name of the dataset\n    \"\"\"\n    REQUIRED_KEYS = {\"observation\", \"action\"}\n    if language_key is not None:\n        REQUIRED_KEYS.add(language_key)\n\n    def restructure(traj):\n        # apply a standardization function, if provided\n        if standardize_fn is not None:\n            traj = standardize_fn(traj)\n\n        if not all(k in traj for k in REQUIRED_KEYS):\n            raise ValueError(\n                f\"Trajectory is missing keys: {REQUIRED_KEYS - set(traj.keys())}. \" \"Did you write a `standardize_fn`?\"\n            )\n\n        # extracts images, depth images and proprio from the \"observation\" dict\n        traj_len = tf.shape(traj[\"action\"])[0]\n        old_obs = traj[\"observation\"]\n        new_obs = {}\n        for new, old in image_obs_keys.items():  # image_\n            if old is None:\n                new_obs[f\"image_{new}\"] = tf.repeat(\"\", traj_len)  # padding\n            else:\n                new_obs[f\"image_{new}\"] = old_obs[old]\n\n        for new, old in depth_obs_keys.items():\n            if old is None:\n                new_obs[f\"depth_{new}\"] = tf.repeat(\"\", traj_len)  # padding\n            else:\n                new_obs[f\"depth_{new}\"] = old_obs[old]\n\n        if state_obs_keys:\n            new_obs[\"proprio\"] = tf.concat(\n                [\n                    (\n                        tf.zeros((traj_len, 1), dtype=tf.float32)  # padding\n                        if key is None\n                        else tf.cast(old_obs[key], tf.float32)\n                    )\n                    for key in state_obs_keys\n                ],\n                axis=1,\n            )\n        # add timestep info\n        new_obs[\"timestep\"] = tf.range(traj_len)\n\n        # extracts `language_key` into the \"task\" dict\n        task = {}\n        if language_key is not None:\n            if traj[language_key].dtype != tf.string:\n                raise ValueError(\n                    f\"Language key {language_key} has dtype {traj[language_key].dtype}, \" \"but it must be tf.string.\"\n                )\n            task[\"language_instruction\"] = traj.pop(language_key)\n            \n        traj = {\n            \"observation\": new_obs,\n            \"task\": task,\n            #\"history\": traj[\"history\"],\n            \"action\": tf.cast(traj[\"action\"], tf.float32),\n            \"dataset_name\": tf.repeat(name, traj_len),\n        }\n        \n        if absolute_action_mask is not None:\n            if len(absolute_action_mask) != traj[\"action\"].shape[-1]:\n                raise ValueError(\n                    f\"Length of absolute_action_mask ({len(absolute_action_mask)}) \"\n                    f\"does not match action dimension ({traj['action'].shape[-1]}).\"\n                )\n            traj[\"absolute_action_mask\"] = tf.tile(\n                tf.convert_to_tensor(absolute_action_mask, dtype=tf.bool)[None],\n                [traj_len, 1],\n            )\n\n        return traj\n\n    builder = tfds.builder(name, data_dir=data_dir)\n    # load or compute dataset statistics\n    if isinstance(dataset_statistics, str):\n        with tf.io.gfile.GFile(dataset_statistics, \"r\") as f:\n            dataset_statistics = json.load(f)\n    elif dataset_statistics is None:\n        full_dataset = dl.DLataset.from_rlds(\n            builder, split=\"all\", shuffle=False, num_parallel_reads=1\n        )\n\n        full_dataset = full_dataset.traj_map(restructure, 1)\n        \n        # tries to load from cache, otherwise computes on the fly\n        dataset_statistics = get_dataset_statistics(\n            full_dataset,\n            hash_dependencies=(\n                str(builder.info),\n                str(state_obs_keys),\n                inspect.getsource(standardize_fn) if standardize_fn is not None else \"\",\n            ),\n            save_dir=builder.data_dir,\n        )\n    dataset_statistics = tree_map(np.array, dataset_statistics)\n\n    # skip normalization for certain action dimensions\n    if action_normalization_mask is not None:\n        if len(action_normalization_mask) != dataset_statistics[\"action\"][\"mean\"].shape[-1]:\n            raise ValueError(\n                f\"Length of skip_normalization_mask ({len(action_normalization_mask)}) \"\n                f\"does not match action dimension ({dataset_statistics['action']['mean'].shape[-1]}).\"\n            )\n        dataset_statistics[\"action\"][\"mask\"] = np.array(action_normalization_mask)\n\n    # construct the dataset\n    if \"val\" not in builder.info.splits:\n        split = \"train[:95%]\" if train else \"train[95%:]\"\n    else:\n        split = \"train\" if train else \"val\"\n\n    dataset = dl.DLataset.from_rlds(builder, split=split, shuffle=shuffle, num_parallel_reads=num_parallel_reads)\n\n    dataset = dataset.traj_map(restructure, num_parallel_calls)\n    \n    dataset = dataset.traj_map(\n        partial(\n            normalize_action_and_proprio,\n            metadata=dataset_statistics,\n            normalization_type=action_proprio_normalization_type,\n        ),\n        num_parallel_calls,\n    )\n\n    return dataset, dataset_statistics\n\n\ndef apply_trajectory_transforms(\n    dataset: dl.DLataset,\n    *,\n    train: bool,\n    goal_relabeling_strategy: Optional[str] = None,\n    goal_relabeling_kwargs: dict = {},\n    window_size: int = 1,\n    future_action_window_size: int = 0,\n    subsample_length: Optional[int] = None,\n    skip_unlabeled: bool = False,\n    max_action: Optional[float] = None,\n    max_proprio: Optional[float] = None,\n    task_augment_strategy: Optional[str] = None,\n    task_augment_kwargs: dict = {},\n    num_parallel_calls: int = tf.data.AUTOTUNE,\n) -> dl.DLataset:\n    \"\"\"\n    Applies common transforms that happen at a trajectory level. Such transforms are usually some sort of \"relabeling\"\n    (e.g., filtering, chunking, adding goals, dropping keys).\n\n    Transforms in this function should have the following properties:\n        - They require access to an entire trajectory (i.e., they cannot be applied frame-wise).\n        - They are generally not CPU-intensive, mostly involving moving and copying data.\n        - They do not require decoded images.\n\n    Args:\n        dataset (dl.DLataset): The dataset to transform.\n        train (bool): Whether the dataset is for training (affects subsampling).\n        goal_relabeling_strategy (str, optional): The goal relabeling strategy to use, or None for\n            no goal relabeling. See `goal_relabeling.py`.\n        goal_relabeling_kwargs (dict, optional): Additional keyword arguments to pass to the goal relabeling function.\n        window_size (int, optional): The length of the snippets that trajectories are chunked into.\n        future_action_window_size (int, optional): The number of future actions beyond window_size to include\n            in the chunked actions.\n        subsample_length (int, optional): If provided, trajectories longer than this will be subsampled to\n            this length (after goal relabeling and chunking).\n        skip_unlabeled (bool, optional): Whether to skip trajectories with no language labels.\n        max_action: (float, optional): If provided, trajectories in which *any* action dimension\n            of *any* transition has an absolute value larger than this will be skipped.\n        max_proprio: (float, optional): If provided, trajectories in which *any* proprio dimension\n            of *any* transition has an absolute value larger than this will be skipped.\n        task_augment_strategy (str, optional): The task augmentation strategy to use, or None for no task\n            augmentation. See `task_augmentation.py`.\n        task_augment_kwargs (dict, optional): Additional keyword arguments to pass to the task augmentation\n            function.\n        num_parallel_calls (int, optional): number of parallel calls for map operations. Default to AUTOTUNE.\n    \"\"\"\n    if skip_unlabeled:\n        if \"language_instruction\" not in dataset.element_spec[\"task\"]:\n            raise ValueError(\"skip_unlabeled=True but dataset does not have language labels.\")\n\n        dataset = dataset.filter(lambda x: tf.math.reduce_any(x[\"task\"][\"language_instruction\"] != \"\"))\n\n    if max_action is not None:\n        dataset = dataset.filter(lambda x: tf.math.reduce_all(tf.math.abs(x[\"action\"]) <= max_action))\n\n    if max_proprio is not None and \"proprio\" in dataset.element_spec[\"observation\"]:\n        dataset = dataset.filter(lambda x: tf.math.reduce_all(tf.math.abs(x[\"observation\"][\"proprio\"]) <= max_proprio))\n\n    # marks which entires of the observation and task dicts are padding\n    dataset = dataset.traj_map(add_pad_mask_dict, num_parallel_calls)\n\n    # updates the \"task\" dict\n    if goal_relabeling_strategy is not None:\n        dataset = dataset.traj_map(\n            partial(getattr(goal_relabeling, goal_relabeling_strategy), **goal_relabeling_kwargs),\n            num_parallel_calls,\n        )\n\n    # must run task augmentation before chunking, in case it changes goal timesteps\n    if train and task_augment_strategy is not None:\n        # perform task augmentation (e.g., dropping keys)\n        dataset = dataset.traj_map(\n            partial(\n                getattr(task_augmentation, task_augment_strategy),\n                **task_augment_kwargs,\n            ),\n            num_parallel_calls,\n        )\n\n    # chunks observations and actions, giving them a new axis at index 1 of size `window_size` and\n    # `window_size + future_action_window_size`, respectively\n    dataset = dataset.traj_map(\n        partial(\n            chunk_act_obs,\n            window_size=window_size,\n            future_action_window_size=future_action_window_size,\n        ),\n        num_parallel_calls,\n    )\n\n    if train and subsample_length is not None:\n        dataset = dataset.traj_map(\n            partial(subsample, subsample_length=subsample_length),\n            num_parallel_calls,\n        )\n\n    return dataset\n\ndef apply_per_dataset_frame_transforms(\n    dataset: dl.DLataset,\n    chunk_filter_fn: Optional[Callable] = None,\n):\n    \"\"\"\n    Optionally applied *per-dataset* transforms that happen at a frame level.\n\n    Args:\n        chunk_filter_fn (callable, optional): Filter function for chunks.\n    \"\"\"\n    if chunk_filter_fn:\n        dataset = dataset.filter(chunk_filter_fn)\n    return dataset\n    \ndef apply_frame_transforms(\n    dataset: dl.DLataset,\n    *,\n    train: bool,\n    image_augment_kwargs: Union[Dict, Dict[str, Dict]] = {},\n    resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]] = {},\n    depth_resize_size: Union[Tuple[int, int], Dict[str, Tuple[int, int]]] = {},\n    num_parallel_calls: int = tf.data.AUTOTUNE,\n) -> dl.DLataset:\n    \"\"\"\n    Applies common transforms that happen at a frame level. These transforms are usually more CPU-intensive, (e.g.,\n    decoding or resizing images).\n\n    Args:\n        train (bool): Whether the dataset is for training (affects image augmentation).\n        dataset (dl.DLataset): The dataset to transform.\n        image_augment_kwargs (dict|Mapping[str, dict]): Keyword arguments to pass to the image augmentation\n            function. See `dlimp.transforms.augment_image` for documentation of these kwargs. If a dict of\n            dicts is provided, then key \"k\" will be used for \"image_{k}\" (names determined by `image_obs_keys`\n            in `make_dataset_from_rlds`). Augmentation will be skipped for missing keys (so pass an empty dict\n            to skip augmentation for all images).\n        resize_size (Tuple[int, int]|Mapping[str, Tuple[int, int]]): If provided, images will be resized to\n            this size. If a dict of tuples is provided, then key \"k\" will be used for \"image_{k}\" (names\n            determined by `image_obs_keys` in `make_dataset_from_rlds`). Resizing will be skipped for missing\n            keys (so pass an empty dict to skip resizing for all images).\n        depth_resize_size (Tuple[int, int]|Mapping[str, Tuple[int, int]]): Same as resize_size, but for depth\n            images.\n        num_parallel_calls (int): number of parallel calls for frame_map operations. Default to AUTOTUNE.\n    \"\"\"\n\n    # Convenience wrapper that takes a function that operates on a non-chunked \"observation\" dict and applies\n    # it to the chunked \"observation\" dict as well as the non-chunked \"task\" dict\n    def apply_obs_transform(fn: Callable[[Dict], Dict], frame: Dict) -> Dict:\n        frame[\"task\"] = fn(frame[\"task\"])\n        frame[\"observation\"] = dl.vmap(fn)(frame[\"observation\"])\n        return frame\n\n    # Decode + resize images (and depth images)\n    dataset = dataset.frame_map(\n        partial(\n            apply_obs_transform,\n            partial(decode_and_resize, resize_size=resize_size, depth_resize_size=depth_resize_size),\n        ),\n        num_parallel_calls,\n    )\n\n    return dataset\n# === Core Initializer ===\ndef make_interleaved_dataset(\n    dataset_kwargs_list: List[Dict],\n    sample_weights: Optional[List[float]] = None,\n    *,\n    train: bool,\n    shuffle_buffer_size: int,\n    traj_transform_kwargs: Optional[Dict] = None,\n    frame_transform_kwargs: Optional[Dict] = None,\n    batch_size: Optional[int] = None,\n    balance_weights: bool = False,\n    traj_transform_threads: Optional[int] = None,\n    traj_read_threads: Optional[int] = None,\n) -> dl.DLataset:\n    \"\"\"\n    Creates an interleaved dataset from list of dataset configs (kwargs). Returns a dataset of batched frames.\n\n    Args:\n        dataset_kwargs_list: list of kwargs, each element of which is passed to `make_dataset_from_rlds`.\n            \"num_parallel_calls\" and \"num_parallel_reads\" are overridden using `traj_transform_threads` and\n            `traj_read_threads`, respectively.\n        sample_weights: sampling weights for each dataset in list. If None, defaults to uniform.\n        train: whether this is a training or validation dataset.\n        shuffle_buffer_size: size of the dataset shuffle buffer (in number of frames).\n        traj_transform_kwargs: kwargs passed to `apply_trajectory_transforms`. \"num_parallel_calls\" is\n            overridden using `traj_transform_threads`.\n        frame_transform_kwargs: kwargs passed to `apply_frame_transforms`.\n        batch_size: batch size, if not provided output is not batched.\n        balance_weights: if True, the sample weights are multiplied by the number of frames in each dataset.\n            This makes it so that, if all the sample weights are equal, one full iteration through the interleaved\n            dataset will correspond to one full iteration through each individual dataset (only in expectation,\n            since in practice the sampling is random).\n        traj_transform_threads: total number of parallel calls for trajectory transforms, distributed across\n            datasets according to their sampling weights. If None, defaults to AUTOTUNE for every dataset.\n        traj_read_threads: total number of parallel read workers for trajectory transforms, distributed across\n            datasets according to their sampling weights. If None, defaults to AUTOTUNE for every dataset.\n    \"\"\"\n    # Default to uniform sampling (if `sample_weights` is not specified)\n    if not sample_weights:\n        sample_weights = [1.0] * len(dataset_kwargs_list)\n\n    if len(sample_weights) != len(dataset_kwargs_list):\n        raise ValueError(f\"sample_weights must be None or have length {len(dataset_kwargs_list)}.\")\n\n    # Check valid `traj_transform_kwargs` and `frame_transform_kwargs`\n    if (traj_transform_kwargs is None) or (frame_transform_kwargs is None):\n        raise ValueError(\"Missing `traj_transform_kwargs` and `frame_transform_kwargs`!\")\n\n    # Get Dataset Sizes\n    dataset_sizes, all_dataset_statistics = [], {}\n    for dataset_kwargs in dataset_kwargs_list:\n        data_kwargs = copy.deepcopy(dataset_kwargs)\n        if \"dataset_frame_transform_kwargs\" in data_kwargs:\n            data_kwargs.pop(\"dataset_frame_transform_kwargs\")\n        _, dataset_statistics = make_dataset_from_rlds(**data_kwargs, train=train)\n        dataset_sizes.append(dataset_statistics[\"num_transitions\"])\n        all_dataset_statistics[dataset_kwargs[\"name\"]] = dataset_statistics\n\n    # Get the indices of the \"primary\" datasets (i.e., datasets with sample_weight == 1.0)\n    primary_dataset_indices = np.array([idx for idx in range(len(sample_weights)) if sample_weights[idx] == 1.0])\n\n    # Balance and Normalize Weights\n    if balance_weights:\n        sample_weights = np.array(sample_weights) * np.array(dataset_sizes)\n    sample_weights = np.array(sample_weights) / np.sum(sample_weights)\n    pprint_data_mixture(dataset_kwargs_list, sample_weights)\n\n    # Effective Dataset Length = Number of samples until each dataset has completed at least one epoch\n    #   =>> Note :: Only counting the \"primary\" datasets (i.e., datasets with sample_weight == 1.0)\n    dataset_len = int((np.array(dataset_sizes) / sample_weights)[primary_dataset_indices].max())\n\n    # Allocate Threads based on Weights\n    threads_per_dataset = allocate_threads(traj_transform_threads, sample_weights)\n    reads_per_dataset = allocate_threads(traj_read_threads, sample_weights)\n\n    overwatch.info(\"Threads per Dataset: %s\", threads_per_dataset)\n    overwatch.info(\"Reads per Dataset: %s\", reads_per_dataset)\n\n    # Construct Datasets\n    overwatch.info(\"Constructing datasets...\")\n    datasets = []\n    for dataset_kwargs, threads, reads in zip(\n        dataset_kwargs_list,\n        threads_per_dataset,\n        reads_per_dataset,\n    ):\n        dataset_frame_transform_kwargs = (\n            dataset_kwargs.pop(\"dataset_frame_transform_kwargs\")\n            if \"dataset_frame_transform_kwargs\" in dataset_kwargs\n            else {}\n        )\n        dataset, _ = make_dataset_from_rlds(\n            **dataset_kwargs,\n            train=train,\n            num_parallel_calls=threads,\n            num_parallel_reads=reads,\n            dataset_statistics=all_dataset_statistics[dataset_kwargs[\"name\"]],\n        )\n        dataset = apply_trajectory_transforms(\n            dataset.repeat(),\n            **traj_transform_kwargs,\n            num_parallel_calls=threads,\n            train=train,\n        ).flatten(num_parallel_calls=threads)\n        dataset = apply_per_dataset_frame_transforms(dataset, **dataset_frame_transform_kwargs)\n        datasets.append(dataset)\n\n    # Interleave at the Frame Level\n    dataset: dl.DLataset = dl.DLataset.sample_from_datasets(datasets, sample_weights)\n\n    # Validation =>> fix a single shuffle buffer of data and cache it in RAM; prevents gradual memory increase!\n    if not train:\n        dataset = dataset.take(shuffle_buffer_size).cache()\n\n    # Shuffle the Dataset\n    #   =>> IMPORTANT :: Shuffle AFTER .cache(), or else memory will still leak!\n    dataset = dataset.shuffle(shuffle_buffer_size)\n\n    # Apply Frame Transforms\n    overwatch.info(\"Applying frame transforms on dataset...\")\n    dataset = apply_frame_transforms(dataset, **frame_transform_kwargs, train=train)\n\n    # [Contract] When training VLA Policies, we let the Collator handle Batching!\n    if batch_size is not None:\n        dataset = dataset.batch(batch_size)\n\n    # Note =>> Seems to reduce memory usage without affecting speed?\n    dataset = dataset.with_ram_budget(1)\n\n    # Save for Later\n    dataset.sample_weights = sample_weights\n\n    return dataset, dataset_len, all_dataset_statistics\n\ndef vln_transform(trajectory: Dict[str, Any]) -> Dict[str, Any]:\n    \"\"\"\n    Applies to original version of Bridge V2 from the official project website.\n\n    Note =>> In original Bridge V2 dataset, the first timestep has an all-zero action, so we remove it!\n    \"\"\"\n   \n    for key in trajectory.keys():\n        if key == \"traj_metadata\":\n            continue\n        elif key == \"observation\":\n            for key2 in trajectory[key]:\n                trajectory[key][key2] = trajectory[key][key2][1:]\n        else:\n            trajectory[key] = trajectory[key][1:]\n\n    trajectory[\"action\"] = tf.concat(\n        [\n            trajectory[\"action\"][:, :3],\n            trajectory[\"action\"][:, 3:],\n        ],\n        axis=1,\n    )\n    return trajectory\n\ndef make_oxe_dataset_kwargs(\n    dataset_name: str,\n    data_root_dir: Path,\n    load_camera_views: Tuple[str] = (\"primary\",),\n    load_depth: bool = False,\n    load_proprio: bool = True,\n    load_language: bool = True,\n    action_proprio_normalization_type: NormalizationType = NormalizationType.NORMAL,\n) -> Dict[str, Any]:\n    \"\"\"Generates config (kwargs) for given dataset from Open-X Embodiment.\"\"\"\n    dataset_kwargs = {\n        \"image_obs_keys\": {\"primary\": \"image_1\", \"secondary\": \"image_2\", \"wrist\": \"image_3\",},\n        \"depth_obs_keys\": {\"primary\": None, \"secondary\": None, \"wrist\": None},\n        \"state_obs_keys\": [\"base_pose_tool_reached\", \"gripper_closed\"],\n        \"state_encoding\": 2,\n        \"action_encoding\": 5,\n    }\n    dataset_kwargs[\"action_proprio_normalization_type\"] = action_proprio_normalization_type\n\n    # Adjust Loaded Camera Views\n    if len(missing_keys := (set(load_camera_views) - set(dataset_kwargs[\"image_obs_keys\"]))) > 0:\n        raise ValueError(f\"Cannot load `{dataset_name}`; missing camera views `{missing_keys}`\")\n\n    # Filter\n    dataset_kwargs[\"image_obs_keys\"] = {\n        k: v for k, v in dataset_kwargs[\"image_obs_keys\"].items() if k in load_camera_views\n    }\n    dataset_kwargs[\"depth_obs_keys\"] = {\n        k: v for k, v in dataset_kwargs[\"depth_obs_keys\"].items() if k in load_camera_views\n    }\n\n    # Eliminate Unnecessary Keys\n    dataset_kwargs.pop(\"state_encoding\")\n    dataset_kwargs.pop(\"action_encoding\")\n    if not load_depth:\n        dataset_kwargs.pop(\"depth_obs_keys\")\n    if not load_proprio:\n        dataset_kwargs.pop(\"state_obs_keys\")\n\n    # Load Language\n    if load_language:\n        dataset_kwargs[\"language_key\"] = \"language_instruction\"\n\n    # Specify Standardization Transform\n    dataset_kwargs[\"standardize_fn\"] = vln_transform\n\n    # Add any aux arguments\n    if \"aux_kwargs\" in dataset_kwargs:\n        dataset_kwargs.update(dataset_kwargs.pop(\"aux_kwargs\"))\n\n    return {\"name\": dataset_name, \"data_dir\": str(data_root_dir), **dataset_kwargs}\n\n\ndef get_oxe_dataset_kwargs_and_weights(\n    data_root_dir: Path,\n    mixture_spec: List[Tuple[str, float]],\n    load_camera_views: Tuple[str] = (\"primary\",),\n    load_depth: bool = False,\n    load_proprio: bool = True,\n    load_language: bool = True,\n    action_proprio_normalization_type: NormalizationType = NormalizationType.NORMAL,\n) -> Tuple[Dict[str, Any], List[float]]:\n    \"\"\"\n    Generates dataset kwargs for a given dataset mix from the Open X-Embodiment dataset. The returned kwargs\n    (per-dataset configs) and weights can be passed directly to `make_interleaved_dataset`.\n\n    :param data_root_dir: Base directory containing RLDS/TFDS-formatted datasets (from Open-X)\n    :param mixture_spec: List of (dataset_name, sampling_weight) from `oxe.mixtures.OXE_NAMED_MIXTURES`\n    :param load_camera_views: Camera views to load; see `oxe.dataset_configs.py` for available views.\n    :param load_depth: Load depth information in addition to camera RGB.\n    :param load_proprio: Load proprioceptive state.\n    :param load_language: Load language instructions.\n    :param action_proprio_normalization_type: Normalization scheme to use for proprioceptive actions.\n\n    return: Tuple of (per_dataset_kwargs, sampling_weights)\n    \"\"\"\n    included_datasets, filtered_mixture_spec = set(), []\n    for d_name, d_weight in mixture_spec:\n        if d_name in included_datasets:\n            overwatch.warning(f\"Skipping Duplicate Dataset: `{(d_name, d_weight)}`\")\n            continue\n\n        included_datasets.add(d_name)\n        filtered_mixture_spec.append((d_name, d_weight))\n\n    # Assemble Dataset Config (kwargs) and Weights\n    per_dataset_kwargs, sampling_weights = [], []\n    for d_name, d_weight in filtered_mixture_spec:\n        try:\n            per_dataset_kwargs.append(\n                make_oxe_dataset_kwargs(\n                    d_name,\n                    data_root_dir,\n                    load_camera_views,\n                    load_depth,\n                    load_proprio,\n                    load_language,\n                    action_proprio_normalization_type,\n                )\n            )\n            sampling_weights.append(d_weight)\n\n        except ValueError as e:\n            overwatch.warning(f\"Skipping `{d_name}` due to Error: {e}\")\n\n    return per_dataset_kwargs, sampling_weights\n\n@dataclass\nclass RLDSBatchTransform:\n    action_tokenizer: ActionTokenizer\n    base_tokenizer: PreTrainedTokenizerBase\n    image_transform: DinoSigLIPImageTransform\n    predict_stop_token: bool = True\n\n    def __call__(self, rlds_batch: Dict[str, Any]) -> Dict[str, Any]:\n        \"\"\"Converts a RLDS batch to the format expected by the OpenVLA collator/models.\"\"\"\n        dataset_name, action = rlds_batch[\"dataset_name\"], rlds_batch[\"action\"][0]\n        img_cur = Image.fromarray(rlds_batch[\"observation\"][\"image_primary\"][0])\n        img_past1 = Image.fromarray(rlds_batch[\"observation\"][\"image_secondary\"][0])\n        img_past2 = Image.fromarray(rlds_batch[\"observation\"][\"image_wrist\"][0])\n        lang = rlds_batch[\"task\"][\"language_instruction\"].decode().lower()\n\n        # Construct Chat-based Prompt =>> Input is default query + language instruction, output are the action tokens\n        prompt_builder = LLaMa2ChatPromptBuilder(\"prismatic\")\n        conversation = [\n            {\"from\": \"human\", \"value\": f\"What action should the robot take to {lang}?\"},\n            {\"from\": \"gpt\", \"value\": self.action_tokenizer(action)},\n        ]\n        for turn in conversation:\n            prompt_builder.add_turn(turn[\"from\"], turn[\"value\"])\n\n        # Tokenize (w/ `base_tokenizer`)\n        input_ids = self.base_tokenizer(prompt_builder.get_prompt(), add_special_tokens=True).input_ids\n        labels = list(input_ids)\n\n        # Tensorize =>> Run Image Transform to get `pixel_values` =>> Return\n        #   =>> IMPORTANT :: IF WE'RE USING HF LLM.forward(..., labels=labels), SHIFTING HAPPENS _INSIDE_ MODEL!\n        input_ids, labels = torch.tensor(input_ids), torch.tensor(labels)\n\n        tr_img_cur, tr_pst1, tr_pst2 = self.image_transform(img_cur), self.image_transform(img_past1), self.image_transform(img_past2)# , self.image_transform(img_past3),\n        pixel_values = {\n                k: torch.cat(\n                      (tr_img_cur[k], tr_pst1[k], tr_pst2[k]), dim=0  # , tr_pst3[k]\n                )\n                for k in tr_img_cur.keys()\n            }\n            \n        # [CRITICAL] We do not want to take the loss for anything but the predicted action tokens!\n        labels[: -(len(action) + 1)] = IGNORE_INDEX\n        if not self.predict_stop_token:\n            labels[-1] = IGNORE_INDEX\n\n        return dict(pixel_values=pixel_values, input_ids=input_ids, labels=labels, dataset_name=dataset_name)\n\n\nclass RLDSDataset(IterableDataset):\n    def __init__(\n        self,\n        data_root_dir: Path,\n        data_mix: str,\n        batch_transform: RLDSBatchTransform,\n        resize_resolution: Tuple[int, int],\n        shuffle_buffer_size: int = 256_000,\n        train: bool = True,\n        image_aug: bool = False,\n    ) -> None:\n        \"\"\"Lightweight wrapper around RLDS TFDS Pipeline for use with PyTorch/OpenVLA Data Loaders.\"\"\"\n        self.data_root_dir, self.data_mix, self.batch_transform = data_root_dir, data_mix, batch_transform\n\n        OXE_NAMED_MIXTURES: Dict[str, List[Tuple[str, float]]] = {\n            \"vln_mix\" : [                                  \n                (\"vln_norm\", 1.0),                            \n            ],\n        }\n\n        mixture_spec = OXE_NAMED_MIXTURES[self.data_mix]\n\n        # fmt: off\n        per_dataset_kwargs, weights = get_oxe_dataset_kwargs_and_weights(\n            self.data_root_dir,\n            mixture_spec,\n            load_camera_views= (\"primary\", \"secondary\", \"wrist\",),\n            load_depth=False,\n            load_proprio=False,\n            load_language=True,\n            action_proprio_normalization_type=NormalizationType.BOUNDS_Q99,\n        )\n        rlds_config = dict(\n            traj_transform_kwargs=dict(\n                window_size=1,                                      # If we wanted to feed / predict more than one step\n                future_action_window_size=0,                        # For action chunking\n                skip_unlabeled=True,                                # Skip trajectories without language labels\n            ),\n            frame_transform_kwargs=dict(\n                resize_size=resize_resolution,\n                num_parallel_calls=16,                          # For CPU-intensive ops (decoding, resizing, etc.)\n            ),\n            dataset_kwargs_list=per_dataset_kwargs,\n            shuffle_buffer_size=shuffle_buffer_size,\n            sample_weights=weights,\n            balance_weights=True,\n            traj_transform_threads=len(mixture_spec),\n            traj_read_threads=len(mixture_spec),\n            train=train,\n        )\n\n        # If applicable, enable image augmentations\n        if image_aug:\n            rlds_config[\"frame_transform_kwargs\"].update({\"image_augment_kwargs\" : dict(\n                random_resized_crop=dict(scale=[0.9, 0.9], ratio=[1.0, 1.0]),\n                random_brightness=[0.2],\n                random_contrast=[0.8, 1.2],\n                random_saturation=[0.8, 1.2],\n                random_hue=[0.05],\n                augment_order=[\n                    \"random_resized_crop\",\n                    \"random_brightness\",\n                    \"random_contrast\",\n                    \"random_saturation\",\n                    \"random_hue\",\n                ],\n            )}),\n        # fmt: on\n\n        # Initialize RLDS Dataset\n        self.dataset, self.dataset_length, self.dataset_statistics = self.make_dataset(rlds_config)\n\n    def make_dataset(self, rlds_config):\n        return make_interleaved_dataset(**rlds_config)\n\n    def __iter__(self) -> Dict[str, Any]:\n        for rlds_batch in self.dataset.as_numpy_iterator():\n            yield self.batch_transform(rlds_batch)\n\n    def __len__(self) -> int:\n        return self.dataset_length\n\n    # === Explicitly Unused ===\n    def __getitem__(self, idx: int) -> None:\n        raise NotImplementedError(\"IterableDataset does not implement map-style __getitem__; see __iter__ instead!\")\n"
  },
  {
    "path": "train/eval.py",
    "content": "\nfrom unrealcv import Client  \nimport cv2  \nimport numpy as np  \nimport io\nimport time\nimport math\nimport subprocess, threading\nimport airsim\nfrom common import *\nimport psutil\nimport requests\nimport random\nimport numpy as np\nimport torch\nfrom PIL import Image\nfrom transformers import AutoConfig, AutoImageProcessor, AutoModelForVision2Seq, AutoProcessor\nimport os, json\nfrom extern.hf.configuration_prismatic import OpenFlyConfig\nfrom extern.hf.modeling_prismatic import OpenVLAForActionPrediction\nfrom extern.hf.processing_prismatic import PrismaticImageProcessor, PrismaticProcessor\n\n\nAutoConfig.register(\"openvla\", OpenFlyConfig)\nAutoImageProcessor.register(OpenFlyConfig, PrismaticImageProcessor)\nAutoProcessor.register(OpenFlyConfig, PrismaticProcessor)\nAutoModelForVision2Seq.register(OpenFlyConfig, OpenVLAForActionPrediction)\n\n\ndef kill_env_process(keyword):\n    result = subprocess.run(['pgrep', '-n', keyword], stdout=subprocess.PIPE)\n    cr_pid = result.stdout.decode().strip()\n    if len(cr_pid) > 0:\n        subprocess.run(['kill', '-9', cr_pid])\n\nclass AirsimBridge:\n    def __init__(self, env_name):\n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_airsim_sim)\n        self._sim_thread.start()\n        time.sleep(10)\n\n        self._client = airsim.MultirotorClient()\n        self._client.confirmConnection()\n        self._client.enableApiControl(True)\n        self._client.armDisarm(True)\n\n        self.distance_to_goal = []\n        self.spl = []\n        self.success = []\n        self.traj_len = 0\n        self.pass_len = 1e-3\n        self.osr = []\n\n    def _init_airsim_sim(self):\n        env_dir = \"envs/airsim/\" + self.env_name\n\n        if not os.path.exists(env_dir):\n            raise ValueError(f\"Specified directory {env_dir} does not exist\")\n        \n        command = [\"bash\", f\"{env_dir}/LinuxNoEditor/start.sh\"]\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        # print(\"Command output:\\n\", stdout)\n\n    def print_info(self):\n        print(f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\")\n        return f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\"\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll):\n        target_pose = airsim.Pose(airsim.Vector3r(x, -y, -z),\n                                  airsim.to_quaternion(math.radians(pitch), 0, math.radians(-yaw)))\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        self._client.simSetVehiclePose(target_pose, True)\n\n    def set_drone_pos(self, x, y, z, pitch, yaw, roll):\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        qua = euler_to_quaternion(pitch, -yaw, roll)\n        target_pose = airsim.Pose(airsim.Vector3r(x, y, z),\n                                  airsim.Quaternionr(qua[0], qua[1], qua[2], qua[3]))\n        self._client.simSetVehiclePose(target_pose, True)\n        self._client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.02)\n        time.sleep(0.1)\n\n    def _camera_init(self):\n        '''Camera initialization'''\n        camera_pose = airsim.Pose(airsim.Vector3r(0, 0, 0), airsim.to_quaternion(math.radians(15), 0, 0))\n        self._client.simSetCameraPose(\"0\", camera_pose)\n        time.sleep(1)\n\n    def _drone_init(self):\n        '''Drone initialization'''\n        self.set_drone_pos(0, 0, 0, 0, 0, 0)\n        time.sleep(1)\n\n    def get_camera_data(self, camera_type = 'color'):\n        valid_types = {'color', 'object_mask', 'depth'}\n        if camera_type not in valid_types:\n            raise ValueError(f\"Invalid camera type. Expected one of {valid_types}, but got '{camera_type}'.\")\n\n        if camera_type == 'color':\n            image_type = airsim.ImageType.Scene\n        elif camera_type == 'depth':\n            image_type = airsim.ImageType.DepthPlanar\n        else:\n            image_type = airsim.ImageType.Segmentation\n\n        responses = self._client.simGetImages([airsim.ImageRequest('front_custom', image_type, False, False)])\n        response = responses[0]\n        if response.pixels_as_float:\n            img_data = np.array(response.image_data_float, dtype=np.float32)\n            img_data = np.reshape(img_data, (response.height, response.width))\n        else:\n            img_data = np.frombuffer(response.image_data_uint8, dtype=np.uint8)\n            img_data = img_data.reshape(response.height, response.width, 3)\n\n        return img_data\n\n    def save_image(self, image_data, file_path):\n        cv2.imwrite(file_path, image_data)\n\n    def process_camera_data(self, file_path, camera_type='color'):\n        img = self.get_camera_data(camera_type)\n        self.save_image(img, file_path)\n        print(\"Image saved\")\n\nclass UEBridge:\n    def __init__(self, ue_ip, ue_port, env_name):\n        self.kill_failed_process()\n        time.sleep(10)\n\n        # port = self.find_available_port()\n\n        port = random.randint(9000, 9100)\n        print(f\"Available port: {port}\")\n        self.modify_port_in_ini(port, env_name)\n        ue_port = port\n\n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_ue_sim)\n        self._sim_thread.start()\n        time.sleep(15)\n\n        self._client = Client((ue_ip, ue_port))\n        self._connection_check()\n\n        self._camera_init()\n\n        # self._drone_init()  \n        self.distance_to_goal = []\n        self.spl = []\n        self.success = []\n        self.traj_len = 0\n        self.pass_len = 1e-3\n        self.osr = []\n\n    def print_info(self):\n        print(f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\")\n        return f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\"\n\n    def find_available_port(self):\n        port = 9000\n        while True:\n            result = subprocess.run(['lsof', f'-i:{port}'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n            netstat_output = result.stdout.decode()\n\n            if f'PID' not in netstat_output:\n                return port\n            port += 1\n\n    def modify_port_in_ini(self, port, ue_env_name):\n        ini_file = f\"envs/ue/{ue_env_name}/City_UE52/Binaries/Linux/unrealcv.ini\"\n        with open(ini_file, 'r') as file:\n            lines = file.readlines()\n\n        with open(ini_file, 'w') as file:\n            for line in lines:\n                if line.startswith(\"Port=\"):\n                    file.write(f\"Port={port}\\n\")\n                else:\n                    file.write(line)\n\n    def kill_failed_process(self):\n        result = subprocess.run(['pgrep', '-n', 'CrashReport'], stdout=subprocess.PIPE)\n        cr_pid = result.stdout.decode().strip()\n        if len(cr_pid) > 0:\n            subprocess.run(['kill', '-9', cr_pid])\n\n        result = subprocess.run(['pgrep', '-n', 'CitySample'], stdout=subprocess.PIPE)\n        cr_pid = result.stdout.decode().strip()\n        if len(cr_pid) > 0:\n            subprocess.run(['kill', '-9', cr_pid])\n\n    def _init_ue_sim(self):\n        env_dir = \"envs/ue/\" + self.env_name\n        if not os.path.exists(env_dir):\n            raise ValueError(f\"Specified directory {env_dir} does not exist\")\n\n        command = [\"bash\", f\"{env_dir}/CitySample.sh\"]\n\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        # print(\"Command output:\\n\", stdout)\n        time.sleep(2)\n\n    def __del__(self):\n        self._client.disconnect()\n\n    def _connection_check(self):\n        '''Check if connected'''\n        if self._client.connect():\n            print('UnrealCV connected successfully')\n        else:\n            print('UnrealCV is not connected')\n            exit()\n\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll):\n        '''Set camera position'''\n        x = x * 100\n        y = - y * 100\n        z = z * 100\n        camera_settings = {\n            'location': {'x': x, 'y': y, 'z': z},\n            'rotation': {'pitch': pitch, 'yaw': -yaw, 'roll': roll}\n        }\n\n        self._client.request('vset /camera/0/location {x} {y} {z}'.format(**camera_settings['location']))\n        self._client.request('vset /camera/1/location {x} {y} {z}'.format(**camera_settings['location']))\n        self._client.request('vset /camera/0/rotation {pitch} {yaw} {roll}'.format(**camera_settings['rotation']))\n        self._client.request('vset /camera/1/rotation {pitch} {yaw} {roll}'.format(**camera_settings['rotation']))\n        print('camera_settings', camera_settings)\n\n    def _camera_init(self):\n        '''Camera initialization'''\n        time.sleep(2)\n        self._client.request('vset /cameras/spawn')\n        self._client.request('vset /camera/1/size 1920 1080')\n        time.sleep(2)\n        self.set_camera_pose(150, 400, 15, 0, 0, 0)  # Initial position\n        time.sleep(2)\n\n    def get_camera_data(self, camera_type = 'lit'):\n        valid_types = {'lit', 'object_mask', 'depth'}\n        if camera_type not in valid_types:\n            raise ValueError(f\"Invalid camera type. Expected one of {valid_types}, but got '{camera_type}'.\")\n\n        if camera_type == 'lit':\n            data = self._client.request('vget /camera/1/lit png')\n            return cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)\n        elif camera_type == 'object_mask':\n            data = self._client.request('vget /camera/1/object_mask png')\n            return cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)\n        elif camera_type == 'depth':\n            data = self._client.request('vget /camera/1/depth npy')\n            depth_np = np.load(io.BytesIO(data))\n            return depth_np  # Return depth data\n\n    def save_image(self, image_data, file_path):\n        cv2.imwrite(file_path, image_data)\n\n    def process_camera_data(self, file_path, camera_type='lit'):\n        img = self.get_camera_data(camera_type)\n        self.save_image(img, file_path)\n\nclass GSBridge:  \n    def __init__(self, env_name):  \n        self.env_name = env_name\n        self._sim_thread = threading.Thread(target=self._init_gs_sim)\n        self._sim_thread.start()\n        self.url = \"http://localhost:18080/render\"\n        time.sleep(10)\n\n        self.distance_to_goal = []\n        self.spl = []\n        self.success = []\n        self.traj_len = 0\n        self.pass_len = 1e-3\n        self.osr = []\n\n    def print_info(self):\n        print(f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\")\n        return f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\"\n\n    def _init_gs_sim(self):\n        # dataset_dir = \"envs/gs/\" + self.env_name  \n        dataset_dir = \"/media/pjlabrl/hdd/all_files_relate_to_3dgs/reconstruction_result/nwpu02\"\n        gs_vis_tool_dir = \"envs/gs/SIBR_viewers/\"  \n        if not os.path.exists(dataset_dir):\n            raise ValueError(f\"Specified directory {dataset_dir} does not exist\")\n        command = [\n            gs_vis_tool_dir + \"install/bin/SIBR_gaussianHierarchyViewer_app\",\n            \"--path\", f\"{dataset_dir}/camera_calibration/aligned\",\n            \"--scaffold\", f\"{dataset_dir}/output/scaffold/point_cloud/iteration_30000\",\n            \"--model-path\", f\"{dataset_dir}/output/merged.hier\",\n            \"--images-path\", f\"{dataset_dir}/camera_calibration/rectified/images\"\n        ]\n        self.process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = self.process.communicate()\n        print(\"Command output:\\n\", stdout)\n\n    def transform_euler_to_new_frame(self, roll, pitch, yaw):\n        R = euler_to_rotation_matrix(roll, pitch, yaw)\n        transformation_matrix = np.array([\n            [0, -1, 0],\n            [1, 0, 0],\n            [0, 0, -1]\n        ])\n        new_R = np.dot(transformation_matrix, R)\n        new_roll, new_pitch, new_yaw = rotation_matrix_to_euler_angles(new_R)\n        return new_roll, new_pitch, new_yaw\n    \n    def rotation_matrix_roll(self, roll):\n        return np.array([\n            [1, 0, 0],\n            [0, np.cos(roll), -np.sin(roll)],\n            [0, np.sin(roll), np.cos(roll)]\n        ])\n\n    def rotation_matrix_pitch(self, pitch):\n        return np.array([\n            [np.cos(pitch), 0, np.sin(pitch)],\n            [0, 1, 0],\n            [-np.sin(pitch), 0, np.cos(pitch)]\n        ])\n\n    def rotation_matrix_yaw(self, yaw):\n        return np.array([\n            [np.cos(yaw), -np.sin(yaw), 0],\n            [np.sin(yaw), np.cos(yaw), 0],\n            [0, 0, 1]\n        ])\n\n    def transform_to_camera_frame(self, roll, pitch, yaw):\n        R_roll = self.rotation_matrix_roll(roll)\n        R_pitch = self.rotation_matrix_pitch(pitch)\n        R_yaw = self.rotation_matrix_yaw(yaw)\n        R_combined = np.dot(R_pitch, np.dot(R_yaw, R_roll))\n        QW, QX, QY, QZ = rotation_matrix_to_quaternion(R_combined)\n        print(f\"QW: {QW}, QX: {QX}, QY: {QY}, QZ: {QZ}\")\n        transformation_matrix = np.array([\n            [0, -1, 0],\n            [0, 0, -1],\n            [1, 0, 0]\n        ])\n        new_R = np.dot(transformation_matrix, R_combined)\n        QW_new, QX_new, QY_new, QZ_new = rotation_matrix_to_quaternion(new_R)\n        return QW_new, QX_new, QY_new, QZ_new\n\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll, path_params):\n        yaw = -yaw\n        pitch = -40\n        QW, QX, QY, QZ = self.transform_to_camera_frame(math.radians(roll), math.radians(pitch), math.radians(yaw))\n        camera_position = world2cam_WXYZ(x, y, z, QW, QX, QY, QZ)\n        quat = [QW, QX, QY, QZ]\n        camera_id = 0\n        image_name = \"00000000.png\"\n        image_data = f\"{camera_id} {' '.join(map(str, quat))} {' '.join(map(str, [camera_position[0], camera_position[1], camera_position[2]]))} {0} {image_name}\"\n        camera_params = f\"0 PINHOLE 1436 1077 718.861 718.861 718 538.5\"\n        data = {\n            \"camera\": camera_params,\n            \"image\": image_data,\n            \"path\": path_params\n        }\n        print(data)\n        try:\n            response = requests.post(self.url, data=data)\n            if response.status_code == 200:\n                print(\"Request successful!\")\n                print(response.text) \n            else:\n                print(f\"Request failed, status code: {response.status_code}\")\n                print(response.text)\n            memory = psutil.virtual_memory()\n            print(memory.percent)\n            if memory.percent >= 90:\n                print(\"Memory usage is above 90%\")\n                self.process.terminate()\n                self.__init__()\n        except requests.RequestException as e:\n            print(f\"Error during request: {e}\")\n            time.sleep(20)\n\n    def process_camera_data(self, file_path):\n        pass\n\n\n\ndef get_images(lst,if_his,step):\n    if if_his is False:\n        return lst[-1]\n    else:\n        if step == 1:\n            if len(lst) >= 2:\n                return [lst[-2], lst[-1]]\n            elif len(lst) == 1:\n                return [lst[0], lst[0]]\n        elif step == 2:\n            if len(lst) >= 3:\n                return lst[-3:]\n            elif len(lst) == 2:\n                return [lst[0], lst[0], lst[1]]\n            elif len(lst) == 1:\n                return [lst[0],lst[0], lst[0]]\n\ndef convert_to_action_id(action):\n    action_dict = {\n        \"0\": np.array([1, 0, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # stop\n        \"1\": np.array([0, 3, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # move forward\n        \"2\": np.array([0, 0, 15, 0, 0, 0, 0, 0]).astype(np.float32),  # turn left 30\n        \"3\": np.array([0, 0, 0, 15, 0, 0, 0, 0]).astype(np.float32),  # turn right 30\n        \"4\": np.array([0, 0, 0, 0, 2, 0, 0, 0]).astype(np.float32),  # go up\n        \"5\": np.array([0, 0, 0, 0, 0, 2, 0, 0]).astype(np.float32),  # go down\n        \"6\": np.array([0, 0, 0, 0, 0, 0, 5, 0]).astype(np.float32),  # move left\n        \"7\": np.array([0, 0, 0, 0, 0, 0, 0, 5]).astype(np.float32),  # move right\n        \"8\": np.array([0, 6, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # move forward 6\n        \"9\": np.array([0, 9, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # move forward 9\n    }\n    action_values = list(action_dict.values())\n    result = 0\n\n    matched = False\n    for idx, value in enumerate(action_values):\n        if np.array_equal(action, value):\n            result = idx\n            matched = True\n            break\n    # If no match is found, default to 0\n    if not matched:\n        result = 0\n    return result\n\ndef get_action(policy, processor, image_list, text, his, if_his=False, his_step=0):\n\n    # Otherwise, generate new actions using the policy\n    image_list = get_images(image_list, if_his, his_step)\n\n    if isinstance(image_list, np.ndarray):\n        img = image_list\n        img = Image.fromarray(img)\n        images = [img, img, img]\n    else:\n        images = []\n        for img in image_list:\n            img = Image.fromarray(img)\n            images.append(img)\n        \n    prompt = text\n    inputs = processor(prompt, images).to(\"cuda:0\", dtype=torch.bfloat16)\n    action = policy.predict_action(**inputs, unnorm_key=\"vlnv1\", do_sample=False)\n    print(\"raw action:\", action)\n    action = action.round().astype(int)\n\n    # Convert action_chunk to action IDs\n    action_id = convert_to_action_id(action)\n\n    cur_action = action_id\n    print(\"Action:\", action_id)\n    return cur_action\n\ndef calculate_distance(point1, point2):\n    return math.sqrt((point2[0] - point1[0])**2 + \n                     (point2[1] - point1[1])**2 + \n                     (point2[2] - point1[2])**2)\n\ndef getPoseAfterMakeAction(new_pose, action):\n    x, y, z, yaw = new_pose\n\n    # Define step size\n    step_size = 3.0  # Translation step size (units can be adjusted as needed)\n\n    # Update new_pose based on action value\n    if action == 0:\n        pass\n    elif action == 1:\n        x += step_size * math.cos(yaw)\n        y += step_size * math.sin(yaw)\n    elif action == 2:\n        yaw += math.radians(30)\n    elif action == 3:\n        yaw -= math.radians(30)\n    elif action == 4:\n        z += step_size\n    elif action == 5:\n        z -= step_size\n    elif action == 6:\n        x -= step_size * math.sin(yaw)\n        y += step_size * math.cos(yaw)\n    elif action == 7:\n        x += step_size * math.sin(yaw)\n        y -= step_size * math.cos(yaw)\n    elif action == 8:\n        x += step_size * math.cos(yaw) *2\n        y += step_size * math.sin(yaw) *2\n    elif action == 9:\n        x += step_size * math.cos(yaw) *3\n        y += step_size * math.sin(yaw) *3\n\n    yaw = (yaw + math.pi) % (2 * math.pi) - math.pi\n\n    return [x, y, z, yaw]\n\ndef main():\n    eval_info = \"configs/eval_test.json\"\n\n    f = open(eval_info, 'r')\n    all_eval_info = json.loads(f.read())\n    f.close()\n    \n    # Load model\n    model_name_or_path=\"IPEC-COMMUNITY/openfly-agent-7b\"\n    processor = AutoProcessor.from_pretrained(model_name_or_path)\n    policy = AutoModelForVision2Seq.from_pretrained(\n        model_name_or_path, \n        attn_implementation=\"flash_attention_2\",  # [Optional] Requires `flash_attn`\n        torch_dtype=torch.bfloat16, \n        low_cpu_mem_usage=True, \n        trust_remote_code=True,\n    ).to(\"cuda:0\")\n\n    # Test metrics\n    acc = 0\n    stop = 0\n    data_num = 0\n    MAX_STEP = 100\n\n    # Group by environment type\n    env_groups = {}\n    for item in all_eval_info:\n        env_type = item[\"image_path\"].split(\"/\")[0]  # Get environment type\n        if env_type not in env_groups:\n            env_groups[env_type] = []\n        env_groups[env_type].append(item)\n    \n    # Process each environment type sequentially\n    for env_name, eval_info in env_groups.items():\n        print(f\"Starting evaluation of environment: {env_name}, with {len(eval_info)} data entries\")\n        time.sleep(5)\n        \n        # Create appropriate environment bridge based on environment type\n        if \"airsim\" in env_name:\n            env_bridge = AirsimBridge(env_name)\n            pos_ratio = 1.0\n        elif \"ue\" in env_name:\n            env_bridge = UEBridge(ue_ip=\"127.0.0.1\", ue_port=\"9000\", env_name=env_name)\n            pos_ratio = 1.0\n        elif \"gs\" in env_name:\n            env_bridge = GSBridge(env_name)\n            pos_ratio = 5.15\n        else:\n            print(f\"Unknown environment type: {env_name}, skipping\")\n            continue\n        \n        # Evaluate all data for current environment\n        for idx, item in enumerate(eval_info):\n            acts = []  # Reset action list\n            data_num += 1\n            pos_list = item['pos']\n            text = item['gpt_instruction']\n            start_postion = pos_list[0]\n            start_yaw = item['yaw'][0]\n            new_pose = [start_postion[0], start_postion[1], start_postion[2], start_yaw]\n            end_position = pos_list[-1]\n            print(f\"Sample {idx}: {start_postion} -> {end_position}, initial heading: {start_yaw}\")\n            \n            stop_error = 1\n            image_error = False\n            \n            # Set camera pose\n            pitch = -45.0 if 'high' in item['image_path'] else 0.0\n            env_bridge.set_camera_pose(\n                start_postion[0]/pos_ratio, \n                start_postion[1]/pos_ratio, \n                start_postion[2]/pos_ratio, \n                pitch, \n                np.rad2deg(start_yaw), \n                0\n            )\n            \n            step = 0\n            flag_osr = 0\n            image_list = []\n            env_bridge.pass_len = 1e-3\n            old_pose = new_pose\n            \n            while step < MAX_STEP:\n                try:\n                    raw_image = env_bridge.get_camera_data()\n                    cv2.imwrite(\"test/cur_img.jpg\", raw_image)\n                    image = raw_image\n                    \n                    image_list.append(image)\n                    model_action = get_action(policy, processor, image_list, text, acts, if_his=True, his_step=2)\n                    acts.append(model_action)\n                    new_pose = getPoseAfterMakeAction(new_pose, model_action)\n                    print(f\"Environment: {env_name}, Sample: {idx}, Step: {step}, Action: {model_action}, New position: {new_pose}\")\n                    env_bridge.set_camera_pose(\n                        new_pose[0]/pos_ratio, \n                        new_pose[1]/pos_ratio, \n                        new_pose[2]/pos_ratio, \n                        pitch, \n                        np.rad2deg(new_pose[3]), \n                        0\n                    )\n                    env_bridge.pass_len += calculate_distance(old_pose, new_pose)\n                    dis = calculate_distance(end_position, new_pose)\n                    if dis < 20 and flag_osr != 2:\n                        flag_osr = 2\n                        env_bridge.osr.append(1)\n                    old_pose = new_pose\n\n                    if model_action == 0:\n                        stop_error = 0\n                        break\n                    step += 1\n                except Exception as e:\n                    print(f\"Error processing image: {e}\")\n                    image_error = True\n                    break\n\n            dis = calculate_distance(end_position, new_pose)\n            env_bridge.traj_len = calculate_distance(end_position, start_postion)\n            env_bridge.distance_to_goal.append(dis)\n            if dis < 20:\n                env_bridge.success.append(1)\n                env_bridge.spl.append(env_bridge.traj_len / env_bridge.pass_len)\n                acc += 1\n            else:\n                env_bridge.success.append(0)\n                env_bridge.spl.append(0)\n            if flag_osr == 0:\n                env_bridge.osr.append(0)\n            env_bridge.print_info()\n\n            if image_error:\n                continue\n                \n        \n        # Clean up environment resources\n        print(f\"Completed evaluation of environment {env_name}\")\n        kill_env_process(\"AirVLN\")\n        kill_env_process(\"guangzhou\")\n        kill_env_process(\"shanghai\")\n        kill_env_process(\"CitySample\")\n        kill_env_process(\"CrashReport\")\n\n        del env_bridge\n        import gc\n        gc.collect()\n    \n    # Final results\n    final_acc = acc / data_num if data_num > 0 else 0\n    final_stop = 1 - stop / data_num if data_num > 0 else 0\n    \n    print(f\"\\nEvaluation complete!\")\n    print(f\"Total samples: {data_num}\")\n    print(f\"Final accuracy: {final_acc:.4f}\")\n    print(f\"Final stop rate: {final_stop:.4f}\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "train/eval_gtav.py",
    "content": "#!/usr/bin/env python3 \nimport numpy as np  \nimport cv2\nfrom collections import deque\nimport io\nimport time\nimport subprocess, threading\nfrom datetime import datetime\nimport math\nfrom tqdm import tqdm\nimport random\nimport os\nimport json\nimport torch\nfrom PIL import Image as Image_\nfrom transformers import AutoConfig, AutoImageProcessor, AutoModelForVision2Seq, AutoProcessor\nfrom prismatic.extern.hf.configuration_prismatic import OpenVLAConfig\nfrom prismatic.extern.hf.modeling_prismatic import OpenVLAForActionPrediction\nfrom prismatic.extern.hf.processing_prismatic import PrismaticImageProcessor, PrismaticProcessor  \n\nfrom utils.Constants import IMG_WIDTH, IMG_HEIGHT\nfrom PIL import ImageGrab\nfrom deepgtav.messages import Start, Stop, Scenario, Dataset, Commands, frame2numpy, GoToLocation, TeleportToLocation, SetCameraPositionAndRotation\nfrom deepgtav.messages import StartRecording, StopRecording, SetClockTime, SetWeather, CreatePed\nfrom deepgtav.client import Client\nfrom utils.BoundingBoxes import add_bboxes, parseBBox2d_LikePreSIL, parseBBoxesVisDroneStyle, parseBBox_YoloFormatStringToImage\nfrom utils.utils import save_image_and_bbox, save_meta_data, getRunCount, generateNewTargetLocation\nfrom scipy.spatial.transform import Rotation as R\nimport argparse\nfrom PIL import Image\nimport pyautogui\nimport shutil\nimport select\nimport socket\nfrom datetime import datetime\nimport threading\nimport yaml\nfrom pathlib import Path\n\nclass GTAVBridge:\n    def __init__(self, host='127.0.0.1', port=8000, save_dir='./output'):\n        self.host = '127.0.0.1'\n        self.port = 8000\n        self.save_dir = os.path.normpath(save_dir)\n        self.client = Client(ip=self.host, port=self.port)\n        scenario = Scenario(drivingMode=[786603,0], vehicle=\"voltic\", location=[245.23306274414062, -998.244140625, 29.205352783203125])\n        dataset = Dataset(location=True, time=True, exportBBox2D=True, segmentationImage=True, exportLiDAR=True, maxLidarDist=5000)   \n        self.client.sendMessage(Start(scenario=scenario, dataset=dataset))  \n\n        self.distance_to_goal = []\n        self.spl = []\n        self.success = []\n        self.traj_len = 0\n        self.pass_len = 1e-3\n        self.osr = []\n\n    def print_info(self):\n        print(f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\")\n        return f\"SR: {self.success[-1]}, OSR: {self.osr[-1]}, NE: {self.distance_to_goal[-1]}, SPL: {self.spl[-1]}\"\n\n    def set_camera_pose(self, x, y, z, pitch, yaw, roll):\n        pitch = 0.0 \n        roll = 0.0\n\n        rotation = R.from_euler('y', 90, degrees=True)  # Inverse rotation\n        rotation_matrix = rotation.as_matrix()\n\n        out_x = -y\n        out_y = x\n        position = np.array([out_x, out_y, z])\n        rotated_position = np.dot(rotation_matrix, position)\n\n        rotation_angles = np.array([roll, pitch, yaw])  # [pitch, yaw, roll]\n        rotated_angles = rotation.inv().apply(rotation_angles)\n\n        # print(f\"Original Position: x: {x}, y: {y}, z: {z}\")\n        # print(f\"Rotated Position: x: {rotated_position[0]}, y: {rotated_position[1]}, z: {rotated_position[2]}\")\n        # print(f\"Original Angles: pitch: {pitch}, yaw: {yaw}, roll: {roll}\")\n        # print(f\"Rotated Angles: pitch: {rotated_angles[0]}, yaw: {rotated_angles[1]}, roll: {rotated_angles[2]}\")\n        \n        self.client.sendMessage(SetClockTime(12))\n        message = self.client.recvMessage() \n        pos =  message[\"location\"]\n        target_position = (position[0], position[1], position[2])\n        current_position = (pos[0], pos[1], pos[2])\n        distance = self.calculate_distance_3d(current_position, target_position)\n        cccc=0\n        while distance > 1 and cccc<10:\n            # 重设位置\n            cccc+=1\n            self.client.sendMessage(SetCameraPositionAndRotation(\n                0, 0, -10,\n                rotation_angles[0], rotation_angles[1], -rotation_angles[2]\n            ))\n            self.client.sendMessage(TeleportToLocation(position[0], position[1], position[2]))\n            \n            # 等待0.1秒\n            time.sleep(0.05)\n            \n            # 获取新位置\n            message = self.client.recvMessage()\n            pos = message[\"location\"]\n            current_position = (pos[0], pos[1], pos[2])\n            \n            # 重新计算距离\n            distance = self.calculate_distance_3d(current_position, target_position)\n       \n\n            self.client.sendMessage(SetClockTime(12))\n        print(message['CameraAngle'])\n        time.sleep(0.02)\n\n\n    def calculate_distance_3d(self, point1, point2):\n        return math.sqrt((point2[0] - point1[0])**2 + \n                        (point2[1] - point1[1])**2 + \n                        (point2[2] - point1[2])**2)\n\n    def get_camera_data(self, file_path):\n        os.makedirs(os.path.dirname(file_path), exist_ok=True)\n        \n        # 定义截取区域（左上角x, 左上角y, 右下角x, 右下角y）\n        bbox = (320, 171, 2239, 1250)\n        screenshot = ImageGrab.grab(bbox=bbox)\n        \n        screenshot.save(file_path)\n        return cv2.imread(file_path)\n\n    # def get_camera_data(self):\n    #     output_path = \"C:/Program Files/Epic Games/GTAVEnhanced/color.raw\"\n    #     self.press_key_l()\n    #     output_filename = output_path.replace(\".raw\", \".jpg\")\n    #     return cv2.imread(output_path)\n\n    def press_key_l(self):\n        \"\"\"Simulate pressing the 'l' key\"\"\"\n        pyautogui.press('l')\n\n    def move_raw_image(self, source, destination):\n        \"\"\"Move raw image file to the destination path\"\"\"\n        shutil.move(source, destination)\n\n    def stop_client(self):\n        \"\"\"Stop the client and disconnect\"\"\"\n        self.client.sendMessage(Stop())\n        self.client.close()\n\nclass TCPServer:\n    def __init__(self, host='0.0.0.0', port=9999, timeout=80):\n        self.server_address = (host, port)\n        self.timeout = timeout  # Timeout in seconds\n        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.server_socket.bind(self.server_address)\n        self.server_socket.listen(1)\n        print(f\"Listening on {host}:{port}\")\n    \n    def accept_connection(self):\n        conn, addr = self.server_socket.accept()\n        print(f\"Connected by {addr}\")\n        return conn\n    \n    def receive_pose(self, conn):\n        ready_to_read, _, _ = select.select([conn], [], [], self.timeout)\n        \n        if ready_to_read:\n            data = conn.recv(1024).decode()\n            return data\n        else:\n            print(f\"No data received within {self.timeout} seconds.\")\n            return 'timeout'\n\n\ndef load_config(config_file=\"config.yaml\"):\n    with open(config_file, 'r') as f:\n        config = yaml.safe_load(f)\n        # print(config)\n    return config['thread_params']\n\ndef find_pose_json_folders(root_folder):\n    \"\"\"递归查找有且只有pose.json文件的最深层文件夹\"\"\"\n    pose_folders = []\n    \n    for root, dirs, files in os.walk(root_folder):\n        dirs.sort() \n        if not dirs:\n            if len(files) == 2 and files[1] == 'pose.json':\n                pose_folders.append(root)\n    \n    return pose_folders\n\n\n\n# Register OpenVLA model to HF AutoClasses (not needed if you pushed model to HF Hub)\nAutoConfig.register(\"openvla\", OpenVLAConfig)\nAutoImageProcessor.register(OpenVLAConfig, PrismaticImageProcessor)\nAutoProcessor.register(OpenVLAConfig, PrismaticProcessor)\nAutoModelForVision2Seq.register(OpenVLAConfig, OpenVLAForActionPrediction)\n\n\ndef convert_to_action_id(action):\n    action_dict = {\n        \"0\": np.array([1, 0, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # stop\n        \"1\": np.array([0, 3, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # move forward\n        \"2\": np.array([0, 0, 15, 0, 0, 0, 0, 0]).astype(np.float32),  # turn left 30\n        \"3\": np.array([0, 0, 0, 15, 0, 0, 0, 0]).astype(np.float32),  # turn right 30\n        \"4\": np.array([0, 0, 0, 0, 2, 0, 0, 0]).astype(np.float32),  # go up\n        \"5\": np.array([0, 0, 0, 0, 0, 2, 0, 0]).astype(np.float32),  # go down\n        \"6\": np.array([0, 0, 0, 0, 0, 0, 5, 0]).astype(np.float32),  # move left\n        \"7\": np.array([0, 0, 0, 0, 0, 0, 0, 5]).astype(np.float32),  # move right\n        \"8\": np.array([0, 6, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # move forward 6\n        \"9\": np.array([0, 9, 0, 0, 0, 0, 0, 0]).astype(np.float32),  # move forward 9\n    }\n    action_values = list(action_dict.values())\n    result = 0\n\n    matched = False\n    for idx, value in enumerate(action_values):\n        if np.array_equal(action, value):\n            result = idx\n            matched = True\n            break\n    # If no match is found, default to 0\n    if not matched:\n        result = 0\n    return result\n\ndef get_images(lst, if_his, step):\n    if if_his is False:\n        return lst[-1]\n    else:\n        if step == 1:\n            if len(lst) >= 2:\n                return [lst[-2], lst[-1]]\n            elif len(lst) == 1:\n                return [lst[0], lst[0]]\n        elif step == 2:\n            if len(lst) >= 3:\n                return lst[-3:]\n            elif len(lst) == 2:\n                return [lst[0], lst[0], lst[1]]\n            elif len(lst) == 1:\n                return [lst[0],lst[0], lst[0]]\n\ndef get_action(policy, processor, image_list, text, if_his=False, his_step=0):\n\n    # Otherwise, generate new actions using the policy\n    image_list = get_images(image_list, if_his, his_step)\n\n    if isinstance(image_list, np.ndarray):\n        img = image_list\n        img = Image.fromarray(img)\n        images = [img, img, img]\n    else:\n        images = []\n        for img in image_list:\n            img = Image.fromarray(img)\n            images.append(img)\n        \n    prompt = text\n    inputs = processor(prompt, images).to(\"cuda:0\", dtype=torch.bfloat16)\n    action = policy.predict_action(**inputs, unnorm_key=\"vlnv1\", do_sample=False)\n    action = action.round().astype(int)\n\n    # Convert action_chunk to action IDs\n    action_id = convert_to_action_id(action)\n\n    cur_action = action_id\n    return cur_action\n\ndef calculate_distance(point1, point2):\n    return math.sqrt((point2[0] - point1[0])**2 + \n                     (point2[1] - point1[1])**2 + \n                     (point2[2] - point1[2])**2)\n\ndef getPoseAfterMakeAction(new_pose, action):\n    x, y, z, yaw = new_pose\n\n    # Define step size\n    step_size = 3.0  # Translation step size (units can be adjusted as needed)\n\n    # Update new_pose based on action value\n    if action == 0:\n        pass\n    elif action == 1:\n        x += step_size * math.cos(yaw)\n        y += step_size * math.sin(yaw)\n    elif action == 2:\n        yaw += math.radians(30)\n    elif action == 3:\n        yaw -= math.radians(30)\n    elif action == 4:\n        z += step_size\n    elif action == 5:\n        z -= step_size\n    elif action == 6:\n        x -= step_size * math.sin(yaw)\n        y += step_size * math.cos(yaw)\n    elif action == 7:\n        x += step_size * math.sin(yaw)\n        y -= step_size * math.cos(yaw)\n    elif action == 8:\n        x += step_size * math.cos(yaw) *2\n        y += step_size * math.sin(yaw) *2\n    elif action == 9:\n        x += step_size * math.cos(yaw) *3\n        y += step_size * math.sin(yaw) *3\n\n    yaw = (yaw + math.pi) % (2 * math.pi) - math.pi\n\n    return [x, y, z, yaw]\n\n\n\ndef main():\n\n    # Test metrics\n    acc = 0\n    stop = 0\n    data_num = 0\n    MAX_STEP = 100\n\n    eval_data_directory = \"gtav.json\"\n    f = open(eval_data_directory, 'r')\n    eval_info = json.load(f)\n\n    env_name = \"env_game_gtav\"\n        \n    env_bridge = GTAVBridge()\n    output_file = f\"eval_results_{time.strftime('%Y%m%d_%H%M%S')}.jsonl\"\n    pos_ratio = 1.0\n\n    message = env_bridge.client.recvMessage()\n\n    # Load model\n    model_name_or_path=\"IPEC-COMMUNITY/openfly-agent-7b\"\n    processor = AutoProcessor.from_pretrained(model_name_or_path)\n    policy = AutoModelForVision2Seq.from_pretrained(\n        model_name_or_path, \n        # attn_implementation=\"flash_attention_2\",  # [Optional] Requires `flash_attn`\n        torch_dtype=torch.bfloat16, \n        low_cpu_mem_usage=True, \n        trust_remote_code=True,\n    ).to(\"cuda:0\")\n\n    for sample_idx, item in enumerate(eval_info):\n        if not \"image_path\" in item.keys():\n            continue\n        if not item['image_path'].__contains__(env_name):\n            continue\n\n        step = 0\n        flag_osr = 0\n        image_list = []\n        env_bridge.pass_len = 1e-3\n        os.makedirs(f\"{env_name}/{sample_idx}\", exist_ok=True)\n\n        pos_list = item['pos']\n        text = item['gpt_instruction']\n        start_postion = pos_list[0]\n        start_yaw = item['yaw'][0]\n        new_pose = [start_postion[0], start_postion[1], start_postion[2], start_yaw]\n        old_pose = new_pose\n        end_position = pos_list[-1]\n        print(f\"Sample {sample_idx}: {start_postion} -> {end_position}, initial heading: {start_yaw}\")\n        \n        stop_error = 1\n        image_error = False\n        \n        # Set camera pose\n        pitch = -45.0 if 'high' in item['image_path'] else 0.0\n        env_bridge.set_camera_pose(\n            start_postion[0]/pos_ratio, \n            start_postion[1]/pos_ratio, \n            start_postion[2]/pos_ratio, \n            pitch, \n            np.rad2deg(start_yaw), \n            0\n        )\n\n        while step < MAX_STEP:    \n            \n            image = env_bridge.get_camera_data(f\"{env_name}/{sample_idx}/{str(step).zfill(3)}.png\")\n\n            image_list.append(image)\n\n            model_action = get_action(policy, processor, image_list, text, if_his=True, his_step=2)\n            print(model_action)\n            new_pose = getPoseAfterMakeAction(new_pose, model_action)\n\n            # print(f\"Environment: {env_name}, Sample: {idx}, Step: {step}, Action: {model_action}, New position: {new_pose}\")\n            env_bridge.set_camera_pose(\n                new_pose[0]/pos_ratio, \n                new_pose[1]/pos_ratio, \n                new_pose[2]/pos_ratio, \n                0.0, \n                np.rad2deg(new_pose[3]),\n                0\n            )\n\n            env_bridge.pass_len += calculate_distance(old_pose, new_pose)\n            dis = calculate_distance(end_position, new_pose)\n            if dis < 40 and flag_osr != 2:\n                flag_osr = 2\n                env_bridge.osr.append(1)\n            old_pose = new_pose\n\n            if model_action == 0:\n                stop_error = 0\n                break\n            step += 1\n\n            dis = calculate_distance(end_position, new_pose)\n            env_bridge.traj_len = calculate_distance(end_position, start_postion)\n            env_bridge.distance_to_goal.append(dis)\n            if dis < 20:\n                env_bridge.success.append(1)\n                env_bridge.spl.append(env_bridge.traj_len / env_bridge.pass_len)\n            else:\n                env_bridge.success.append(0)\n                env_bridge.spl.append(0)\n            if flag_osr == 0:\n                env_bridge.osr.append(0)\n\n            if image_error:\n                continue\n                \n            # Save individual sample statistics to jsonl\n            sample_stats = {\n                \"env_name\": env_name,\n                \"sample_idx\": sample_idx,\n                \"instruction\": text,\n                \"start_position\": start_postion,\n                \"end_position\": end_position,\n                \"final_position\": new_pose[:3],\n                \"distance_to_goal\": dis,\n                \"success\": 1 if dis < 20 else 0,\n                \"spl\": env_bridge.spl[-1],\n                \"osr\": env_bridge.osr[-1],\n                \"steps_taken\": step,\n                \"trajectory_length\": env_bridge.traj_len,\n                \"path_length\": env_bridge.pass_len,\n                \"stop_error\": stop_error,\n                \"image_error\": image_error\n            }\n            \n            with open(output_file, 'a', encoding='utf-8') as f:\n                f.write(json.dumps(sample_stats, ensure_ascii=False) + '\\n')\n            \n            data_num += 1\n            if dis < 20:\n                acc += 1\n            stop += stop_error\n                \n        # Save environment summary statistics\n        env_summary = {\n            \"type\": \"environment_summary\",\n            \"env_name\": env_name,\n            \"total_samples\": len(eval_info),\n            \"success_rate\": sum(env_bridge.success) / len(env_bridge.success) if env_bridge.success else 0,\n            \"spl\": sum(env_bridge.spl) / len(env_bridge.spl) if env_bridge.spl else 0,\n            \"osr\": sum(env_bridge.osr) / len(env_bridge.osr) if env_bridge.osr else 0,\n            \"avg_distance_to_goal\": sum(env_bridge.distance_to_goal) / len(env_bridge.distance_to_goal) if env_bridge.distance_to_goal else 0,\n            \"success_count\": sum(env_bridge.success),\n            \"total_distance_to_goal\": sum(env_bridge.distance_to_goal)\n        }\n        \n        with open(output_file, 'a', encoding='utf-8') as f:\n            f.write(json.dumps(env_summary, ensure_ascii=False) + '\\n')\n        \n        # Clean up environment resources\n        print(f\"Completed evaluation of environment {env_name}\")\n        f.close()\n        \n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "train/extern/__init__.py",
    "content": "\n"
  },
  {
    "path": "train/extern/hf/__init__.py",
    "content": "\n"
  },
  {
    "path": "train/extern/hf/configuration_prismatic.py",
    "content": "\"\"\"\nconfiguration_prismatic.py\n\nHuggingFace-style configuration definition for Prismatic VLMs, inheriting from `transformers.PretrainedConfig`.\nDefault configuration specifies `siglip-224px+7b`.\n\"\"\"\n\nfrom typing import Any, Dict, List, Optional\n\nfrom transformers import PretrainedConfig\nfrom transformers.models.auto import CONFIG_MAPPING\n\n# === Utilities for Mapping Prismatic names to HF names ===\n# fmt: off\nVISION_BACKBONE_TO_RESOLUTION: Dict[str, List[int]] = {\n    \"clip-vit-l\": [224], \"siglip-vit-so400m\": [224], \"dinov2-vit-l\": [224], \"in1k-vit-l\": [224],\n\n    \"clip-vit-l-336px\": [336],\n    \"siglip-vit-so400m-384px\": [384],\n\n    \"dinoclip-vit-l-336px\": [336, 336],\n    \"dinosiglip-vit-so-224px\": [224, 224],\n    \"dinosiglip-vit-so-384px\": [384, 384],\n}\nVISION_BACKBONE_TO_TIMM_ID: Dict[str, List[str]] = {\n    \"clip-vit-l\": [\"vit_large_patch14_clip_224.openai\"],\n    \"clip-vit-l-336px\": [\"vit_large_patch14_clip_336.openai\"],\n\n    \"dinov2-vit-l\": [\"vit_large_patch14_reg4_dinov2.lvd142m\"],\n    \"in1k-vit-l\": [\"vit_large_patch16_224.augreg_in21k_ft_in1k\"],\n\n    \"siglip-vit-so400m\": [\"vit_so400m_patch14_siglip_224\"],\n    \"siglip-vit-so400m-384px\": [\"vit_so400m_patch14_siglip_384\"],\n\n    \"dinoclip-vit-l-336px\": [\"vit_large_patch14_reg4_dinov2.lvd142m\", \"vit_large_patch14_clip_336.openai\"],\n    \"dinosiglip-vit-so-224px\": [\"vit_large_patch14_reg4_dinov2.lvd142m\", \"vit_so400m_patch14_siglip_224\"],\n    \"dinosiglip-vit-so-384px\": [\"vit_large_patch14_reg4_dinov2.lvd142m\", \"vit_so400m_patch14_siglip_384\"],\n}\nTIMM_OVERRIDE_ACT_LAYER: Dict[str, List[Optional[str]]] = {\n    \"clip-vit-l\": [\"quick_gelu\"], \"clip-vit-l-336px\": [\"quick_gelu\"],\n    \"dinov2-vit-l\": [None], \"in1k-vit-l\": [None],\n    \"siglip-vit-so400m\": [None], \"siglip-vit-so400m-384px\": [None],\n    \"dinoclip-vit-l-336px\": [None, \"quick_gelu\"],\n    \"dinosiglip-vit-so-224px\": [None, None], \"dinosiglip-vit-so-384px\": [None, None]\n}\n\nLLM_BACKBONE_TO_HF_PATH = {\n    \"llama3-3b-inst\": \"meta-llama/Llama-3.2-3B-Instruct\",\n    \"llama2-7b-pure\": \"meta-llama/Llama-2-7b-hf\", \"llama2-13b-pure\": \"meta-llama/Llama-2-13b-hf\",\n    \"llama2-7b-chat\": \"meta-llama/Llama-2-7b-chat-hf\", \"llama2-13b-chat\": \"meta-llama/Llama-2-13b-chat-hf\",\n\n    \"vicuna-v15-7b\": \"lmsys/vicuna-7b-v1.5\", \"vicuna-v15-13b\": \"lmsys/vicuna-13b-v1.5\",\n\n    \"mistral-v0.1-7b-pure\": \"mistralai/Mistral-7B-v0.1\",\n    \"mistral-v0.1-7b-instruct\": \"mistralai/Mistral-7B-Instruct-v0.1\",\n\n    \"phi-2-3b\": \"microsoft/phi-2\",\n}\nLLM_BACKBONE_TO_HF_METACLASS = {\n    \"llama3-3b-inst\": \"llama\",\n    \"llama2-7b-pure\": \"llama\", \"llama2-13b-pure\": \"llama\", \"llama2-7b-chat\": \"llama\", \"llama2-13b-chat\": \"llama\",\n    \"vicuna-v15-7b\": \"llama\", \"vicuna-v15-13b\": \"llama\",\n\n    \"mistral-v0.1-7b-pure\": \"mistral\", \"mistral-v0.1-7b-instruct\": \"mistral\",\n\n    \"phi-2-3b\": \"phi\",\n}\n\nVALID_VISION_BACKBONES = set(VISION_BACKBONE_TO_RESOLUTION.keys())\nVALID_LLM_BACKBONES = set(LLM_BACKBONE_TO_HF_PATH)\n# fmt: on\n\n\nclass PrismaticConfig(PretrainedConfig):\n    model_type: str = \"prismatic\"\n    is_composition: bool = False\n\n    def __init__(\n        self,\n        vision_backbone_id: str = \"siglip-vit-so400m\",\n        llm_backbone_id: str = \"vicuna-v15-7b\",\n        arch_specifier: str = \"no-align+gelu-mlp\",\n        use_fused_vision_backbone: Optional[bool] = None,\n        image_resize_strategy: str = \"letterbox\",\n        text_config: Optional[Dict[str, Any]] = None,\n        llm_max_length: int = 2048,\n        pad_token_id: int = 32000,  # 128256\n        pad_to_multiple_of: int = 64,\n        output_projector_states: bool = False,\n        **kwargs: str,\n    ) -> None:\n        if vision_backbone_id not in VALID_VISION_BACKBONES:\n            raise ValueError(f\"Vision backbone `{vision_backbone_id}` not in {VALID_VISION_BACKBONES = }\")\n\n        if llm_backbone_id not in VALID_LLM_BACKBONES:\n            raise ValueError(f\"LLM backbone `{llm_backbone_id}` not in {VALID_LLM_BACKBONES = }\")\n\n        # Set Prismatic Configuration Fields\n        self.vision_backbone_id = vision_backbone_id\n        self.llm_backbone_id = llm_backbone_id\n        self.arch_specifier = arch_specifier\n        self.output_projector_states = output_projector_states\n\n        # [Contract] All vision backbone parameters are lists =>> supports fused backbones with different preprocessing\n        self.use_fused_vision_backbone = (\n            use_fused_vision_backbone\n            if use_fused_vision_backbone is not None\n            else any(self.vision_backbone_id.startswith(v) for v in [\"dinoclip\", \"dinosiglip\"])\n        )\n\n        self.timm_model_ids = VISION_BACKBONE_TO_TIMM_ID[self.vision_backbone_id]\n        self.timm_override_act_layers = TIMM_OVERRIDE_ACT_LAYER[self.vision_backbone_id]\n        self.image_sizes = VISION_BACKBONE_TO_RESOLUTION[self.vision_backbone_id]\n        self.image_resize_strategy = image_resize_strategy\n        \n        self.hf_llm_id = LLM_BACKBONE_TO_HF_PATH[self.llm_backbone_id]\n        self.llm_max_length = llm_max_length\n        self.pad_token_id, self.pad_to_multiple_of = pad_token_id, pad_to_multiple_of\n        # [IMPORTANT] HF Utilities actually look for a `text_config` field... we need to use that specific naming!\n        \n        self.text_config = (\n            # text_config\n            CONFIG_MAPPING[LLM_BACKBONE_TO_HF_METACLASS[self.llm_backbone_id]](**text_config)\n            if text_config is not None\n            else CONFIG_MAPPING[LLM_BACKBONE_TO_HF_METACLASS[self.llm_backbone_id]]()\n        )\n\n        # Dispatch **kwargs to super() =>> note that `pad_token_id` collides, so we pass it in here as well...\n        super().__init__(pad_token_id=pad_token_id, **kwargs)\n\n\nclass OpenFlyConfig(PrismaticConfig):\n    model_type: str = \"openvla\"\n\n    def __init__(\n        self,\n        norm_stats: Optional[Dict[str, Dict[str, Dict[str, Dict[str, List[float]]]]]] = None,\n        n_action_bins: int = 256,\n        **kwargs: str,\n    ) -> None:\n        self.norm_stats, self.n_action_bins = norm_stats, n_action_bins\n\n        super().__init__(**kwargs)\n"
  },
  {
    "path": "train/extern/hf/modeling_prismatic.py",
    "content": "\"\"\"\nmodeling_prismatic.py\n\nCore HuggingFace-style PrismaticPreTrainedModel and PrismaticForConditionalGeneration class definitions, inheriting\nfrom the default `transformers.PretrainedModel`. Meant to be standalone and self-contained, but exactly replicate the\nlogic in `prismatic.models.vlms.prismatic.py`.\n\nNote =>> for the time being, not adding the custom HF \"docstring\" formatting.\n\nReferences [LLaVa, IDEFICS-2]:\n    => https://github.com/huggingface/transformers/blob/main/src/transformers/models/llava/modeling_llava.py\n    => https://github.com/huggingface/transformers/blob/main/src/transformers/models/idefics2/modeling_idefics2.py\n\"\"\"\n\nimport logging\nfrom dataclasses import dataclass\nfrom functools import partial\nfrom typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple, Union\n\nimport numpy as np\nimport timm\nimport tokenizers\nimport torch\nimport torch.nn as nn\nimport transformers\nimport torch.nn.functional as F\nfrom timm.models.vision_transformer import LayerScale\nfrom transformers import AutoModelForCausalLM, PretrainedConfig, PreTrainedModel\nfrom transformers.modeling_outputs import ModelOutput\n\nfrom .configuration_prismatic import OpenFlyConfig, PrismaticConfig\n\n# Get Logger\nlogger = logging.getLogger(__name__)\n\n\n# === PyTorch/HuggingFace Default IGNORE_INDEX (for CrossEntropyLoss labels)\nIGNORE_INDEX = -100\n\n\n# === Utility Functions for Monkey-Patching ===\ndef unpack_tuple(fn: Callable[[Any], Tuple[Any]]) -> Callable[[Any], Any]:\n    def wrapper(*args: Any, **kwargs: Any) -> Any:\n        result = fn(*args, **kwargs)\n        return result[0] if isinstance(result, tuple) else result\n\n    return wrapper\n\n\n# HF Transformers overwrites parameters with names containing `gamma`; we're going to patch VisionBackbone.LayerScale.\n#   =>> TIMM :: https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py#L109\n#   =>> Transformers :: https://github.com/huggingface/transformers/blob/main/src/transformers/modeling_utils.py#L3960\ndef _ls_new_forward(self, x: torch.Tensor) -> torch.Tensor:\n    return x.mul_(self.scale_factor) if self.inplace else x * self.scale_factor\n\n\ndef ls_apply_patch(ls_module: LayerScale):\n    ls_module.scale_factor = nn.Parameter(ls_module.gamma.clone())\n    ls_module.forward = _ls_new_forward.__get__(ls_module, LayerScale)\n    del ls_module.gamma\n\n\n# === Prismatic Vision Backbone (nn.Module) Definitions (w/ Fused Backbone Support) ===\nclass PrismaticVisionBackbone(nn.Module):\n    def __init__(\n        self,\n        use_fused_vision_backbone: bool,\n        image_sizes: List[int],\n        timm_model_ids: List[str],\n        timm_override_act_layers: List[Optional[str]],\n    ) -> None:\n        super().__init__()\n        self.use_fused_vision_backbone = use_fused_vision_backbone\n\n        # [Contract] Validate number of (fused) vision backbones, create \"alpha\" featurizer and Instantiate\n        #   =>> Note :: Monkey-Patch the `forward()` function of the backbone to ensure FSDP-compatibility\n        #               Hardcodes `get_intermediate_layers` to return the **SECOND-TO-LAST** layer patches!\n        assert len(timm_model_ids) <= 2, \"Prismatic models only support up to 2 (fused) vision backbones!\"\n        \n        self.featurizer = timm.create_model(\n            timm_model_ids[0], pretrained=True, num_classes=0, img_size=image_sizes[0]\n        )\n\n        self.featurizer.forward = unpack_tuple(\n            partial(self.featurizer.get_intermediate_layers, n={len(self.featurizer.blocks) - 2}, return_prefix_tokens=True)\n        )\n        self.embed_dim = self.featurizer.embed_dim\n        \n        if self.use_fused_vision_backbone:\n            \n            \n            self.fused_featurizer = timm.create_model(\n                timm_model_ids[1], pretrained=True, num_classes=0, img_size=image_sizes[1]\n            )\n#             self.fused_featurizer = timm.create_model(\n#                 timm_model_ids[1],\n#                 pretrained=False,\n#                 num_classes=0,\n#                 img_size=image_sizes[1],\n#                 act_layer=timm_override_act_layers[1],\n#             )\n            self.fused_featurizer.forward = unpack_tuple(\n                partial(self.fused_featurizer.get_intermediate_layers, n={len(self.fused_featurizer.blocks) - 2}, return_prefix_tokens=True)\n            )\n            self.embed_dim += self.fused_featurizer.embed_dim\n\n        # Patch `vision_backbone.featurizer` and `vision_backbone.fused_featurizer` with HF-Compatible LayerScale\n        for module in self.featurizer.modules():\n            if isinstance(module, LayerScale):\n                ls_apply_patch(module)\n\n        if self.use_fused_vision_backbone:\n            for module in self.fused_featurizer.modules():\n                if isinstance(module, LayerScale):\n                    ls_apply_patch(module)\n\n    def forward(self, pixel_values: list, grid_size=16) -> list:\n        \"\"\"Run image (`pixel_values`) through featurizer; if channel-stacked, then dispatch and sequence stack.\"\"\"\n        # Split `pixel_values :: [bsz, 2 * 3, resolution, resolution]` =>> featurize =>> channel stack\n\n        img3, img_fused3 = torch.split(pixel_values[0:1], [3, 3], dim=1)\n        patches3, patches_fused3 = self.featurizer(img3)[1][:, :1, :], self.post_process(self.fused_featurizer(img_fused3)[0], grid_size)\n        img4, img_fused4 = torch.split(pixel_values[1:2], [3, 3], dim=1)\n        patches4, patches_fused4 = self.featurizer(img4)[1][:, :1, :], self.post_process(self.fused_featurizer(img_fused4)[0], grid_size)\n\n        img5, img_fused5 = torch.split(pixel_values[2:3], [3, 3], dim=1)\n        patches5, patches_fused5 = self.featurizer(img5)[0], self.fused_featurizer(img_fused5)[0]\n\n        return [torch.cat([patches3, patches_fused3], dim=2), torch.cat([patches4, patches_fused4], dim=2), torch.cat([patches5, patches_fused5], dim=2)]\n      \n#         if not self.use_fused_vision_backbone:\n#             return self.featurizer(pixel_values)\n\n#         # Split `pixel_values :: [bsz, 2 * 3, resolution, resolution]` =>> featurize =>> channel stack\n#         img, img_fused = torch.split(pixel_values, [3, 3], dim=1)\n#         patches, patches_fused = self.featurizer(img), self.fused_featurizer(img_fused)\n\n#         return torch.cat([patches, patches_fused], dim=2)\n \n    def post_process(self, tensorA, grid_size):\n        B, _, C = tensorA.size()\n        tensorA_like = tensorA.view(B, 16, 16, C)\n        pooled_tensorA = F.avg_pool2d(tensorA_like.permute(0, 3, 1, 2), kernel_size=grid_size)\n        result = pooled_tensorA.permute(0, 2, 3, 1).flatten(1,2)\n\n        return result\n\n\n\n# === Prismatic Projector (nn.Module) Definitions ===\nclass PrismaticProjector(nn.Module):\n    def __init__(self, use_fused_vision_backbone: bool, vision_dim: int, llm_dim: int) -> None:\n        super().__init__()\n        self.use_fused_vision_backbone = use_fused_vision_backbone\n        self.vision_dim, self.llm_dim = vision_dim, llm_dim\n\n        # Switch on `use_fused_vision_backbone` =>> use slightly different MLPs and projection factors!\n        if not self.use_fused_vision_backbone:\n            self.fc1 = nn.Linear(self.vision_dim, self.llm_dim, bias=True)\n            self.fc2 = nn.Linear(self.llm_dim, self.llm_dim, bias=True)\n            self.act_fn1 = nn.GELU()\n        else:\n            initial_projection_dim = 4 * vision_dim\n            self.fc1 = nn.Linear(self.vision_dim, initial_projection_dim, bias=True)\n            self.fc2 = nn.Linear(initial_projection_dim, self.llm_dim, bias=True)\n            self.fc3 = nn.Linear(self.llm_dim, self.llm_dim, bias=True)\n            self.act_fn1 = nn.GELU()\n            self.act_fn2 = nn.GELU()\n\n    def forward(self, img_patches: torch.Tensor) -> torch.Tensor:\n        if not self.use_fused_vision_backbone:\n            projected_features = self.fc1(img_patches)\n            projected_features = self.act_fn1(projected_features)\n            projected_features = self.fc2(projected_features)\n        else:\n            projected_features = self.fc1(img_patches)\n            projected_features = self.act_fn1(projected_features)\n            projected_features = self.fc2(projected_features)\n            projected_features = self.act_fn2(projected_features)\n            projected_features = self.fc3(projected_features)\n\n        return projected_features\n\n\n# === Main HF Class Definitions ===\n@dataclass\nclass PrismaticCausalLMOutputWithPast(ModelOutput):\n    \"\"\"Base class for Prismatic casual (visually-conditioned) language model outputs; also exposes visual features.\"\"\"\n\n    loss: Optional[torch.FloatTensor] = None\n    logits: torch.FloatTensor = None\n    past_key_values: Optional[Tuple[Tuple[torch.FloatTensor]]] = None\n    hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None\n    attentions: Optional[Tuple[torch.FloatTensor]] = None\n\n    # Additions for VLMs\n    projector_features: Optional[torch.FloatTensor] = None\n\n\nclass PrismaticPreTrainedModel(PreTrainedModel):\n    config_class: PretrainedConfig = PrismaticConfig\n    base_model_prefix: str = \"model\"\n    supports_gradient_checkpointing: bool = True\n\n    _no_split_modules: ClassVar[List[str]] = [\"PrismaticProjector\"]\n    _skip_keys_device_placement: str = \"past_key_values\"\n    _supports_flash_attn_2: bool = True\n\n    def _init_weights(self, module: nn.Module) -> None:\n        # Important :: this HF ported version is *not* meant for training from scratch; only inference and fine-tuning!\n        #   => As such, this init_weights code is not correct; if training VLMs from scratch, use the main codebase at\n        #      https://github.com/TRI-ML/prismatic-vlms\n        std = (\n            self.config.initializer_range\n            if hasattr(self.config, \"initializer_range\")\n            else self.config.text_config.initializer_range\n        )\n\n        if hasattr(module, \"class_embedding\"):\n            module.class_embedding.data.normal_(mean=0.0, std=std)\n\n        if isinstance(module, (nn.Linear, nn.Conv2d)):\n            module.weight.data.normal_(mean=0.0, std=std)\n            if module.bias is not None:\n                module.bias.data.zero_()\n        elif isinstance(module, nn.Embedding):\n            module.weight.data.normal_(mean=0.0, std=std)\n            if module.padding_idx is not None:\n                module.weight.data[module.padding_idx].zero_()\n\n    @property\n    def _supports_sdpa(self) -> bool:\n        \"\"\"Check LLM supports SDPA Attention\"\"\"\n        return self.language_model._supports_sdpa\n\n\nclass PrismaticForConditionalGeneration(PrismaticPreTrainedModel):\n    def __init__(self, config: PrismaticConfig) -> None:\n        super().__init__(config)\n\n        # [Validation] Lightweight Validate on `config` Fields + Dependency Versions\n        if config.use_fused_vision_backbone is None:\n            raise ValueError(\"Missing config field `use_fused_vision_backbone`\")\n\n        if timm.__version__ not in {\"0.9.10\", \"0.9.11\", \"0.9.12\", \"0.9.16\"}:\n            raise NotImplementedError(\n                \"TIMM Version must be >= 0.9.10 and < 1.0.0 (breaking); please raise a GitHub Issue \"\n                \"if you urgently need support for latest TIMM versions.\"\n            )\n\n        if (transformers.__version__ != \"4.48.1\") or (tokenizers.__version__ != \"0.21.1\"):\n            logger.warning(\n                f\"Expected `transformers==4.48.1` and `tokenizers==0.21.1` but got \"\n                f\"`transformers=={transformers.__version__}` and `tokenizers=={tokenizers.__version__}`; \"\n                f\"there might be inference-time regressions due to dependency changes. If in doubt, please\"\n                f\"use the above versions.\"\n            )\n\n        # Instantiate PrismaticVisionBackbone (w/ Potential Fused Backbone)\n        self.vision_backbone = PrismaticVisionBackbone(\n            config.use_fused_vision_backbone, config.image_sizes, config.timm_model_ids, config.timm_override_act_layers\n        )\n    \n        # Create Multimodal Projector\n        self.projector = PrismaticProjector(\n            config.use_fused_vision_backbone,\n            vision_dim=self.vision_backbone.embed_dim,\n            llm_dim=config.text_config.hidden_size,\n        )\n\n        # Instantiate LLM Backbone\n        self.language_model = AutoModelForCausalLM.from_config(\n            config.text_config, attn_implementation=config._attn_implementation\n        )\n        self.vocab_size = config.text_config.vocab_size\n        self.pad_token_id = config.pad_token_id\n\n        # HF Boilerplate =>> initializes weights via `_init_weights()` and sets gradient checkpointing\n        self.post_init()\n\n    # === `PreTrainedModel` Boilerplate ===\n    def get_input_embeddings(self) -> nn.Module:\n        return self.language_model.get_input_embeddings()\n\n    def set_input_embeddings(self, value: nn.Module) -> None:\n        self.language_model.set_input_embeddings(value)\n\n    def get_output_embeddings(self) -> nn.Module:\n        return self.language_model.get_output_embeddings()\n\n    def set_output_embeddings(self, new_embeddings: nn.Module) -> None:\n        self.language_model.set_output_embeddings(new_embeddings)\n\n    def get_decoder(self) -> nn.Module:\n        return self.language_model.get_decoder()\n\n    def set_decoder(self, decoder: nn.Module) -> None:\n        self.language_model.set_decoder(decoder)\n\n    def tie_weights(self) -> None:\n        self.language_model.tie_weights()  # Note: `Llama-2` and `Mistral` don't tie weights (no-op)\n\n    def resize_token_embeddings(\n        self, new_num_tokens: Optional[int] = None, pad_to_multiple_of: Optional[int] = None\n    ) -> nn.Embedding:\n        updated_embeddings = self.language_model.resize_token_embeddings(new_num_tokens, pad_to_multiple_of)\n\n        # Update config/instance variables\n        self.config.text_config.vocab_size = updated_embeddings.num_embeddings\n        self.vocab_size = updated_embeddings.num_embeddings\n\n        return updated_embeddings\n\n    # === Core Prismatic VLM `forward()` Logic ===\n    def forward(\n        self,\n        input_ids: Optional[torch.LongTensor] = None,\n        attention_mask: Optional[torch.Tensor] = None,\n        pixel_values: Optional[torch.FloatTensor] = None,\n        labels: Optional[torch.LongTensor] = None,\n        inputs_embeds: Optional[torch.FloatTensor] = None,\n        past_key_values: Optional[List[torch.FloatTensor]] = None,\n        use_cache: Optional[bool] = None,\n        output_attentions: Optional[bool] = None,\n        output_hidden_states: Optional[bool] = None,\n        output_projector_features: Optional[bool] = None,\n        return_dict: Optional[bool] = None,\n    ) -> Union[Tuple, PrismaticCausalLMOutputWithPast]:\n        \"\"\"Run a forward pass through the VLM, returning a PrismaticCausalLMOutputWithPast instance.\"\"\"\n        output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions\n        output_hidden_states = (\n            output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states\n        )\n        output_projector_features = output_projector_features if output_projector_features is not None else False\n        return_dict = return_dict if return_dict is not None else self.config.use_return_dict\n\n        # Respect `use_cache` only if not training (even if `gradient_checkpointing` is off)\n        use_cache = use_cache and not self.training\n\n        # Instantiate Placeholder for Projector Features\n        projected_patch_embeddings = None\n        \n        # Note :: We only support forward passes with the following cases:\n        #   => Cached Generation :: (input_ids.shape[1] == 1) and (past_key_values is not None)\n        #   => Unimodal Forward :: (pixel_values is None)\n        #   => Multimodal Forward :: (pixel_values is not None) and (input_ids/embeds.shape[0] == pixel_values.shape[0])\n\n        # === Handle Generation with Cache (`input_ids.shape[1] == 1`) =>> requires `past_keys_values` ===\n        if input_ids.shape[1] == 1:\n            assert input_ids.shape[0] == 1, \"Generation is only currently supported for batch size of 1!\"\n            assert past_key_values is not None, \"You must provide `past_key_values` during cached generation!\"\n            assert labels is None, \"Unexpected key `labels` provided during cached generation!\"\n\n            language_model_output = self.language_model(\n                input_ids=input_ids,\n                attention_mask=None,\n                position_ids=None,\n                past_key_values=past_key_values,\n                inputs_embeds=None,\n                labels=None,\n                use_cache=use_cache,\n                output_attentions=output_attentions,\n                output_hidden_states=output_hidden_states,\n                return_dict=return_dict,\n            )\n\n        # === Handle Unimodal Forward ===\n        elif pixel_values is None:\n            assert (input_ids is not None) and (inputs_embeds is None), \"Missing `input_ids` in language-only forward!\"\n            assert past_key_values is None, \"Unexpected key `past_key_values` provided during language-only forward!\"\n\n            language_model_output = self.language_model(\n                input_ids=input_ids,\n                attention_mask=attention_mask,\n                position_ids=None,\n                past_key_values=None,\n                inputs_embeds=None,\n                labels=labels,\n                use_cache=use_cache,\n                output_attentions=output_attentions,\n                output_hidden_states=output_hidden_states,\n                return_dict=return_dict,\n            )\n\n        # === Handle Multimodal Forward ===\n        elif pixel_values is not None:\n            assert past_key_values is None, \"Unexpected key `past_key_values` provided during language-only forward!\"\n\n            # Visual Feature Extraction\n            patch_features = self.vision_backbone(pixel_values, grid_size=16) \n            \n            projected_patch_embedding = []\n            for patch_feature in patch_features:\n                projected_patch_embedding.append(self.projector(patch_feature))\n\n            projected_patch_embeddings = torch.cat(projected_patch_embedding[::1], dim=1)\n            projected_patch_attention_mask = None\n\n            if attention_mask is not None:\n                projected_patch_attention_mask = torch.full(\n                    (projected_patch_embeddings.shape[0], projected_patch_embeddings.shape[1]),\n                    True,\n                    dtype=attention_mask.dtype,\n                    device=attention_mask.device,\n                )\n\n            # Get Input Embeddings (from Language Model Embeddings)\n            input_embeddings = self.get_input_embeddings()(input_ids)\n\n            # Build Multimodal Embeddings & Attention Mask =>> Prismatic defaults to inserting after <BOS> token (1:)\n            multimodal_embeddings = torch.cat(\n                [input_embeddings[:, :1, :], projected_patch_embeddings, input_embeddings[:, 1:, :]], dim=1\n            )\n\n            multimodal_attention_mask = None\n            if attention_mask is not None:\n                multimodal_attention_mask = torch.cat(\n                    [attention_mask[:, :1], projected_patch_attention_mask, attention_mask[:, 1:]], dim=1\n                )\n\n            # Build Labels (if specified) =>> Ignore Labels for Patch Embeddings\n            multimodal_labels = None\n            if labels is not None:\n                projected_patch_labels = torch.full(\n                    (projected_patch_embeddings.shape[0], projected_patch_embeddings.shape[1]),\n                    fill_value=IGNORE_INDEX,\n                    dtype=labels.dtype,\n                    device=labels.device,\n                )\n                multimodal_labels = torch.cat([labels[:, :1], projected_patch_labels, labels[:, 1:]], dim=1)\n\n            # Dispatch to Language Model\n            language_model_output = self.language_model(\n                input_ids=None,\n                attention_mask=multimodal_attention_mask,\n                position_ids=None,\n                past_key_values=None,\n                inputs_embeds=multimodal_embeddings,\n                labels=multimodal_labels,\n                use_cache=use_cache,\n                output_attentions=output_attentions,\n                output_hidden_states=output_hidden_states,\n                return_dict=return_dict,\n            )\n\n        # === Otherwise =>> Assume Invalid! ===\n        elif (input_ids.shape[0] != pixel_values.shape[0]) or (inputs_embeds.shape[0] != pixel_values.shape[0]):\n            raise ValueError(\"Non-homogenous batch of (text, image) input -- forward() does not support mixed batches!\")\n\n        else:\n            raise ValueError(\n                \"Invalid PrismaticForConditionalGeneration `forward()` call with provided arguments:\\n\"\n                f\"=> `input_ids` = {input_ids is not None}\\n\"\n                f\"=> `attention_mask` = {attention_mask is not None}\\n\"\n                f\"=> `pixel_values` = {pixel_values is not None}\\n\"\n                f\"=> `labels` = {labels is not None}\\n\"\n                f\"=> `input_embeds` = {inputs_embeds is not None}\\n\"\n                f\"=> `past_key_values` = {past_key_values is not None}\\n\"\n                f\"=> `use_cache` = {use_cache}\"\n            )\n\n        # Unpack `language_model_output` and return PrismaticCausalLMOutputWithPast (or tuple if not `return_dict`)\n        if not return_dict:\n            if output_projector_features and (projected_patch_embeddings is not None):\n                return *language_model_output, projected_patch_embeddings\n\n            return language_model_output\n\n        return PrismaticCausalLMOutputWithPast(\n            loss=language_model_output.loss,\n            logits=language_model_output.logits,\n            past_key_values=language_model_output.past_key_values,\n            hidden_states=language_model_output.hidden_states,\n            attentions=language_model_output.attentions,\n            projector_features=projected_patch_embeddings,\n        )\n\n    # === GenerationMixin Methods ===\n    def prepare_inputs_for_generation(\n        self,\n        input_ids: Optional[torch.Tensor] = None,\n        past_key_values: Optional[List[torch.FloatTensor]] = None,\n        inputs_embeds: Optional[torch.FloatTensor] = None,\n        pixel_values: Optional[torch.FloatTensor] = None,\n        attention_mask: Optional[torch.Tensor] = None,\n        **kwargs: str,\n    ) -> Dict[str, torch.Tensor]:\n        \"\"\"Borrowed from `LlamaForCausalLM` and simplified for batch size = 1; mirrors original PrismaticVLM logic.\"\"\"\n        if ((input_ids is not None) and (input_ids.shape[0] > 1)) or (\n            (inputs_embeds is not None) and (inputs_embeds.shape[0] > 1)\n        ):\n            raise ValueError(\"Generation with batch size > 1 is not currently supported!\")\n\n        # Handle `past_key_values` (cache) =>> assume `input_ids` just has unprocessed tokens\n        if past_key_values is not None:\n            input_ids = input_ids[:, -1:]\n\n        # If `input_embeds` are passed, we only want to use them in the 1st generation step\n        if inputs_embeds is not None and past_key_values is None:\n            model_inputs = {\"input_embeds\": inputs_embeds}\n        else:\n            model_inputs = {\"input_ids\": input_ids}\n\n        # Make sure `pixel_values` are preserved in `model_inputs`\n        model_inputs.update(\n            {\n                \"attention_mask\": attention_mask,\n                \"pixel_values\": pixel_values,\n                \"past_key_values\": past_key_values,\n                \"use_cache\": kwargs.get(\"use_cache\"),\n            }\n        )\n\n        return model_inputs\n\n    # Defer to Language Model (all handle this differently, with different return types)\n    def _reorder_cache(self, *args, **kwargs) -> Any:\n        return self.language_model._reorder_cache(*args, **kwargs)\n\n\nclass OpenVLAForActionPrediction(PrismaticForConditionalGeneration):\n    config_class: PretrainedConfig = OpenFlyConfig\n\n    def __init__(self, config: OpenFlyConfig) -> None:\n        super().__init__(config)\n        self.norm_stats = config.norm_stats\n\n        # Compute action bins\n        self.bins = np.linspace(-1, 1, config.n_action_bins)\n        self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0\n\n        # Compute vocab size for de-tokenization -- revert added \"multiple of\"\n        self.vocab_size = self.config.text_config.vocab_size - self.config.pad_to_multiple_of\n\n    def predict_action(\n        self, input_ids: Optional[torch.LongTensor] = None, unnorm_key: Optional[str] = None, **kwargs: str\n    ) -> np.ndarray:\n        \"\"\"Thin wrapper around .generate() that decodes predicted actions and unnormalizes them.\"\"\"\n        # If the special empty token ('') does not already appear after the colon (':') token in the prompt\n        # (after \"OUT:\" or \"ASSISTANT:\"), insert it to match the inputs seen at training time\n        if not torch.all(input_ids[:, -1] == 29871):\n            input_ids = torch.cat(\n                (input_ids, torch.unsqueeze(torch.Tensor([29871]).long(), dim=0).to(input_ids.device)), dim=1\n            )\n\n        # Run VLA inference\n        generated_ids = self.generate(input_ids, max_new_tokens=self.get_action_dim(unnorm_key), **kwargs)\n\n        # Extract predicted action tokens and translate into (normalized) continuous actions\n        predicted_action_token_ids = generated_ids[0, -self.get_action_dim(unnorm_key) :].cpu().numpy()\n        discretized_actions = self.vocab_size - predicted_action_token_ids\n        discretized_actions = np.clip(discretized_actions - 1, a_min=0, a_max=self.bin_centers.shape[0] - 1)\n        normalized_actions = self.bin_centers[discretized_actions]\n\n        # Unnormalize actions\n        action_norm_stats = self.get_action_stats(unnorm_key)\n        mask = action_norm_stats.get(\"mask\", np.ones_like(action_norm_stats[\"q01\"], dtype=bool))\n        action_high, action_low = np.array(action_norm_stats[\"q99\"]), np.array(action_norm_stats[\"q01\"])\n        actions = np.where(\n            mask,\n            0.5 * (normalized_actions + 1) * (action_high - action_low) + action_low,\n            normalized_actions,\n        )\n\n        return actions\n\n    @staticmethod\n    def _check_unnorm_key(norm_stats: Dict[str, Dict[str, Any]], unnorm_key: Optional[str]) -> str:\n        if unnorm_key is None:\n            assert len(norm_stats) == 1, (\n                f\"Your model was trained on more than one dataset, \"\n                f\"please pass a `unnorm_key` from the following options to choose the statistics \"\n                f\"used for un-normalizing actions: {norm_stats.keys()}\"\n            )\n            unnorm_key = next(iter(norm_stats.keys()))\n\n        assert unnorm_key in norm_stats, (\n            f\"The `unnorm_key` you chose is not in the set of available dataset statistics, \"\n            f\"please choose from: {norm_stats.keys()}\"\n        )\n        return unnorm_key\n\n    def get_action_dim(self, unnorm_key: Optional[str] = None) -> int:\n        \"\"\"Get the dimensionality of the policy's action space.\"\"\"\n        unnorm_key = self._check_unnorm_key(self.norm_stats, unnorm_key)\n        return len(self.norm_stats[unnorm_key][\"action\"][\"q01\"])\n\n    def get_action_stats(self, unnorm_key: Optional[str] = None) -> Dict[str, Any]:\n        \"\"\"Get all the logged statistics for the given dataset.\"\"\"\n        unnorm_key = self._check_unnorm_key(self.norm_stats, unnorm_key)\n        return self.norm_stats[unnorm_key][\"action\"]\n"
  },
  {
    "path": "train/extern/hf/processing_prismatic.py",
    "content": "\"\"\"\nprocessing_prismatic.py\n\nHuggingFace-style preprocessor definitions for Prismatic VLMs, inheriting from `ProcessorMixin`. Default configuration\nspecifies `siglip-224px+7b`.\n\"\"\"\n\nfrom typing import Any, ClassVar, List, Optional, Tuple, Union\n\nimport timm.data\nimport torch\nimport torchvision.transforms.functional as TVF\nfrom PIL import Image\nfrom torchvision.transforms import CenterCrop, Compose, Normalize, Resize, ToTensor\nfrom transformers import PreTrainedTokenizerBase\nfrom transformers.image_processing_utils import BatchFeature, ImageProcessingMixin\nfrom transformers.processing_utils import ProcessorMixin\nfrom transformers.tokenization_utils import PaddingStrategy, PreTokenizedInput, TextInput, TruncationStrategy\nfrom transformers.utils import TensorType\n\n\n# === Image Processing ===\ndef letterbox_pad_transform(image: Image.Image, padding_fill_value: Tuple[int, int, int]) -> Image.Image:\n    \"\"\"Given a PIL.Image, pad to square by adding a symmetric border around the height/width.\"\"\"\n    (w, h), max_wh = image.size, max(image.size)\n    horizontal_pad, vertical_pad = int((max_wh - w) / 2), int((max_wh - h) / 2)\n    padding = (horizontal_pad, vertical_pad, horizontal_pad, vertical_pad)\n\n    return TVF.pad(image, padding, fill=padding_fill_value, padding_mode=\"constant\")\n\n\nclass PrismaticImageProcessor(ImageProcessingMixin):\n    model_input_names: ClassVar[List[str]] = [\"pixel_values\"]\n\n    def __init__(\n        self,\n        use_fused_vision_backbone: bool = False,\n        image_resize_strategy: str = \"letterbox\",\n        input_sizes: Optional[List[Tuple[int, int, int]]] = None,\n        interpolations: Optional[List[str]] = None,\n        means: Optional[List[Tuple[float, float, float]]] = None,\n        stds: Optional[List[Tuple[float, float, float]]] = None,\n        **kwargs: str,\n    ) -> None:\n        \"\"\"\n        Initialize a PrismaticImageProcessor as a wrapper around a torchvision transform; this transform will be\n        created by TIMM, and edited to follow our custom `image_resize_strategy` logic.\n        @param use_fused_vision_backbone: Boolean indicating single or fused (dual) vision backbone\n        @param image_resize_strategy: Prismatic image resize strategy in < resize-naive | resize-crop | letterbox >\n        @param input_size: [TIMM :: `data_cfg`] Input image size as tuple (channels, width, height)\n        @param interpolation: [TIMM :: `data_cfg`] Interpolation as string (default: \"bicubic\")\n        @param mean: [TIMM :: `data_cfg`] Normalization mean as float tuple (or two-tuple if `fused_backbone`)\n        @param std: [TIMM :: `data_cfg`] Normalization std as float tuple (or two-tuple if `fused_backbone`)\n        \"\"\"\n        self.use_fused_vision_backbone = use_fused_vision_backbone\n        self.image_resize_strategy = image_resize_strategy\n\n        # Handle `None` default values\n        input_sizes = [(3, 224, 224)] if input_sizes is None else input_sizes\n        means = [(0.5, 0.5, 0.5)] if means is None else means\n        stds = [(0.5, 0.5, 0.5)] if stds is None else stds\n\n        # TIMM `data_cfg` Parameters\n        self.input_sizes, self.interpolations, self.means, self.stds = input_sizes, interpolations, means, stds\n\n        # Grab torchvision transforms via TIMM =>> need to parse for specific \"functional\" transform values!\n        self.tvf_resize_params, self.tvf_crop_params, self.tvf_normalize_params = [], [], []\n        self.tvf_do_letterbox, self.tvf_letterbox_fill = False, None\n\n        for idx in range(len(input_sizes)):\n            transform = timm.data.create_transform(\n                input_size=self.input_sizes[idx],\n                interpolation=self.interpolations[idx],\n                mean=self.means[idx],\n                std=self.stds[idx],\n                crop_pct=1.0,  # Set to 1.0 to ignore cropping (initial Resize sets `input_size`)\n                crop_mode=\"center\",  # Default crop mode -- no-op when `crop_pct == 1.0`\n                is_training=False,  # No image augmentations when loading the transform!\n            )\n\n            # [Validation] Ensure appropriate transform structure, expected sizes\n            if not (\n                isinstance(transform, Compose)\n                and (len(transform.transforms) == 4)\n                and isinstance(transform.transforms[0], Resize)\n                and isinstance(transform.transforms[1], CenterCrop)\n                and isinstance(transform.transforms[2], ToTensor)\n                and isinstance(transform.transforms[3], Normalize)\n                and (transform.transforms[0].size == self.input_sizes[idx][-1])\n                and (transform.transforms[1].size == self.input_sizes[idx][-2:])\n            ):\n                raise ValueError(f\"Unexpected TIMM image transformation structure/sizes: `{transform}`\")\n\n            # HF Image Processors *must* be JSON-serializable; as such, cannot have torchvision. as an attribute.\n            #   => Instead, we're going to parse the transform and call \"torchvision.transforms.functional\" (`tvf`)\n            resize_t, crop_t, norm_t = transform.transforms[0], transform.transforms[1], transform.transforms[3]\n            self.tvf_resize_params.append(\n                {\n                    \"size\": resize_t.size,\n                    \"interpolation\": TVF.pil_modes_mapping[resize_t.interpolation],\n                    \"max_size\": None,\n                    \"antialias\": True,\n                }\n            )\n            self.tvf_crop_params.append({\"output_size\": crop_t.size})\n            self.tvf_normalize_params.append(\n                {\n                    \"mean\": norm_t.mean.float().numpy().tolist(),\n                    \"std\": norm_t.std.float().numpy().tolist(),\n                    \"inplace\": False,\n                }\n            )\n            self.tvf_do_letterbox, self.tvf_letterbox_fill = False, None\n\n            # Handle Prismatic `image_resize_strategy`\n            if self.image_resize_strategy == \"resize-naive\":\n                self.tvf_resize_params[idx][\"size\"] = (resize_t.size, resize_t.size)\n            elif self.image_resize_strategy == \"letterbox\":\n                self.tvf_do_letterbox, self.tvf_letterbox_fill = True, tuple([int(x * 255) for x in self.means[idx]])\n            elif self.image_resize_strategy == \"resize-crop\":\n                pass\n            else:\n                raise ValueError(f\"Image resize strategy `{self.image_resize_strategy}` is not supported!\")\n\n        # Dispatch **kwargs to super()\n        super().__init__(**kwargs)\n\n    def apply_transform(self, img: Image.Image) -> torch.Tensor:\n        \"\"\"Apply `functional` variant of TIMM's Transform = Compose([Resize -> CenterCrop -> ToTensor -> Normalize])\"\"\"\n        if self.tvf_do_letterbox:\n            img = letterbox_pad_transform(img, self.tvf_letterbox_fill)\n\n        # [Contract] Fused Backbones expect \"channel-stacked\" inputs; we'll unpack on the model side!\n        imgs_t = []\n        for idx in range(len(self.input_sizes)):\n            img_idx = TVF.resize(img, **self.tvf_resize_params[idx])\n            img_idx = TVF.center_crop(img_idx, **self.tvf_crop_params[idx])\n            img_idx_t = TVF.to_tensor(img_idx)\n            img_idx_t = TVF.normalize(img_idx_t, **self.tvf_normalize_params[idx])\n            imgs_t.append(img_idx_t)\n\n        # [Contract] `imgs_t` is a list of Tensors of shape [3, input_size, input_size]; stack along dim = 0\n        img_t = torch.vstack(imgs_t)\n\n        return img_t\n\n    def preprocess(\n        self,\n        images: Union[Image.Image, List[Image.Image]],\n        return_tensors: Optional[Union[str, TensorType]] = None,\n        **_: str,\n    ) -> BatchFeature:\n        \"\"\"\n        Preprocess an image (or batch of images); note that unlike the `transformers :: BaseImageProcessor` we\n        explicitly only handle PIL.Image.Image instances for simplicity.\n        @param images: A (batch of) PIL.Image.Image instance(s) to preprocess.\n        @param return_tensors: BatchFeature default Tensor format (e.g., \"pt\" for torch); if None, returns np.ndarray\n        @return: Instance of `transformers :: BatchFeature` with a single key \"pixel_values\"\n        \"\"\"\n        if not isinstance(images, list):\n            images = [images]\n\n        # Apply `self.img_transform` to each image (will return list of torch.Tensors); stack into \"batched\" Tensor\n        pixel_values = torch.stack([self.apply_transform(img.convert(\"RGB\")) for img in images])\n\n        # Return BatchFeature =>> note that for compatibility, constructor expects Dict[str, np.ndarray], so we convert\n        return BatchFeature(data={\"pixel_values\": pixel_values.float().numpy()}, tensor_type=return_tensors)\n\n    def __call__(self, images: Union[Image.Image, List[Image.Image]], **kwargs) -> BatchFeature:\n        return self.preprocess(images, **kwargs)\n\n\n# === PrismaticProcessor =>> Wraps both ImageProcessor and Tokenizer ===\n#   =>> https://github.com/huggingface/transformers/blob/main/src/transformers/models/llava/processing_llava.py\nclass PrismaticProcessor(ProcessorMixin):\n    attributes: ClassVar[List[str]] = [\"image_processor\", \"tokenizer\"]\n    image_processor_class: str = \"AutoImageProcessor\"\n    tokenizer_class: str = \"AutoTokenizer\"\n\n    def __init__(\n        self,\n        image_processor: Optional[ImageProcessingMixin] = None,\n        tokenizer: Optional[PreTrainedTokenizerBase] = None,\n    ) -> None:\n        super().__init__(image_processor, tokenizer)\n\n    def __call__(\n        self,\n        text: Union[TextInput, PreTokenizedInput, List[TextInput], List[PreTokenizedInput]],\n        images: Union[Image.Image, List[Image.Image]],\n        padding: Union[bool, str, PaddingStrategy] = False,\n        truncation: Optional[Union[bool, str, TruncationStrategy]] = None,\n        max_length: Optional[int] = None,\n        return_tensors: Optional[Union[str, TensorType]] = TensorType.PYTORCH,\n    ) -> BatchFeature:\n        \"\"\"\n        Preprocess a given (batch) of text/images for a Prismatic VLM; forwards text to the underlying LLM's tokenizer,\n        forwards images to PrismaticImageProcessor.\n        @param text: The (batch) of text to encode; must be a string or list of strings.\n        @param images: A (batch of) PIL.Image.Image instance(s) to preprocess.\n        @param padding: Sequence padding strategy (if multiple specified) in < True = \"longest\" | \"max_length\" | False >\n        @param truncation: Truncation strategy for the output sequences; requires `max_length` to be specified\n        @param max_length: Maximum length (in tokens) to truncate\n        @param return_tensors: Type of return tensors (usually \"pt\" or TensorType.PYTORCH)\n        @return: BatchFeature with keys for `input_ids`, `attention_mask` and `pixel_values`.\n        \"\"\"\n        pixel_values = self.image_processor(images, return_tensors=return_tensors)[\"pixel_values\"]\n        text_inputs = self.tokenizer(\n            text, return_tensors=return_tensors, padding=padding, truncation=truncation, max_length=max_length\n        )\n\n        return BatchFeature(data={**text_inputs, \"pixel_values\": pixel_values})\n\n    # === Tokenizer Dispatch Utilities =>> check `PreTrainedTokenizerBase` for documentation ===\n    def batch_decode(\n        self,\n        sequences: Union[List[int], List[List[int]], torch.Tensor, Any],  # `Any` = np.ndarray | tf.Tensor\n        skip_special_tokens: bool = False,\n        clean_up_tokenization_spaces: Optional[bool] = None,\n        **kwargs: str,\n    ) -> List[str]:\n        return self.tokenizer.batch_decode(\n            sequences=sequences,\n            skip_special_tokens=skip_special_tokens,\n            clean_up_tokenization_spaces=clean_up_tokenization_spaces,\n            **kwargs,\n        )\n\n    def decode(\n        self,\n        token_ids: Union[int, List[int], torch.Tensor, Any],  # `Any` = np.ndarray | tf.Tensor\n        skip_special_tokens: bool = False,\n        clean_up_tokenization_spaces: Optional[bool] = None,\n        **kwargs: str,\n    ) -> str:\n        return self.tokenizer.decode(\n            token_ids=token_ids,\n            skip_special_tokens=skip_special_tokens,\n            clean_up_tokenization_spaces=clean_up_tokenization_spaces,\n            **kwargs,\n        )\n\n    @property\n    def model_input_names(self) -> List[str]:\n        tokenizer_input_names = self.tokenizer.model_input_names\n        image_processor_input_names = self.image_processor.model_input_names\n\n        return list(dict.fromkeys(tokenizer_input_names + image_processor_input_names))\n"
  },
  {
    "path": "train/model/__init__.py",
    "content": "from .config import OpenFlyConfig\nfrom .load_model import load_vla\nfrom .action_tokenizer import ActionTokenizer\nfrom .llm_backbone import LLaMa2LLMBackbone\nfrom .vision_backbone import DinoSigLIPViTBackbone, DinoSigLIPImageTransform\nfrom .overwatch import initialize_overwatch\n\n\nfrom .base_prompter import PromptBuilder, PurePromptBuilder\nfrom .prompt_llama2 import LLaMa2ChatPromptBuilder\n"
  },
  {
    "path": "train/model/action_tokenizer.py",
    "content": "\"\"\"\naction_tokenizer.py\n\nExtension class; wraps base LLM/VLM tokenizer with logic to discretize and tokenize continuous robot actions.\n\"\"\"\n\nfrom typing import List, Union\n\nimport numpy as np\nfrom transformers import PreTrainedTokenizerBase\n\n\nclass ActionTokenizer:\n    def __init__(\n        self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1\n    ) -> None:\n        \"\"\"\n        Discretizes continuous robot actions into N bins per dimension and maps to the least used tokens.\n\n        NOTE =>> by default, assumes a BPE-style tokenizer akin to the LlamaTokenizer, where *the least used tokens*\n                 appear at the end of the vocabulary!\n\n        :param tokenizer: Base LLM/VLM tokenizer to extend.\n        :param bins: Number of bins for each continuous value; we'll adopt a uniform binning strategy.\n        :param min_action: Minimum action value (for clipping, setting lower bound on bin interval).\n        :param max_action: Maximum action value (for clipping, setting upper bound on bin interval).\n        \"\"\"\n        self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action\n\n        # Create Uniform Bins + Compute Bin Centers\n        self.bins = np.linspace(min_action, max_action, self.n_bins)\n        self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0\n\n        # [Contract] Set \"action_token_begin_idx\" based on `self.tokenizer.vocab_size - (self.n_bins + 1)`\n        #   =>> Assumes we're always overwriting the final `n_bins` tokens of the vocabulary!\n        self.action_token_begin_idx: int = int(self.tokenizer.vocab_size - (self.n_bins + 1))\n\n    def __call__(self, action: np.ndarray) -> Union[str, List[str]]:\n        \"\"\"Clip & bin actions to *the last `n_bins` tokens* of the vocabulary (e.g., tokenizer.vocab[-256:]).\"\"\"\n        action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action))\n        discretized_action = np.digitize(action, self.bins)\n\n        # Handle single element vs. batch\n        if len(discretized_action.shape) == 1:\n            return self.tokenizer.decode(list(self.tokenizer.vocab_size - discretized_action))\n        else:\n            return self.tokenizer.batch_decode((self.tokenizer.vocab_size - discretized_action).tolist())\n\n    def decode_token_ids_to_actions(self, action_token_ids: np.ndarray) -> np.ndarray:\n        \"\"\"\n        Returns continuous actions for discrete action token IDs.\n\n        NOTE =>> Because of the way the actions are discretized w.r.t. the bins (and not the bin centers), the\n                 digitization returns bin indices between [1, # bins], inclusive, when there are actually only\n                 (# bins - 1) bin intervals.\n\n                 Therefore, if the digitization returns the last possible index, we map this to the last bin interval.\n\n        EXAMPLE =>> Let's say self._bins has 256 values. Then self._bin_centers has 255 values. Digitization returns\n                    indices between [1, 256]. We subtract 1 from all indices so that they are between [0, 255]. There\n                    is still one index (i==255) that would cause an out-of-bounds error if used to index into\n                    self._bin_centers. Therefore, if i==255, we subtract 1 from it so that it just becomes the index of\n                    the last bin center. We implement this simply via clipping between [0, 255 - 1].\n        \"\"\"\n        discretized_actions = self.tokenizer.vocab_size - action_token_ids\n        discretized_actions = np.clip(discretized_actions - 1, a_min=0, a_max=self.bin_centers.shape[0] - 1)\n\n        return self.bin_centers[discretized_actions]\n\n    @property\n    def vocab_size(self) -> int:\n        return self.n_bins\n"
  },
  {
    "path": "train/model/base_prompter.py",
    "content": "\"\"\"\nbase_prompter.py\n\nAbstract class definition of a multi-turn prompt builder for ensuring consistent formatting for chat-based LLMs.\n\"\"\"\n\nfrom abc import ABC, abstractmethod\nfrom typing import Optional\n\n\nclass PromptBuilder(ABC):\n    def __init__(\n        self, model_family: str, system_prompt: Optional[str] = None, eos=None, bos=None\n    ) -> None:\n        self.model_family = model_family\n\n        # Only some models define a system prompt => let subclasses handle this logic!\n        self.system_prompt = system_prompt\n        self.eos = eos\n        self.bos = bos\n\n    @abstractmethod\n    def add_turn(self, role: str, message: str) -> str:\n        ...\n\n    @abstractmethod\n    def get_potential_prompt(self, user_msg: str) -> None:\n        ...\n\n    @abstractmethod\n    def get_prompt(self) -> str:\n        ...\n\n\nclass PurePromptBuilder(PromptBuilder):\n    def __init__(\n        self, model_family: str, system_prompt: Optional[str] = None, eos=None, bos=None\n    ) -> None:\n        super().__init__(model_family, system_prompt, eos, bos)\n\n        # TODO (siddk) =>> Can't always assume LlamaTokenizer --> FIX ME!\n        if self.bos is None and self.eos is None:\n            self.bos, self.eos = \"<s>\", \"</s>\"\n\n        # Get role-specific \"wrap\" functions\n        self.wrap_human = lambda msg: f\"In: {msg}\\nOut: \"\n        self.wrap_gpt = lambda msg: f\"{msg if msg != '' else ' '}{self.eos}\"\n\n        # === `self.prompt` gets built up over multiple turns ===\n        self.prompt, self.turn_count = \"\", 0\n\n    def add_turn(self, role: str, message: str) -> str:\n        assert (role == \"human\") if (self.turn_count % 2 == 0) else (role == \"gpt\")\n        message = message.replace(\"<image>\", \"\").strip()\n\n        if (self.turn_count % 2) == 0:\n            human_message = self.wrap_human(message)\n            wrapped_message = human_message\n        else:\n            gpt_message = self.wrap_gpt(message)\n            wrapped_message = gpt_message\n\n        # Update Prompt\n        self.prompt += wrapped_message\n\n        # Bump Turn Counter\n        self.turn_count += 1\n\n        # Return \"wrapped_message\" (effective string added to context)\n        return wrapped_message\n\n    def get_potential_prompt(self, message: str) -> None:\n        # Assumes that it's always the user's (human's) turn!\n        prompt_copy = str(self.prompt)\n\n        human_message = self.wrap_human(message)\n        prompt_copy += human_message\n\n        return prompt_copy.removeprefix(self.bos).rstrip()\n\n    def get_prompt(self) -> str:\n        # Remove prefix <bos> (if exists) because it gets auto-inserted by tokenizer!\n        # return self.prompt.removeprefix(self.bos).rstrip()\n        return self.prompt.replace(self.bos, \"\").rstrip()"
  },
  {
    "path": "train/model/config.py",
    "content": "# coding=utf-8\n# Copyright 2024 Microsoft Research & University of Wisconsin-Madison and the HuggingFace Inc. team. All rights reserved.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"OpenFly configuration\"\"\"\n\nfrom __future__ import annotations\nfrom typing import Callable, List, Optional\n\nimport torch\nimport torch.nn as nn\nfrom transformers import PretrainedConfig\nfrom transformers import CONFIG_MAPPING\n\n\n\nclass OpenFlyConfig(PretrainedConfig):\n    model_type: str = \"prismatic\"\n    is_composition: bool = False\n\n    # from  openvla/prismatic/extern/hf/configuration_prismatic.py  \n    def __init__(\n        self,\n        vision_backbone_id: str = \"siglip-vit-so400m\",\n        llm_backbone_id: str = \"llama2-7b-pure\",\n        arch_specifier: str = \"no-align+gelu-mlp\",\n        use_fused_vision_backbone: Optional[bool] = None,\n        image_resize_strategy: str = \"letterbox\",\n        text_config: Optional[Dict[str, Any]] = None,\n        llm_max_length: int = 2048,\n        pad_token_id: int = 32000,\n        pad_to_multiple_of: int = 64,\n        output_projector_states: bool = False,\n        norm_stats: Optional[Dict[str, Dict[str, Dict[str, Dict[str, List[float]]]]]] = None,\n        n_action_bins: int = 256,\n        **kwargs: str,\n    ) -> None:\n        \n        self.tokenizer.add_special_tokens({\"pad_token\": \"<PAD>\"})\n        self.llm.config.pad_token_id = self.tokenizer.pad_token_id\n        self.llm.resize_token_embeddings(len(self.tokenizer), pad_to_multiple_of=64)\n\n        # Set Prismatic Configuration Fields\n        self.vision_backbone_id = vision_backbone_id\n        self.llm_backbone_id = llm_backbone_id\n        self.arch_specifier = arch_specifier\n        self.output_projector_states = output_projector_states\n\n        # [Contract] All vision backbone parameters are lists =>> supports fused backbones with different preprocessing\n        self.use_fused_vision_backbone = (\n            use_fused_vision_backbone\n            if use_fused_vision_backbone is not None\n            else any(self.vision_backbone_id.startswith(v) for v in [\"dinoclip\", \"dinosiglip\"])\n        )\n\n\n        self.timm_model_ids = \"vit_so400m_patch14_siglip_224\"\n        self.timm_override_act_layers = None\n        self.image_sizes = [224, 224]\n        self.image_resize_strategy = \"letterbox\"\n\n        self.hf_llm_id = \"meta-llama/Llama-2-7b-hf\"\n        self.llm_max_length = self.llm_max_length\n        self.pad_token_id, self.pad_to_multiple_of = pad_token_id, pad_to_multiple_of\n\n        # [IMPORTANT] HF Utilities actually look for a `text_config` field... we need to use that specific naming!\n        self.text_config = (\n            CONFIG_MAPPING[\"llama\"](**text_config)\n            if text_config is not None\n            else CONFIG_MAPPING[\"llama\"]()\n        )\n\n\n    "
  },
  {
    "path": "train/model/llm_backbone.py",
    "content": "import torch\nimport torch.nn as nn\nfrom torch.distributed.fsdp.wrap import transformer_auto_wrap_policy\nfrom functools import partial\nfrom typing import Optional, List, Type, Callable, Sequence\nfrom transformers import (\n    AutoTokenizer,\n    AutoConfig,\n    PreTrainedModel,\n    PreTrainedTokenizerBase,\n    LlamaForCausalLM,\n    LlamaTokenizerFast,\n    LlamaConfig,\n)\nfrom transformers.models.llama.modeling_llama import LlamaDecoderLayer\nfrom transformers.modeling_outputs import CausalLMOutputWithPast\n\n\nfrom model.base_prompter import PromptBuilder\nfrom model.prompt_llama2 import LLaMa2ChatPromptBuilder\n\nclass LLaMa2LLMBackbone(nn.Module):\n    def __init__(\n        self,\n        llm_max_length: int = 2048,\n        hf_token: Optional[str] = None,\n        inference_mode: bool = False,\n        use_flash_attention_2: bool = True,\n    ) -> None:\n        super().__init__()\n        self.llm_max_length = llm_max_length\n        self.inference_mode = inference_mode\n\n        # Get model configuration from preset\n        # hf_hub_path = model_config[\"hf_hub_path\"]\n        hf_hub_path = \"meta-llama/llama2-7b-hf\"\n\n        # Model initialization\n        if not inference_mode:\n            self.llm = LlamaForCausalLM.from_pretrained(\n                hf_hub_path,\n                token=hf_token,\n                use_flash_attention_2=use_flash_attention_2,\n                do_sample=False,\n                temperature=1.0,\n                top_p=1.0,\n            )\n        else:\n            llm_config = AutoConfig.from_pretrained(hf_hub_path, token=hf_token)\n            self.llm = LlamaForCausalLM._from_config(llm_config)\n\n        # Configuration settings\n        self.llm.config.use_cache = False if not inference_mode else True\n        if not inference_mode:\n            self.llm.enable_input_require_grads()\n\n        # Tokenizer initialization\n        self.tokenizer = AutoTokenizer.from_pretrained(\n            hf_hub_path,\n            model_max_length=self.llm_max_length,\n            token=hf_token,\n            padding_side=\"right\"\n        )\n\n        # Handle special tokens for LLaMA2\n        self.tokenizer.add_special_tokens({\"pad_token\": \"<PAD>\"})\n        self.llm.config.pad_token_id = self.tokenizer.pad_token_id\n        self.llm.resize_token_embeddings(len(self.tokenizer), pad_to_multiple_of=64)\n\n    def get_tokenizer(self) -> PreTrainedTokenizerBase:\n        return self.tokenizer\n\n    def get_fsdp_wrapping_policy(self) -> Callable:\n        return partial(\n            transformer_auto_wrap_policy,\n            transformer_layer_cls={LlamaDecoderLayer}\n        )\n\n    def enable_gradient_checkpointing(self) -> None:\n        self.llm.gradient_checkpointing_enable()\n\n    def forward(\n        self,\n        input_ids: Optional[torch.LongTensor] = None,\n        attention_mask: Optional[torch.Tensor] = None,\n        position_ids: Optional[torch.LongTensor] = None,\n        past_key_values: Optional[List[torch.FloatTensor]] = None,\n        inputs_embeds: Optional[torch.FloatTensor] = None,\n        labels: Optional[torch.LongTensor] = None,\n        use_cache: Optional[bool] = None,\n        output_attentions: Optional[bool] = None,\n        output_hidden_states: Optional[bool] = None,\n        return_dict: Optional[bool] = None,\n    ) -> CausalLMOutputWithPast:\n        return self.llm(\n            input_ids=input_ids,\n            attention_mask=attention_mask,\n            position_ids=position_ids,\n            past_key_values=past_key_values,\n            inputs_embeds=inputs_embeds,\n            labels=labels,\n            use_cache=use_cache,\n            output_attentions=output_attentions,\n            output_hidden_states=output_hidden_states,\n            return_dict=return_dict,\n        )\n\n    def embed_input_ids(self, input_ids: torch.LongTensor) -> torch.Tensor:\n        return self.llm.get_input_embeddings()(input_ids)\n\n    @property\n    def prompt_builder_fn(self):\n        return LLaMa2ChatPromptBuilder\n        \n    @property\n    def transformer_layer_cls(self) -> Type[nn.Module]:\n        return LlamaDecoderLayer\n\n    @property\n    def half_precision_dtype(self) -> torch.dtype:\n        return torch.bfloat16\n\n    @property\n    def last_layer_finetune_modules(self) -> Sequence[nn.Module]:\n        return (self.llm.model.embed_tokens, self.llm.model.layers[-1], self.llm.lm_head)\n\n    @property\n    def embed_dim(self) -> int:\n        return self.llm.config.hidden_size\n\n    @property\n    def pad_token_id(self) -> int:\n        return self.tokenizer.pad_token_id\n"
  },
  {
    "path": "train/model/load_model.py",
    "content": "\nfrom typing import Dict, List, Optional, Union\nfrom pathlib import Path\nimport numpy as np\nimport torch\nfrom PIL import Image\nfrom transformers import LlamaTokenizerFast\nimport os, json\nfrom model.prismatic import PrismaticVLM\nfrom model.overwatch import initialize_overwatch\nfrom model.action_tokenizer import ActionTokenizer\n\n\nfrom model.vision_backbone import DinoSigLIPViTBackbone, DinoSigLIPImageTransform\nfrom model.llm_backbone import LLaMa2LLMBackbone\n\n\noverwatch = initialize_overwatch(__name__)\n\n\nclass OpenFly(PrismaticVLM):\n    def __init__(\n        self,\n        *args,\n        norm_stats: Dict[str, Dict[str, Dict[str, Dict[str, List[float]]]]],\n        action_tokenizer: ActionTokenizer,\n        **kwargs,\n    ) -> None:\n        super().__init__(*args, **kwargs)\n        self.norm_stats = norm_stats\n        self.action_tokenizer = action_tokenizer\n\n    @torch.inference_mode()\n    def predict_action(\n        self, image: Image, instruction: str, unnorm_key: Optional[str] = None, **kwargs: str\n    ) -> np.ndarray:\n        \"\"\"\n        Core function for VLA inference; maps input image and task instruction to continuous action (de-tokenizes).\n\n        @param image: PIL Image as [height, width, 3]\n        @param instruction: Task instruction string\n        @param unnorm_key: Optional dataset name for retrieving un-normalizing statistics; if None, checks that model\n                           was trained only on a single dataset, and retrieves those statistics.\n\n        @return Unnormalized (continuous) action vector --> end-effector deltas.\n        \"\"\"\n        image_transform, tokenizer = self.vision_backbone.image_transform, self.llm_backbone.tokenizer\n\n        # Build VLA Prompt\n        prompt_builder = self.get_prompt_builder()\n        prompt_builder.add_turn(role=\"human\", message=f\"What action should the robot take to {instruction.lower()}?\")\n        prompt_text = prompt_builder.get_prompt()\n\n        # Prepare Inputs\n        input_ids = tokenizer(prompt_text, truncation=True, return_tensors=\"pt\").input_ids.to(self.device)\n        if isinstance(tokenizer, LlamaTokenizerFast):\n            # Note: We need to add this special empty token ('') after the colon (':') token in \"ASSISTANT:\"\n            #       in order for the predictions to match the training configuration and be accurate.\n            input_ids = torch.cat(\n                (input_ids, torch.unsqueeze(torch.Tensor([29871]).long(), dim=0).to(self.device)), dim=1\n            )\n        else:\n            raise ValueError(f\"Unsupported `tokenizer` type = {type(tokenizer)}\")\n\n        # Preprocess Image\n        pixel_values = image_transform(image)\n        if isinstance(pixel_values, torch.Tensor):\n            pixel_values = pixel_values[None, ...].to(self.device)\n        elif isinstance(pixel_values, dict):\n            pixel_values = {k: v[None, ...].to(self.device) for k, v in pixel_values.items()}\n        else:\n            raise ValueError(f\"Unsupported `pixel_values` type = {type(pixel_values)}\")\n\n        # Invoke super().generate --> taps into `GenerationMixin` which (redirects) to `forward()`\n        autocast_dtype = self.llm_backbone.half_precision_dtype\n        with torch.autocast(\"cuda\", dtype=autocast_dtype, enabled=self.enable_mixed_precision_training):\n            # fmt: off\n            generated_ids = super(PrismaticVLM, self).generate(\n                input_ids=input_ids,                            # Shape: [1, seq]\n                pixel_values=pixel_values,                      # Shape: [1, 3, res, res] or Dict[str, ...]\n                max_new_tokens=self.get_action_dim(unnorm_key),\n                **kwargs\n            )\n            # fmt: on\n\n        # Extract predicted action tokens and translate into (normalized) continuous actions\n        predicted_action_token_ids = generated_ids[0, -self.get_action_dim(unnorm_key) :]\n        normalized_actions = self.action_tokenizer.decode_token_ids_to_actions(predicted_action_token_ids.cpu().numpy())\n\n        # Un-normalize Actions\n        action_norm_stats = self.get_action_stats(unnorm_key)\n        mask = action_norm_stats.get(\"mask\", np.ones_like(action_norm_stats[\"q01\"], dtype=bool))\n        action_high, action_low = np.array(action_norm_stats[\"q99\"]), np.array(action_norm_stats[\"q01\"])\n        actions = np.where(\n            mask,\n            0.5 * (normalized_actions + 1) * (action_high - action_low) + action_low,\n            normalized_actions,\n        )\n\n        return actions\n\n    @staticmethod\n    def _check_unnorm_key(norm_stats: Dict, unnorm_key: str) -> str:\n        if unnorm_key is None:\n            assert len(norm_stats) == 1, (\n                f\"Your model was trained on more than one dataset, please pass a `unnorm_key` from the following \"\n                f\"options to choose the statistics used for un-normalizing actions: {norm_stats.keys()}\"\n            )\n            unnorm_key = next(iter(norm_stats.keys()))\n\n        # Error Handling\n        assert (\n            unnorm_key in norm_stats\n        ), f\"The `unnorm_key` you chose is not in the set of available statistics; choose from: {norm_stats.keys()}\"\n\n        return unnorm_key\n\n    def get_action_dim(self, unnorm_key: Optional[str] = None) -> int:\n        \"\"\"Dimensionality of the policy's action space.\"\"\"\n        unnorm_key = self._check_unnorm_key(self.norm_stats, unnorm_key)\n\n        return len(self.norm_stats[unnorm_key][\"action\"][\"q01\"])\n\n    def get_action_stats(self, unnorm_key: Optional[str] = None) -> Dict:\n        \"\"\"Dimensionality of the policy's action space.\"\"\"\n        unnorm_key = self._check_unnorm_key(self.norm_stats, unnorm_key)\n\n        return self.norm_stats[unnorm_key][\"action\"]\n\n# === Load Pretrained VLA Model ===\ndef load_vla(\n    model_id_or_path: Union[str, Path],\n    hf_token: Optional[str] = None,\n    cache_dir: Optional[Union[str, Path]] = None,\n    load_for_training: bool = False,\n    step_to_load: Optional[int] = None,\n    model_type: str = \"pretrained\",\n    grid_size: int = 16,\n) -> OpenFly:\n    \"\"\"Loads a pretrained OpenVLA from either local disk or the HuggingFace Hub.\"\"\"\n\n    # TODO (siddk, moojink) :: Unify semantics with `load()` above; right now, `load_vla()` assumes path points to\n    #   checkpoint `.pt` file, rather than the top-level run directory!\n    if os.path.isfile(model_id_or_path):\n        overwatch.info(f\"Loading from local checkpoint path `{(checkpoint_pt := Path(model_id_or_path))}`\")\n\n        # [Validate] Checkpoint Path should look like `.../<RUN_ID>/checkpoints/<CHECKPOINT_PATH>.pt`\n        assert (checkpoint_pt.suffix == \".pt\") and (checkpoint_pt.parent.name == \"checkpoints\"), \"Invalid checkpoint!\"\n        run_dir = checkpoint_pt.parents[1]\n\n        # Get paths for `config.json`, `dataset_statistics.json` and pretrained checkpoint\n        config_json, dataset_statistics_json = run_dir / \"config.json\", run_dir / \"dataset_statistics.json\"\n        assert config_json.exists(), f\"Missing `config.json` for `{run_dir = }`\"\n        assert dataset_statistics_json.exists(), f\"Missing `dataset_statistics.json` for `{run_dir = }`\"\n\n    # Otherwise =>> try looking for a match on `model_id_or_path` on the HF Hub (`VLA_HF_HUB_REPO`)\n    else:\n        # Search HF Hub Repo via fsspec API\n        overwatch.info(f\"Checking HF for `{(hf_path := str(Path(VLA_HF_HUB_REPO) / model_type / model_id_or_path))}`\")\n        if not (tmpfs := HfFileSystem()).exists(hf_path):\n            raise ValueError(\"Couldn't find valid HF Hub Path\")\n\n        # Identify Checkpoint to Load (via `step_to_load`)\n        step_to_load = f\"{step_to_load:06d}\" if step_to_load is not None else None\n        valid_ckpts = tmpfs.glob(f\"{hf_path}/checkpoints/step-{step_to_load if step_to_load is not None else ''}*.pt\")\n        if (len(valid_ckpts) == 0) or (step_to_load is not None and len(valid_ckpts) != 1):\n            raise ValueError(f\"Couldn't find a valid checkpoint to load from HF Hub Path `{hf_path}/checkpoints/\")\n\n        # Call to `glob` will sort steps in ascending order (if `step_to_load` is None); just grab last element\n        target_ckpt = Path(valid_ckpts[-1]).name\n\n        overwatch.info(f\"Downloading Model `{model_id_or_path}` Config & Checkpoint `{target_ckpt}`\")\n        with overwatch.local_zero_first():\n            relpath = Path(model_type) / model_id_or_path\n            config_json = hf_hub_download(\n                repo_id=VLA_HF_HUB_REPO, filename=f\"{(relpath / 'config.json')!s}\", cache_dir=cache_dir\n            )\n            dataset_statistics_json = hf_hub_download(\n                repo_id=VLA_HF_HUB_REPO, filename=f\"{(relpath / 'dataset_statistics.json')!s}\", cache_dir=cache_dir\n            )\n            checkpoint_pt = hf_hub_download(\n                repo_id=VLA_HF_HUB_REPO, filename=f\"{(relpath / 'checkpoints' / target_ckpt)!s}\", cache_dir=cache_dir\n            )\n\n    # Load Dataset Statistics for Action Denormalization\n    with open(dataset_statistics_json, \"r\") as f:\n        norm_stats = json.load(f)\n\n    # Load Vision Backbone\n    overwatch.info(f\"Loading Vision Backbone\")\n    vision_backbone = DinoSigLIPViTBackbone(\n        image_resize_strategy = \"resize-naive\",\n        default_image_size=224,\n        grid_size=grid_size,\n    )\n    image_transform = vision_backbone.image_transform\n\n    # Load LLM Backbone --> note `inference_mode = True` by default when calling `load()`\n    overwatch.info(f\"Loading Pretrained LLM via HF Transformers\")\n    llm_backbone = LLaMa2LLMBackbone(llm_max_length=2048,\n            hf_token=hf_token,\n            inference_mode=False,\n        )\n    tokenizer = llm_backbone.get_tokenizer()\n\n    # Create Action Tokenizer\n    action_tokenizer = ActionTokenizer(tokenizer)\n\n    # Load VLM using `from_pretrained` (clobbers HF syntax... eventually should reconcile)\n    overwatch.info(f\"Loading VLA from Checkpoint\")\n    vla = OpenFly.from_pretrained(\n        checkpoint_pt,\n        \"prism-dinosiglip-224px+7b\",\n        vision_backbone,\n        llm_backbone,\n        arch_specifier=\"no-align+fused-gelu-mlp\",\n        freeze_weights=not load_for_training,\n        norm_stats=norm_stats,\n        action_tokenizer=action_tokenizer,\n    )\n\n    return vla\n"
  },
  {
    "path": "train/model/metrics.py",
    "content": "\"\"\"\nmetrics.py\n\nUtility classes defining a Metrics container and multiple Trackers to enable model/stage-specific logging to various\nendpoints (e.g., JSONL local logs, Weights & Biases).\n\"\"\"\n\nimport time\nfrom collections import defaultdict, deque\nfrom pathlib import Path\nfrom typing import Any, Dict, Optional, Protocol, Tuple, Union\n\nimport jsonlines\nimport numpy as np\nimport torch\nimport wandb\n\nfrom model.overwatch import initialize_overwatch\n\n# Initialize Overwatch =>> Wraps `logging.Logger`\noverwatch = initialize_overwatch(__name__)\n\n\n# === Define Tracker Interface ===\nclass Tracker(Protocol):\n    def write_hyperparameters(self) -> None: ...\n\n    def write(self, global_step: int, metrics: Dict[str, Union[int, float]]) -> None: ...\n\n    def finalize(self) -> None: ...\n\n\n# === Individual Tracker Definitions ===\nclass JSONLinesTracker:\n    def __init__(self, run_id: str, run_dir: Path, hparams: Dict[str, Any]) -> None:\n        self.run_id, self.run_dir, self.hparams = run_id, run_dir, hparams\n\n    @overwatch.rank_zero_only\n    def write_hyperparameters(self) -> None:\n        with jsonlines.open(self.run_dir / \"run-metrics.jsonl\", mode=\"w\", sort_keys=True) as js_tracker:\n            js_tracker.write({\"run_id\": self.run_id, \"hparams\": self.hparams})\n\n    @overwatch.rank_zero_only\n    def write(self, _: int, metrics: Dict[str, Union[int, float]]) -> None:\n        with jsonlines.open(self.run_dir / f\"{self.run_id}.jsonl\", mode=\"a\", sort_keys=True) as js_tracker:\n            js_tracker.write(metrics)\n\n    def finalize(self) -> None:\n        return\n\n\nclass WeightsBiasesTracker:\n    def __init__(\n        self,\n        run_id: str,\n        run_dir: Path,\n        hparams: Dict[str, Any],\n        project: str = \"prismatic\",\n        entity: Optional[str] = None,\n        group: str = \"align\",\n    ) -> None:\n        self.run_id, self.run_dir, self.hparams = run_id, run_dir, hparams\n\n        # Get W&B-Specific Initialization Parameters\n        self.project, self.entity, self.group, self.wandb_dir = project, entity, group, self.run_dir\n\n        # Call W&B.init()\n        self.initialize()\n\n    @overwatch.rank_zero_only\n    def initialize(self) -> None:\n        wandb.init(\n            name=self.run_id,\n            dir=self.wandb_dir,\n            config=self.hparams,\n            project=self.project,\n            group=self.group,\n        )\n\n    @overwatch.rank_zero_only\n    def write_hyperparameters(self) -> None:\n        wandb.config = self.hparams\n\n    @overwatch.rank_zero_only\n    def write(self, global_step: int, metrics: Dict[str, Union[int, float]]) -> None:\n        wandb.log(metrics, step=global_step)\n\n    @staticmethod\n    def finalize() -> None:\n        if overwatch.is_rank_zero():\n            wandb.finish()\n\n        # A job gets 210 seconds to get its affairs in order\n        time.sleep(210)\n\n\n# === Core Metrics Container :: Initializes Trackers => Compiles/Pushes Metrics ===\n\n\nclass Metrics:\n    def __init__(\n        self,\n        active_trackers: Tuple[str, ...],\n        run_id: str,\n        run_dir: Path,\n        hparams: Dict[str, Any],\n        stage: str,\n        wandb_project: str = \"prismatic\",\n        wandb_entity: Optional[str] = None,\n        grad_accumulation_steps: int = 1,\n        window_size: int = 128,\n    ) -> None:\n        self.run_id, self.run_dir, self.hparams, self.stage = run_id, run_dir, hparams, stage\n\n        # Initialize Trackers\n        self.trackers = []\n        for tracker_type in active_trackers:\n            if tracker_type == \"jsonl\":\n                tracker = JSONLinesTracker(run_id, run_dir, hparams)\n            elif tracker_type == \"wandb\":\n                tracker = WeightsBiasesTracker(\n                    run_id, run_dir, hparams, project=wandb_project, group=self.stage\n                )\n            else:\n                raise ValueError(f\"Tracker with type `{tracker_type} is not supported!\")\n\n            # Add Hyperparameters --> add to `self.trackers`\n            tracker.write_hyperparameters()\n            self.trackers.append(tracker)\n\n        # Create Universal Metrics Buffers\n        self.global_step, self.start_time, self.step_start_time = 0, time.time(), time.time()\n        self.state = {\n            \"loss_raw\": deque(maxlen=grad_accumulation_steps),\n            \"loss\": deque(maxlen=window_size),\n            \"step_time\": deque(maxlen=window_size),\n            \"lr\": [],\n        }\n\n    def log(self, global_step: int, metrics: Dict[str, Union[int, float]]) -> None:\n        for tracker in self.trackers:\n            tracker.write(global_step, metrics)\n\n    def get_status(self, loss: Optional[torch.Tensor] = None) -> str:\n        lr = self.state[\"lr\"][-1] if len(self.state[\"lr\"]) > 0 else 0\n        if loss is None:\n            return f\"=>> [Global Step] {self.global_step:06d} =>> LR :: {lr:.6f}\"\n\n        # Otherwise, embed `loss` in status report!\n        return f\"=>> [Global Step] {self.global_step:06d} =>> LR :: {lr:.6f} -- Loss :: {loss:.4f}\"\n\n    def commit(\n        self, *, global_step: Optional[int] = None, lr: Optional[float] = None, update_step_time: bool = False, **kwargs\n    ) -> None:\n        \"\"\"Update all metrics in `self.state` by iterating through special positional arguments & kwargs.\"\"\"\n        if global_step is not None:\n            self.global_step = global_step\n\n        # For all other variables --> only track on rank zero!\n        if not overwatch.is_rank_zero():\n            return\n\n        # Special Positional Arguments\n        if lr is not None:\n            self.state[\"lr\"].append(lr)\n\n        if update_step_time:\n            self.state[\"step_time\"].append(time.time() - self.step_start_time)\n            self.step_start_time = time.time()\n\n        # Generic Keyword Arguments\n        for key, value in kwargs.items():\n            if key == \"loss\":\n                loss_val = value.detach()\n                self.state[\"loss_raw\"].append(loss_val)\n                self.state[\"loss\"].append(loss_val)\n            else:\n                self.state[key].append(value.detach())\n\n    @overwatch.rank_zero_only\n    def push(self) -> str:\n        # Note :: Raw Loss is an Average over Gradient Accumulation Steps --> No Smoothing!\n        loss_raw = torch.stack(list(self.state[\"loss_raw\"])).mean().item()\n        loss = torch.stack(list(self.state[\"loss\"])).mean().item()\n        step_time, lr = np.mean(list(self.state[\"step_time\"])), self.state[\"lr\"][-1]\n        status = self.get_status(loss)\n\n        # Fire to Trackers\n        prefix = self.stage.capitalize()\n        self.log(\n            self.global_step,\n            metrics={\n                f\"{prefix}/Step\": self.global_step,\n                f\"{prefix}/Loss\": loss,\n                f\"{prefix}/Loss (Raw)\": loss_raw,\n                f\"{prefix}/Learning Rate\": lr,\n                f\"{prefix}/Step Time\": step_time,\n            },\n        )\n        return status\n\n    def finalize(self) -> str:\n        for tracker in self.trackers:\n            tracker.finalize()\n\n\nclass VLAMetrics:\n    def __init__(\n        self,\n        active_trackers: Tuple[str, ...],\n        run_id: str,\n        run_dir: Path,\n        hparams: Dict[str, Any],\n        wandb_project: str = \"openvla\",\n        wandb_entity: Optional[str] = \"stanford-voltron\",\n        grad_accumulation_steps: int = 1,\n        window_size: int = 1,\n        resume_step: Optional[int] = None,\n        resume_epoch: Optional[int] = None,\n    ) -> None:\n        self.run_id, self.run_dir, self.hparams = run_id, run_dir, hparams\n\n        # Initialize Trackers\n        self.trackers = []\n        for tracker_type in active_trackers:\n            if tracker_type == \"jsonl\":\n                tracker = JSONLinesTracker(run_id, run_dir, hparams)\n            elif tracker_type == \"wandb\":\n                tracker = WeightsBiasesTracker(\n                    run_id, run_dir, hparams, project=wandb_project,  group=\"vla-train\"\n                )\n            else:\n                raise ValueError(f\"Tracker with type `{tracker_type} is not supported!\")\n\n            # Add Hyperparameters --> add to `self.trackers`\n            tracker.write_hyperparameters()\n            self.trackers.append(tracker)\n\n        # Create Universal Metrics Buffers\n        self.global_step = 0 if resume_step is None else resume_step\n        self.epoch = 0 if resume_epoch is None else resume_epoch\n        self.start_time, self.step_start_time = time.time(), time.time()\n        self.state = {\n            \"loss_raw\": deque(maxlen=grad_accumulation_steps),\n            \"loss\": deque(maxlen=window_size),\n            \"l1_loss\": deque(maxlen=window_size),\n            \"action_accuracy\": deque(maxlen=window_size),\n            \"step_time\": deque(maxlen=window_size),\n            \"lr\": [],\n        }\n\n        # Created metrics buffers for individual tracked datasets\n        self.dataset_trackers = defaultdict(lambda: VLAMetrics([], \"\", \"\", {}))\n\n    def log(self, global_step: int, metrics: Dict[str, Union[int, float]]) -> None:\n        for tracker in self.trackers:\n            tracker.write(global_step, metrics)\n\n    def get_status(self, loss: Optional[torch.Tensor] = None) -> str:\n        lr = self.state[\"lr\"][-1] if len(self.state[\"lr\"]) > 0 else 0\n        if loss is None:\n            return f\"=>> [Epoch {self.epoch:03d}] Global Step {self.global_step:06d} =>> LR :: {lr:.6f}\"\n\n        # Otherwise, embed `loss` in status report!\n        return f\"=>> [Epoch {self.epoch:03d}] Global Step {self.global_step:06d} =>> LR :: {lr:.6f} - Loss :: {loss:.4f}\"\n\n    def commit(\n        self,\n        *,\n        global_step: Optional[int] = None,\n        epoch: Optional[int] = None,\n        lr: Optional[float] = None,\n        update_step_time: bool = False,\n        **kwargs,\n    ) -> None:\n        \"\"\"Update all metrics in `self.state` by iterating through special positional arguments & kwargs.\"\"\"\n        if global_step is not None:\n            self.global_step = global_step\n\n        if epoch is not None:\n            self.epoch = epoch\n\n        # For all other variables --> only track on rank zero!\n        if not overwatch.is_rank_zero():\n            return\n\n        # Special Positional Arguments\n        if lr is not None:\n            self.state[\"lr\"].append(lr)\n\n        if update_step_time:\n            self.state[\"step_time\"].append(time.time() - self.step_start_time)\n            self.step_start_time = time.time()\n\n        # Generic Keyword Arguments\n        for key, value in kwargs.items():\n            if key == \"loss\":\n                loss_val = value.detach()\n                self.state[\"loss_raw\"].append(loss_val)\n                self.state[\"loss\"].append(loss_val)\n            else:\n                self.state[key].append(value.detach())\n\n    def commit_for_dataset(self, dataset_name: str, **kwargs) -> None:\n        self.dataset_trackers[dataset_name].commit(**kwargs)\n\n    @overwatch.rank_zero_only\n    def push(self) -> str:\n        # Note :: Raw Loss is an Average over Gradient Accumulation Steps --> No Smoothing!\n        loss_raw = torch.stack(list(self.state[\"loss_raw\"])).mean().item()\n        loss = torch.stack(list(self.state[\"loss\"])).mean().item()\n        l1_loss = torch.stack(list(self.state[\"l1_loss\"])).mean().item()\n        action_accuracy = torch.stack(list(self.state[\"action_accuracy\"])).mean().item()\n        step_time, lr = np.mean(list(self.state[\"step_time\"])), self.state[\"lr\"][-1]\n        status = self.get_status(loss)\n\n        # Get metrics per dataset\n        dataset_metrics = {}\n        for ds, tracker in self.dataset_trackers.items():\n            dataset_metrics.update(\n                {\n                    f\"{ds}/L1 Loss\": torch.stack(list(tracker.state[\"l1_loss\"])).mean().item(),\n                    f\"{ds}/Action Token Accuracy\": torch.stack(list(tracker.state[\"action_accuracy\"])).mean().item(),\n                }\n            )\n\n        # Fire to Trackers\n        prefix = \"VLA Train\"\n        self.log(\n            self.global_step,\n            metrics={\n                f\"{prefix}/Step\": self.global_step,\n                f\"{prefix}/Epoch\": self.epoch,\n                f\"{prefix}/Loss\": loss,\n                f\"{prefix}/L1 Loss\": l1_loss,\n                f\"{prefix}/Action Token Accuracy\": action_accuracy,\n                f\"{prefix}/Loss (Raw)\": loss_raw,\n                f\"{prefix}/Learning Rate\": lr,\n                f\"{prefix}/Step Time\": step_time,\n                **dataset_metrics,\n            },\n        )\n        return status\n\n    def finalize(self) -> str:\n        for tracker in self.trackers:\n            tracker.finalize()\n"
  },
  {
    "path": "train/model/overwatch/__init__.py",
    "content": "from .overwatch import initialize_overwatch\n"
  },
  {
    "path": "train/model/overwatch/overwatch.py",
    "content": "\"\"\"\noverwatch.py\n\nUtility class for creating a centralized/standardized logger (built on Rich) and accelerate handler.\n\"\"\"\n\nimport logging\nimport logging.config\nimport os\nfrom contextlib import nullcontext\nfrom logging import LoggerAdapter\nfrom typing import Any, Callable, ClassVar, Dict, MutableMapping, Tuple, Union\n\n# Overwatch Default Format String\nRICH_FORMATTER, DATEFMT = \"| >> %(message)s\", \"%m/%d [%H:%M:%S]\"\n\n# Set Logging Configuration\nLOG_CONFIG = {\n    \"version\": 1,\n    \"disable_existing_loggers\": True,\n    \"formatters\": {\"simple-console\": {\"format\": RICH_FORMATTER, \"datefmt\": DATEFMT}},\n    \"handlers\": {\n        \"console\": {\n            \"class\": \"rich.logging.RichHandler\",\n            \"formatter\": \"simple-console\",\n            \"markup\": True,\n            \"rich_tracebacks\": True,\n            \"show_level\": True,\n            \"show_path\": True,\n            \"show_time\": True,\n        }\n    },\n    \"root\": {\"level\": \"INFO\", \"handlers\": [\"console\"]},\n}\nlogging.config.dictConfig(LOG_CONFIG)\n\n\n# === Custom Contextual Logging Logic ===\nclass ContextAdapter(LoggerAdapter):\n    CTX_PREFIXES: ClassVar[Dict[int, str]] = {**{0: \"[*] \"}, **{idx: \"|=> \".rjust(4 + (idx * 4)) for idx in [1, 2, 3]}}\n\n    def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> Tuple[str, MutableMapping[str, Any]]:\n        ctx_level = kwargs.pop(\"ctx_level\", 0)\n        return f\"{self.CTX_PREFIXES[ctx_level]}{msg}\", kwargs\n\n\nclass DistributedOverwatch:\n    def __init__(self, name: str) -> None:\n        \"\"\"Initializer for an Overwatch object that wraps logging & `accelerate.PartialState`.\"\"\"\n        from accelerate import PartialState\n\n        # Note that PartialState is always safe to initialize regardless of `accelerate launch` or `torchrun`\n        #   =>> However, might be worth actually figuring out if we need the `accelerate` dependency at all!\n        self.logger, self.distributed_state = ContextAdapter(logging.getLogger(name), extra={}), PartialState()\n\n        # Logger Delegation (for convenience; would be nice to just compose & dynamic dispatch eventually)\n        self.debug = self.logger.debug\n        self.info = self.logger.info\n        self.warning = self.logger.warning\n        self.error = self.logger.error\n        self.critical = self.logger.critical\n\n        # Logging Defaults =>> only Log `INFO` on Main Process, `ERROR` on others!\n        self.logger.setLevel(logging.INFO if self.distributed_state.is_main_process else logging.ERROR)\n\n    @property\n    def rank_zero_only(self) -> Callable[..., Any]:\n        return self.distributed_state.on_main_process\n\n    @property\n    def local_zero_only(self) -> Callable[..., Any]:\n        return self.distributed_state.on_local_main_process\n\n    @property\n    def rank_zero_first(self) -> Callable[..., Any]:\n        return self.distributed_state.main_process_first\n\n    @property\n    def local_zero_first(self) -> Callable[..., Any]:\n        return self.distributed_state.local_main_process_first\n\n    def is_rank_zero(self) -> bool:\n        return self.distributed_state.is_main_process\n\n    def rank(self) -> int:\n        return self.distributed_state.process_index\n\n    def local_rank(self) -> int:\n        return self.distributed_state.local_process_index\n\n    def world_size(self) -> int:\n        return self.distributed_state.num_processes\n\n\nclass PureOverwatch:\n    def __init__(self, name: str) -> None:\n        \"\"\"Initializer for an Overwatch object that just wraps logging.\"\"\"\n        self.logger = ContextAdapter(logging.getLogger(name), extra={})\n\n        # Logger Delegation (for convenience; would be nice to just compose & dynamic dispatch eventually)\n        self.debug = self.logger.debug\n        self.info = self.logger.info\n        self.warning = self.logger.warning\n        self.error = self.logger.error\n        self.critical = self.logger.critical\n\n        # Logging Defaults =>> INFO\n        self.logger.setLevel(logging.INFO)\n\n    @staticmethod\n    def get_identity_ctx() -> Callable[..., Any]:\n        def identity(fn: Callable[..., Any]) -> Callable[..., Any]:\n            return fn\n\n        return identity\n\n    @property\n    def rank_zero_only(self) -> Callable[..., Any]:\n        return self.get_identity_ctx()\n\n    @property\n    def local_zero_only(self) -> Callable[..., Any]:\n        return self.get_identity_ctx()\n\n    @property\n    def rank_zero_first(self) -> Callable[..., Any]:\n        return nullcontext\n\n    @property\n    def local_zero_first(self) -> Callable[..., Any]:\n        return nullcontext\n\n    @staticmethod\n    def is_rank_zero() -> bool:\n        return True\n\n    @staticmethod\n    def rank() -> int:\n        return 0\n\n    @staticmethod\n    def world_size() -> int:\n        return 1\n\n\ndef initialize_overwatch(name: str) -> Union[DistributedOverwatch, PureOverwatch]:\n    return DistributedOverwatch(name) if int(os.environ.get(\"WORLD_SIZE\", -1)) != -1 else PureOverwatch(name)\n"
  },
  {
    "path": "train/model/prismatic.py",
    "content": "\"\"\"\nprismatic.py\n\nPyTorch Module defining a PrismaticVLM, our general interface for defining the various different VLMs in our work.\n\nNotes:\n    - For now, we don't subclass `transformers.PretrainedModel` (or CausalLM). Instead, we assume a very limited subset\n      of the {Model}ForCausalLM API that enables dispatch to the underlying LLM's `generate` utilities (feeding inputs\n      through our custom projection shim).\n\"\"\"\n\nfrom __future__ import annotations\nfrom abc import ABC, abstractmethod\nfrom functools import partial\nfrom pathlib import Path\nfrom typing import Callable, Dict, List, Optional, Type, Union\nimport torch\nimport os\nimport numpy as np\nfrom PIL import Image\nimport torch.nn as nn\nfrom torch.distributed.fsdp.wrap import _module_wrap_policy, _or_policy\n\nfrom transformers import GenerationMixin, PretrainedConfig\nfrom transformers.modeling_outputs import CausalLMOutputWithPast\n\nfrom model.llm_backbone import LLaMa2LLMBackbone\nfrom model.vision_backbone import DinoSigLIPViTBackbone, DinoSigLIPImageTransform\nfrom model.overwatch import initialize_overwatch\n\n\nfrom model.base_prompter import PromptBuilder\n\n# Initialize Overwatch =>> Wraps `logging.Logger`\noverwatch = initialize_overwatch(__name__)\n\n\n# HuggingFace Default / LLaMa-2 IGNORE_INDEX (for labels)\nIGNORE_INDEX = -100\n\n\n\nclass FusedMLPProjector(nn.Module):\n    def __init__(self, fused_vision_dim: int, llm_dim: int, mlp_type: str = \"fused-gelu-mlp\") -> None:\n        super().__init__()\n        self.initial_projection_dim = fused_vision_dim * 4\n        if mlp_type == \"fused-gelu-mlp\":\n            self.projector = nn.Sequential(\n                nn.Linear(fused_vision_dim, self.initial_projection_dim, bias=True),\n                nn.GELU(),\n                nn.Linear(self.initial_projection_dim, llm_dim, bias=True),\n                nn.GELU(),\n                nn.Linear(llm_dim, llm_dim, bias=True),\n            )\n        else:\n            raise ValueError(f\"Fused Projector with `{mlp_type = }` is not supported!\")\n\n    def forward(self, fused_img_patches: torch.Tensor) -> torch.Tensor:\n        return self.projector(fused_img_patches)\n\nclass VLM(nn.Module, GenerationMixin, ABC):\n    def __init__(\n        self,\n        model_family: str,\n        model_id: str,\n        vision_backbone: DinoSigLIPViTBackbone,\n        llm_backbone: LLaMa2LLMBackbone,\n        enable_mixed_precision_training: bool = True,\n    ) -> None:\n        super().__init__()\n        self.model_family, self.model_id = model_family, model_id\n        self.vision_backbone, self.llm_backbone = vision_backbone, llm_backbone\n        self.enable_mixed_precision_training = enable_mixed_precision_training\n\n        # Instance Attributes for a generic VLM\n        self.all_module_keys, self.trainable_module_keys = None, None\n\n        # === GenerationMixin Expected Attributes =>> *DO NOT MODIFY* ===\n        self.generation_config = self.llm_backbone.llm.generation_config\n        self.main_input_name = \"input_ids\"\n\n    @property\n    def device(self) -> torch.device:\n        \"\"\"Borrowed from `transformers.modeling_utils.py` -- checks parameter device; assumes model on *ONE* device!\"\"\"\n        return next(self.parameters()).device\n\n    @classmethod\n    @abstractmethod\n    def from_pretrained(\n        cls,\n        pretrained_checkpoint: Path,\n        model_family: str,\n        model_id: str,\n        vision_backbone: DinoSigLIPViTBackbone,\n        llm_backbone: LLaMa2LLMBackbone,\n        **kwargs: str,\n    ) -> VLM: ...\n\n    @abstractmethod\n    def get_prompt_builder(self, system_prompt: Optional[str] = None) -> PromptBuilder: ...\n\n    @abstractmethod\n    def freeze_backbones(self, stage: str) -> None: ...\n\n    @abstractmethod\n    def load_from_checkpoint(self, stage: str, run_dir: Path, pretrained_checkpoint: Optional[Path] = None) -> None: ...\n\n    @abstractmethod\n    def get_fsdp_wrapping_policy(self) -> Callable: ...\n\n    @abstractmethod\n    def forward(\n        self,\n        input_ids: Optional[torch.LongTensor] = None,\n        attention_mask: Optional[torch.Tensor] = None,\n        pixel_values: Optional[torch.FloatTensor] = None,\n        labels: Optional[torch.LongTensor] = None,\n        inputs_embeds: Optional[torch.FloatTensor] = None,\n        past_key_values: Optional[List[torch.FloatTensor]] = None,\n        use_cache: Optional[bool] = None,\n        output_attentions: Optional[bool] = None,\n        output_hidden_states: Optional[bool] = None,\n        return_dict: Optional[bool] = None,\n        multimodal_indices: Optional[torch.LongTensor] = None,\n    ) -> CausalLMOutputWithPast: ...\n\n    # === GenerationMixin Expected Properties & Methods (DO NOT MODIFY) ===\n    @staticmethod\n    def can_generate() -> bool:\n        return True\n\n    @property\n    def config(self) -> PretrainedConfig:\n        return self.llm_backbone.llm.config\n\n    # => Beam Search Utility\n    def _reorder_cache(self, past_key_values, beam_idx):\n        return self.llm_backbone.llm._reorder_cache(past_key_values, beam_idx)\n\n\nclass PrismaticVLM(VLM):\n    def __init__(\n        self,\n        model_id: str,\n        vision_backbone: DinoSigLIPViTBackbone,\n        llm_backbone: LLaMa2LLMBackbone,\n        enable_mixed_precision_training: bool = True,\n        arch_specifier: str = \"gelu-mlp\",\n        **kwargs,\n    ) -> None:\n        super().__init__(\n            \"prismatic\",\n            model_id,\n            vision_backbone,\n            llm_backbone,\n            enable_mixed_precision_training=enable_mixed_precision_training,\n        )\n\n        # Set Weight Initialization Seed for Projector Consistency\n        torch.manual_seed(vision_backbone.embed_dim)\n\n        # Initialize Projection (Adapter) based on `arch_specifier`\n        self.arch_specifier = arch_specifier\n        self.projector = FusedMLPProjector(vision_backbone.embed_dim, llm_backbone.embed_dim)\n\n        # Trackers\n        self.vision_backbone_requires_grad = False\n\n        # Set Module Keys =>> used in Checkpoint Saving / Model Loading\n        self.all_module_keys = [\"vision_backbone\", \"llm_backbone\", \"projector\"]\n        self.trainable_module_keys = []\n\n        # === Generation Utilities ===\n        #   => For computing likelihoods --> get tokens corresponding to \"True\", \"False\" and \"Yes\", \"No\"\n        self.string2idx = {}\n        for trigger_string in [\"True\", \"False\", \"Yes\", \"No\"] + [chr(ord(\"A\") + i) for i in range(26)]:\n            token_idx_list = self.llm_backbone.tokenizer.encode(trigger_string, add_special_tokens=False)\n            assert len(token_idx_list) == 1, f'String \"{trigger_string}\" is tokenized as more than one token!'\n            self.string2idx[trigger_string] = token_idx_list[0]\n\n    @classmethod\n    def from_pretrained(\n        cls,\n        pretrained_checkpoint: Path,\n        model_id: str,\n        vision_backbone: DinoSigLIPViTBackbone,\n        llm_backbone: LLaMa2LLMBackbone,\n        enable_mixed_precision_training: bool = True,\n        arch_specifier: str = \"gelu-mlp\",\n        freeze_weights: bool = True,\n        **kwargs,\n    ) -> PrismaticVLM:\n        \"\"\"Initialize a PrismaticVLM from a pretrained checkpoint, freezing all weights, tailored for inference.\"\"\"\n        vlm = cls(\n            model_id,\n            vision_backbone,\n            llm_backbone,\n            enable_mixed_precision_training=enable_mixed_precision_training,\n            arch_specifier=arch_specifier,\n            **kwargs,\n        )\n\n        # Load from Checkpoint (Custom --> should load both *projector* and *llm* weights)\n        model_state_dict = torch.load(pretrained_checkpoint, map_location=\"cpu\")[\"model\"]\n        assert (\n            \"projector\" in model_state_dict and \"llm_backbone\" in model_state_dict\n        ), \"PrismaticVLM `from_pretrained` expects checkpoint with keys for `projector` AND `llm_backbone`!\"\n\n\n        # vlm.projector.load_state_dict(model_state_dict[\"projector\"])\n        #vlm.llm_backbone.load_state_dict(model_state_dict[\"llm_backbone\"])\n        \n        if \"vision_backbone\" in model_state_dict.keys():\n            vlm.vision_backbone.load_state_dict(model_state_dict[\"vision_backbone\"])\n\n        # Freeze Weights\n        if freeze_weights:\n            vlm.requires_grad_(False)\n            vlm.eval()\n\n        return vlm\n\n    def get_prompt_builder(self, system_prompt: Optional[str] = None) -> PromptBuilder:\n        prompt_initializer: Type[PromptBuilder] = self.llm_backbone.prompt_builder_fn\n        return prompt_initializer(self.model_family, system_prompt=system_prompt)\n\n    def freeze_backbones(self, stage: str) -> None:\n        \"\"\"\n        This function sets `requires_grad_` on each of the component modules explicitly, depending on stage.\n\n        We support two separate stages --> \"align\" and \"finetune\".\n            => \"align\" --> vision_backbone*, llm_backbone* are frozen; only the `projector` is trained.\n            => \"finetune\" --> vision_backbone* is frozen; both `projector` and `llm_backbone` are trained.\n\n        :param stage: Pretraining stage in < \"align\" | \"finetune\" | \"full-finetune\" | \"vla-train\" | \"vla-full-train\" >\n        \"\"\"\n        if stage == \"align\":\n            self.vision_backbone.requires_grad_(False)\n            self.llm_backbone.requires_grad_(False)\n            self.projector.requires_grad_(True)\n\n            # Add to `self.trainable_module_keys`\n            self.trainable_module_keys = [\"projector\"]\n\n            # Update Trackers\n            self.vision_backbone_requires_grad = False\n\n        elif stage in {\"finetune\", \"vla-train\"}:\n            self.vision_backbone.requires_grad_(False)\n            self.llm_backbone.requires_grad_(True)\n            self.projector.requires_grad_(True)\n\n            # Add to `self.trainable_module_keys`\n            self.trainable_module_keys = [\"projector\", \"llm_backbone\"]\n\n            # Update Trackers\n            self.vision_backbone_requires_grad = False\n\n        elif stage in {\"full-finetune\", \"vla-full-train\"}:\n            self.vision_backbone.dtype = torch.float32\n            self.vision_backbone.requires_grad_(True)\n            self.llm_backbone.requires_grad_(True)\n            self.projector.requires_grad_(True)\n\n            # Add to `self.trainable_module_keys`\n            self.trainable_module_keys = [\"vision_backbone\", \"projector\", \"llm_backbone\"]\n\n            # Update Trackers\n            self.vision_backbone_requires_grad = True\n\n        elif stage in {\"last-layer-finetune\", \"vla-last-layer-train\"}:\n            self.vision_backbone.requires_grad_(False)\n            self.projector.requires_grad_(False)\n            self.llm_backbone.requires_grad_(False)\n\n            # Unfreeze final LLM layer\n            for module in self.llm_backbone.last_layer_finetune_modules:\n                module.requires_grad_(True)\n\n            # Add to `self.trainable_module_keys`\n            self.trainable_module_keys = [\"llm_backbone\"]\n\n            # Update Trackers\n            self.vision_backbone_requires_grad = False\n\n        elif stage in {\"vla-sandwich-train\"}:\n            self.vision_backbone.dtype = torch.float32\n            self.vision_backbone.requires_grad_(True)\n            self.projector.requires_grad_(True)\n            self.llm_backbone.requires_grad_(False)\n\n            # Unfreeze final LLM layer\n            for module in self.llm_backbone.last_layer_finetune_modules:\n                module.requires_grad_(True)\n\n            # Add to `self.trainable_module_keys`\n            self.trainable_module_keys = [\"vision_backbone\", \"projector\", \"llm_backbone\"]\n\n            # Update Trackers\n            self.vision_backbone_requires_grad = True\n\n        else:\n            raise ValueError(f\"Stage `{stage}` is not supported for LLaVa! Try < align | finetune >\")\n\n        overwatch.debug(\"##################################################\")\n        overwatch.debug(\"#####      Trainable Network Parameters:     #####\")\n        overwatch.debug(\"##################################################\")\n        for name, param in self.named_parameters():\n            if param.requires_grad:\n                overwatch.debug(name)\n\n    def load_from_checkpoint(self, stage: str, run_dir: Path, pretrained_checkpoint: Optional[Path] = None) -> None:\n        \"\"\"Load weights from checkpoint (if required by the given stage).\"\"\"\n        assert stage in {\"align\", \"finetune\", \"full-finetune\"}, f\"Stage {stage} is not supported!\"\n\n        # If we're running a `no-align` architecture, we're good!\n        if self.arch_specifier.startswith(\"no-align\"):\n            overwatch.info(\n                f\"PrismaticVLM with `{self.arch_specifier = }` does not require pretrained weights!\", ctx_level=1\n            )\n            return\n\n        # Otherwise, handle stage-specific logic!\n        if stage == \"align\":\n            overwatch.info(\"Stage `align` does not require pretrained weights =>> Starting Training\", ctx_level=1)\n            return\n\n        # Otherwise, load from `pretrained_checkpoint` or match on `run_dir` (s/+stage-finetune/+stage-align/g)\n        overwatch.info(\"Stage `finetune` requires `align` pretrained weights\", ctx_level=1)\n\n        # Config specifies path to a checkpoint to load\n        if pretrained_checkpoint is not None:\n            overwatch.info(f\"Loading from Provided Checkpoint `{pretrained_checkpoint}`\", ctx_level=1)\n            model_state_dict = torch.load(pretrained_checkpoint)[\"model\"]\n            self.projector.load_state_dict(model_state_dict[\"projector\"])\n            return\n\n        # [Contract] If no `pretrained_checkpoint`, assume `align` lives in the run directory; string substitution!\n        model, scale, _, seed = run_dir.name.split(\"+\")\n        align_dirs = [\n            d\n            for d in run_dir.parent.iterdir()\n            if (d.name.startswith(f\"{model}+{scale}\") and d.name.endswith(f\"+stage-align+{seed}\"))\n        ]\n        assert len(align_dirs) == 1, \"Multiple or No Valid Pretrained Directories Exist -- Double Check `runs`!\"\n        if (pretrained_checkpoint := (align_dirs[0] / \"checkpoints\" / \"latest-checkpoint.pt\")).exists():\n            overwatch.info(f\"Loading from Discovered Checkpoint `{pretrained_checkpoint}`\", ctx_level=1)\n            model_state_dict = torch.load(pretrained_checkpoint)[\"model\"]\n            self.projector.load_state_dict(model_state_dict[\"projector\"])\n        else:\n            raise ValueError(f\"Could not find valid `align` checkpoint at {pretrained_checkpoint}!\")\n\n    def get_fsdp_wrapping_policy(self) -> Callable:\n        \"\"\"Return an FSDP _or_policy over the policies returned by each individual backbone (and our VLM policy).\"\"\"\n        vision_fsdp_wrapping_policy = self.vision_backbone.get_fsdp_wrapping_policy()\n        llm_fsdp_wrapping_policy = self.llm_backbone.get_fsdp_wrapping_policy()\n\n        # Get Prismatic Wrapping Policy =>> just a module wrapping policy around `self.projector`\n        prismatic_fsdp_wrapping_policy = partial(\n            _module_wrap_policy,\n            module_classes={FusedMLPProjector},\n        )\n\n        # Return union (_or_) over constituent policies\n        #   => Note: there is *not* a fall-through policy; any module that isn't covered by the above constituents will\n        #            automatically be folded into the root VLM FSDP instance.\n        return partial(\n            _or_policy,\n            policies=[\n                vision_fsdp_wrapping_policy,\n                llm_fsdp_wrapping_policy,\n                prismatic_fsdp_wrapping_policy,\n            ],\n        )\n\n    # Note =>> We're not explicitly subclassing `PreTrainedModel` because we don't need the bloat; however, `forward()`\n    #          *must* match the signature of a `{Model}ForCausalLM` so that we can inherit from `GenerationMixin`\n\n    # ruff: noqa: C901\n    def forward(\n        self,\n        input_ids: Optional[torch.LongTensor] = None,\n        attention_mask: Optional[torch.Tensor] = None,\n        pixel_values: Optional[torch.FloatTensor] = None,\n        labels: Optional[torch.LongTensor] = None,\n        inputs_embeds: Optional[torch.FloatTensor] = None,\n        past_key_values: Optional[List[torch.FloatTensor]] = None,\n        use_cache: Optional[bool] = None,\n        output_attentions: Optional[bool] = None,\n        output_hidden_states: Optional[bool] = None,\n        return_dict: Optional[bool] = None,\n        multimodal_indices: Optional[torch.LongTensor] = None,\n    ) -> CausalLMOutputWithPast:\n        \"\"\"Run a forward pass through the VLM, returning a CausalLMOutputWithPast instance (contains loss).\"\"\"\n\n        # Handle Inference (leverage cache, short-circuit on just LLM forward)\n        if input_ids.shape[1] == 1 and past_key_values is not None:\n            # We're leveraging the cache, so just redirect to `self.llm_backbone` with `input_ids` and `past_key_values`\n            output = self.llm_backbone(\n                input_ids=input_ids,\n                attention_mask=None,\n                position_ids=None,\n                past_key_values=past_key_values,\n                inputs_embeds=None,\n                labels=None,\n                use_cache=use_cache,\n                output_attentions=output_attentions,\n                output_hidden_states=output_hidden_states,\n                return_dict=return_dict,\n            )\n            return output\n\n        elif input_ids.shape[1] == 1 or pixel_values is None:\n            raise RuntimeError(\"Invalid `forward()` call!\")\n\n        # Handle Multimodal Indices is None --> pretend like the batch is fully multimodal (always image + text)!\n        if multimodal_indices is None:\n            multimodal_indices = torch.arange(len(input_ids), dtype=torch.long, device=input_ids.device)\n\n        # Handle Multimodal Indices is Empty (len == 0) --> simple unimodal forward\n        elif len(multimodal_indices) == 0:\n            return self.llm_backbone(\n                input_ids=input_ids,\n                attention_mask=attention_mask,\n                position_ids=None,\n                past_key_values=past_key_values,\n                inputs_embeds=None,\n                labels=labels,\n                use_cache=use_cache,\n                output_attentions=output_attentions,\n                output_hidden_states=output_hidden_states,\n                return_dict=return_dict,\n            )\n\n                \n        \"\"\" TODO \n            1. add a image token in the front of img_tokens \n            2. parse the [image_2], [image_3] into the dict, and add to the pixel_values\n            3. traverse the vit of pixel_values\n            4. max pooling the patch_features from 16*16 -> 4*4\n        \"\"\"\n        \n        # Run Visual Feature Extraction\n        with torch.set_grad_enabled(self.vision_backbone_requires_grad):\n            if isinstance(pixel_values, dict):\n                patch_features = self.vision_backbone({k: pixel_values[k][multimodal_indices] for k in pixel_values})\n            else:\n                patch_features = self.vision_backbone(pixel_values[multimodal_indices])\n\n        # Projection Logic :: [bsz, num_patches, llm_embed_dim] =>> num_patches = (2 *) (256 + 1) for ViT-L + CLS    \n        \n        projected_patch_embedding = []\n        for idx, patch_feature in enumerate(patch_features):\n            projected_patch_embedding.append(self.projector(patch_feature))\n        \n        projected_patch_embeddings = torch.cat(projected_patch_embedding[::-1], dim=1)\n        projected_patch_attention_mask = None\n        \n        if attention_mask is not None:\n            projected_patch_attention_mask = torch.full(\n                (projected_patch_embeddings.shape[0], projected_patch_embeddings.shape[1]),\n                True,\n                dtype=attention_mask.dtype,\n                device=attention_mask.device,\n            )\n\n        input_embeddings = self.llm_backbone.embed_input_ids(input_ids)\n        # torch.Size([16, 56, 4096])\n        \n        # Build Multimodal Embeddings (and build resulting attention mask)\n        multimodal_embeddings = torch.cat(\n            [\n                input_embeddings[multimodal_indices, :1, :],\n                projected_patch_embeddings,\n                input_embeddings[multimodal_indices, 1:, :],\n            ],\n            dim=1,\n        )\n\n        multimodal_attention_mask = None\n        if attention_mask is not None:\n            multimodal_attention_mask = torch.cat(\n                [\n                    attention_mask[multimodal_indices, :1],\n                    projected_patch_attention_mask,\n                    attention_mask[multimodal_indices, 1:],\n                ],\n                dim=1,\n            )\n\n\n        # [Contract] We assume the first token of `labels` (associated with <BOS>) is already marked as \"IGNORE\"\n        #   => We'll ignore the per-token outputs for each of the patch embeddings as well!\n        multimodal_labels = None\n        if labels is not None:\n            projected_patch_labels = torch.full(\n                (projected_patch_embeddings.shape[0], projected_patch_embeddings.shape[1]),\n                IGNORE_INDEX,\n                dtype=labels.dtype,\n                device=labels.device,\n            )\n            multimodal_labels = torch.cat(\n                [labels[multimodal_indices, :1], projected_patch_labels, labels[multimodal_indices, 1:]], dim=1\n            )\n\n        # === Add Unimodal Handling ===\n\n        # Create Fused Embeddings, Attention Mask, and Labels by Merging with \"unimodal\" Inputs (if applicable)\n        unimodal_indices = torch.tensor(\n            [idx for idx in range(len(input_ids)) if idx not in multimodal_indices],\n            dtype=torch.long,\n            device=multimodal_indices.device,\n        )\n\n        # No \"unimodal\" data --> Fused == Multimodal\n        if len(unimodal_indices) == 0:\n            fused_embeddings = multimodal_embeddings\n            fused_attention_mask = multimodal_attention_mask\n            fused_labels = multimodal_labels\n\n        else:\n            # Otherwise --> Merge w/ unimodal data\n\n            # This doesn't matter --> but in the \"normal\" case this is the embedding of the <PAD> token\n            #   => NOTE :: Verified that `zeros/randn/empty/<PAD> embedding` all return the same result!\n            unimodal_embeddings_pad = torch.zeros(\n                (len(unimodal_indices), projected_patch_embeddings.shape[1], input_embeddings.shape[2]),\n                dtype=input_embeddings.dtype,\n                device=input_embeddings.device,\n            )\n            unimodal_attention_pad = torch.full(\n                (len(unimodal_indices), projected_patch_embeddings.shape[1]),\n                False,\n                dtype=attention_mask.dtype,\n                device=attention_mask.device,\n            )\n            unimodal_labels_pad = torch.full(\n                (len(unimodal_indices), projected_patch_embeddings.shape[1]),\n                IGNORE_INDEX,\n                dtype=labels.dtype,\n                device=labels.device,\n            )\n\n            unimodal_embeddings = torch.cat([input_embeddings[unimodal_indices], unimodal_embeddings_pad], dim=1)\n            unimodal_attention_mask = torch.cat([attention_mask[unimodal_indices], unimodal_attention_pad], dim=1)\n            unimodal_labels = torch.cat([labels[unimodal_indices], unimodal_labels_pad], dim=1)\n\n            # Create \"Fused\" Tensors by Stacking Multimodal & Unimodal\n            fused_embeddings = torch.vstack([multimodal_embeddings, unimodal_embeddings])\n            fused_attention_mask = torch.vstack([multimodal_attention_mask, unimodal_attention_mask])\n            fused_labels = torch.vstack([multimodal_labels, unimodal_labels])\n        # Run LLM Forward --> returns CausalLMOutputWithPast!\n        return self.llm_backbone(\n            input_ids=None,\n            attention_mask=fused_attention_mask,\n            position_ids=None,\n            past_key_values=past_key_values,\n            inputs_embeds=fused_embeddings,\n            labels=fused_labels,\n            use_cache=use_cache,\n            output_attentions=output_attentions,\n            output_hidden_states=output_hidden_states,\n            return_dict=return_dict,\n        )\n\n    # === GenerationMixin Methods ===\n    #   => Note: The following methods override the functionality of `transformers.GenerationMixin`; these expect the\n    #            contract in each of the function signatures, and also expect our `forward` function to roughly take\n    #            the same arguments as the underlying LLM (see `LlamaModelForCausalLM` as an example)\n\n    def prepare_inputs_for_generation(\n        self,\n        input_ids: Optional[torch.LongTensor] = None,\n        attention_mask: Optional[torch.Tensor] = None,\n        pixel_values: Optional[torch.FloatTensor] = None,\n        inputs_embeds: Optional[torch.FloatTensor] = None,\n        past_key_values: Optional[List[torch.FloatTensor]] = None,\n        use_cache: Optional[bool] = None,\n        **kwargs: torch.Tensor,\n    ) -> Dict[str, torch.Tensor]:\n        \"\"\"Borrowed from `LlamaForCausalLM` --> in general, just handles caching logic during generation.\"\"\"\n        if past_key_values:\n            input_ids = input_ids[:, -1:]\n\n        # if `inputs_embeds` are passed, we only want to use them in the 1st generation step\n        if inputs_embeds is not None and past_key_values is None:\n            model_inputs = {\"inputs_embeds\": inputs_embeds}\n        else:\n            model_inputs = {\"input_ids\": input_ids}\n\n        # Make sure `pixel_values` are preserved in `model_inputs`\n        model_inputs.update(\n            {\n                \"attention_mask\": attention_mask,\n                \"pixel_values\": pixel_values,\n                \"past_key_values\": past_key_values,\n                \"use_cache\": use_cache,\n            }\n        )\n\n        return model_inputs\n\n    @torch.inference_mode()\n    def generate_batch(\n        self,\n        pixel_values: Union[torch.Tensor, Dict[str, torch.Tensor]],\n        texts: List[str],\n        return_string_probabilities: Optional[List[str]] = None,\n        **kwargs: str,\n    ) -> Union[List[str], List[List[float]]]:\n        # For now, only support generation with a batch size of 1 for simplicity\n        tokenizer = self.llm_backbone.tokenizer\n\n        # Prepare Inputs\n        batch_input_ids = [\n            tokenizer(text, truncation=True, return_tensors=\"pt\").input_ids.to(self.device) for text in texts\n        ]\n        if isinstance(pixel_values, torch.Tensor):\n            pixel_values = pixel_values[None, ...].to(self.device)\n        elif isinstance(pixel_values, dict):\n            pixel_values = {k: v[None, ...].to(self.device) for k, v in pixel_values.items()}\n        else:\n            raise ValueError(f\"Unsupported `pixel_values` type = {type(pixel_values)}\")\n\n        # Create Output Lists\n        gen_texts, gen_probabilities = [], []\n\n        # Invoke super().generate --> taps into `GenerationMixin` which (redirects) to `forward()`\n        autocast_dtype = self.llm_backbone.half_precision_dtype\n        with torch.autocast(\"cuda\", dtype=autocast_dtype, enabled=self.enable_mixed_precision_training):\n            for idx, input_ids in enumerate(batch_input_ids):\n                if isinstance(pixel_values, torch.Tensor):\n                    pixel_values = pixel_values[idx]\n                elif isinstance(pixel_values, dict):\n                    pixel_values = {k: pixel_values[k][idx] for k in pixel_values}\n                else:\n                    raise ValueError(f\"Unsupported `pixel_values` type = {type(pixel_values)}\")\n\n                # Handle `return_string_probabilities`\n                if return_string_probabilities is None:\n                    full_out_ids = super().generate(input_ids=input_ids, pixel_values=pixel_values, **kwargs)\n                    gen_ids = full_out_ids[0, input_ids.shape[1] :]\n\n                    # Decode `gen_ids` and strip any <EOS> tokens\n                    gen_texts.append(tokenizer.decode(gen_ids, skip_special_tokens=True).strip())\n\n                else:\n                    full_out_dict = super().generate(\n                        input_ids=input_ids,\n                        pixel_values=pixel_values,\n                        output_scores=True,\n                        return_dict_in_generate=True,\n                        **kwargs,\n                    )\n\n                    # Generation pattern should usually be [TOKEN] <EOS> for True/False and Yes/No Generations\n                    gen_ids = full_out_dict.sequences[0, input_ids.shape[1] :]\n\n                    # [Debug] Verify that the first token generated is in `self.string2idx.values()`\n                    # assert gen_ids[0] in self.string2idx.values(), \"Generated ID not in mapping!\"\n\n                    # Decode `gen_ids` and strip any <EOS> tokens\n                    gen_texts.append(tokenizer.decode(gen_ids, skip_special_tokens=True).strip())\n\n                    # Get all token probabilities --> softmax over logits\n                    token_probs = torch.softmax(full_out_dict.scores[0][0], dim=0)\n\n                    # Get *normalized* probabilities for all values in `return_token_probabilities`\n                    slice_idxs = torch.tensor([self.string2idx[s] for s in return_string_probabilities])\n                    string_probs_unnormalized = token_probs[slice_idxs]\n                    string_probs = string_probs_unnormalized / string_probs_unnormalized.sum()\n                    gen_probabilities.append(string_probs.cpu().numpy().tolist())\n\n        return gen_texts if return_string_probabilities is None else gen_probabilities\n\n    @torch.inference_mode()\n    def generate(self, image: Image, prompt_text: str, **kwargs: str) -> str:\n        # For now, only support generation with a batch size of 1 for simplicity\n        image_transform, tokenizer = self.vision_backbone.image_transform, self.llm_backbone.tokenizer\n\n        # Prepare Inputs\n        input_ids = tokenizer(prompt_text, truncation=True, return_tensors=\"pt\").input_ids.to(self.device)\n        pixel_values = image_transform(image)\n        if isinstance(pixel_values, torch.Tensor):\n            pixel_values = pixel_values[None, ...].to(self.device)\n        elif isinstance(pixel_values, dict):\n            pixel_values = {k: v[None, ...].to(self.device) for k, v in pixel_values.items()}\n        else:\n            raise ValueError(f\"Unsupported `pixel_values` type = {type(pixel_values)}\")\n\n        # Invoke super().generate --> taps into `GenerationMixin` which (redirects) to `forward()`\n        autocast_dtype = self.llm_backbone.half_precision_dtype\n        with torch.autocast(\"cuda\", dtype=autocast_dtype, enabled=self.enable_mixed_precision_training):\n            # fmt: off\n            generated_ids = super().generate(\n                input_ids=input_ids,            # Shape: [1, seq]\n                pixel_values=pixel_values,      # Shape: [1, 3, res, res] or Dict[str, Shape[1, 3, res, res]]\n                **kwargs\n            )\n            # fmt: on\n\n        generated_text = tokenizer.decode(generated_ids[0, input_ids.shape[1] :], skip_special_tokens=True).strip()\n\n        return generated_text\n"
  },
  {
    "path": "train/model/prompt_llama2.py",
    "content": "\"\"\"\nllama2_prompter.py\n\nDefines a PromptBuilder for building LLaMa-2 Chat Prompts --> not sure if this is \"optimal\", but this is the pattern\nthat's used by HF and other online tutorials.\n\nReference: https://huggingface.co/blog/llama2#how-to-prompt-llama-2\n\"\"\"\n\nfrom typing import Optional\n\nfrom model.base_prompter import PromptBuilder\n\n# Default System Prompt for Prismatic Models\nSYS_PROMPTS = {\n    \"prismatic\": (\n        \"You are a helpful language and vision assistant. \"\n        \"You are able to understand the visual content that the user provides, \"\n        \"and assist the user with a variety of tasks using natural language.\"\n    ),\n    \"openvla\": (\n        \"You are a helpful language and vision assistant. \"\n        \"You are able to understand the visual content that the user provides, \"\n        \"and assist the user with a variety of tasks using natural language.\"\n    ),\n}\n\n\ndef format_system_prompt(system_prompt: str) -> str:\n    return f\"<<SYS>\\n{system_prompt.strip()}\\n<</SYS>>\\n\\n\"\n\n\nclass LLaMa2ChatPromptBuilder(PromptBuilder):\n    def __init__(self, model_family: str, system_prompt: Optional[str] = None) -> None:\n        super().__init__(model_family, system_prompt)\n        self.system_prompt = format_system_prompt(\n            SYS_PROMPTS[self.model_family] if system_prompt is None else system_prompt\n        )\n\n        # LLaMa-2 Specific\n        self.bos, self.eos = \"<s>\", \"</s>\"\n\n        # Get role-specific \"wrap\" functions\n        self.wrap_human = lambda msg: f\"[INST] {msg} [/INST] \"\n        self.wrap_gpt = lambda msg: f\"{msg if msg != '' else ' '}{self.eos}\"\n\n        # === `self.prompt` gets built up over multiple turns ===\n        self.prompt, self.turn_count = \"\", 0\n\n    def add_turn(self, role: str, message: str) -> str:\n        assert (role == \"human\") if (self.turn_count % 2 == 0) else (role == \"gpt\")\n        message = message.replace(\"<image>\", \"\").strip()\n\n        # Special Handling for \"system\" prompt (turn_count == 0)\n        if self.turn_count == 0:\n            sys_message = self.wrap_human(self.system_prompt + message)\n            wrapped_message = sys_message\n        elif (self.turn_count % 2) == 0:\n            human_message = self.wrap_human(message)\n            wrapped_message = human_message\n        else:\n            gpt_message = self.wrap_gpt(message)\n            wrapped_message = gpt_message\n\n        # Update Prompt\n        self.prompt += wrapped_message\n\n        # Bump Turn Counter\n        self.turn_count += 1\n\n        # Return \"wrapped_message\" (effective string added to context)\n        return wrapped_message\n\n    def get_potential_prompt(self, message: str) -> None:\n        # Assumes that it's always the user's (human's) turn!\n        prompt_copy = str(self.prompt)\n\n        # Special Handling for \"system\" prompt (turn_count == 0)\n        if self.turn_count == 0:\n            sys_message = self.wrap_human(self.system_prompt + message)\n            prompt_copy += sys_message\n\n        else:\n            human_message = self.wrap_human(message)\n            prompt_copy += human_message\n\n        return prompt_copy.removeprefix(self.bos).rstrip()\n\n    def get_prompt(self) -> str:\n        # Remove prefix <bos> because it gets auto-inserted by tokenizer!\n        return self.prompt.removeprefix(self.bos).rstrip()"
  },
  {
    "path": "train/model/strategy.py",
    "content": "\"\"\"\nbase_strategy.py\n\nAbstract class definition of a (distributed) training strategy, with full annotations of class methods, utility\nfunctions, and initialization logic.\n\nTraining Strategies (DDP, FSDP-Grad, FSDP-Full) tend to have a lot of repeated components; this class does a lot of\nheavy lifting.\n\"\"\"\nimport math\nfrom collections import OrderedDict\nfrom functools import partial\nfrom abc import ABC\nfrom pathlib import Path\nfrom functools import partial\nfrom typing import Callable, Optional\nfrom torch.optim import AdamW\nimport torch\nimport torch.nn as nn\nimport torch.distributed as dist\nfrom torch.distributed.fsdp import (\n    FullStateDictConfig,\n    MixedPrecision,\n    ShardingStrategy,\n    StateDictType,\n)\nfrom torch.distributed.fsdp import FullyShardedDataParallel as FSDP\nfrom torch.distributed.algorithms._checkpoint.checkpoint_wrapper import (\n    CheckpointImpl,\n    apply_activation_checkpointing,\n    checkpoint_wrapper,\n)\nfrom torch.utils.data import DataLoader, Dataset, DistributedSampler, IterableDataset\nfrom tqdm import tqdm\nfrom transformers.modeling_outputs import CausalLMOutputWithPast\nfrom transformers.optimization import get_constant_schedule, get_cosine_schedule_with_warmup\n\nfrom model.prismatic import PrismaticVLM\nfrom model.overwatch import initialize_overwatch\nfrom model.metrics import Metrics, VLAMetrics\nfrom datasets.data_utils import SplitModalitySampler\nfrom datasets.data_utils import PaddedCollatorForActionPrediction, PaddedCollatorForLanguageModeling\nfrom model.action_tokenizer import ActionTokenizer\n\n# Initialize Overwatch =>> Wraps `logging.Logger`\noverwatch = initialize_overwatch(__name__)\n\n# === Abstract Base Class for an arbitrary Training Strategy ===\nclass TrainingStrategy(ABC):\n    def __init__(\n        self,\n        vlm: PrismaticVLM,\n        device_id: int,\n        stage: str,\n        epochs: int,\n        max_steps: Optional[int],\n        global_batch_size: int,\n        per_device_batch_size: int,\n        learning_rate: float,\n        weight_decay: float,\n        max_grad_norm: float,\n        lr_scheduler_type: str,\n        warmup_ratio: float,\n        enable_gradient_checkpointing: bool = True,\n        enable_mixed_precision_training: bool = True,\n        reduce_in_full_precision: bool = False,\n        mixed_precision_dtype: torch.dtype = torch.bfloat16,\n        worker_init_fn: Optional[Callable[[int], None]] = None,\n        sharding_strategy: str = \"shard-grad-op\",\n        state_dict_type: StateDictType = StateDictType.FULL_STATE_DICT,\n        **_: str,\n    ) -> None:\n        self.vlm, self.device_id, self.stage = vlm, device_id, stage\n\n        # Get relevant VLM instance parameters before they get (potentially) wrapped\n        self.all_module_keys, self.trainable_module_keys = self.vlm.all_module_keys, self.vlm.trainable_module_keys\n        self.llm_transformer_layer_cls = self.vlm.llm_backbone.transformer_layer_cls\n\n        # Optimization Parameters\n        self.epochs, self.max_steps = epochs, max_steps\n        self.global_batch_size, self.per_device_batch_size = global_batch_size, per_device_batch_size\n\n        self.learning_rate, self.weight_decay, self.max_grad_norm = learning_rate, weight_decay, max_grad_norm\n        self.lr_scheduler_type, self.warmup_ratio = lr_scheduler_type, warmup_ratio\n\n        # Generic Strategy Parameters\n        self.enable_gradient_checkpointing = enable_gradient_checkpointing\n        self.enable_mixed_precision_training = enable_mixed_precision_training\n        self.reduce_in_full_precision = reduce_in_full_precision\n        self.mixed_precision_dtype = mixed_precision_dtype\n\n        # DataLoader Parameters\n        self.worker_init_fn = worker_init_fn\n\n        # Optimizers & Scheduler (initialized in `run_setup`)\n        self.optimizer, self.lr_scheduler = None, None\n\n        # Lightweight Validation\n        assert (\n            self.global_batch_size % self.per_device_batch_size == 0\n        ), \"Per-device batch size must evenly divide global batch size!\"\n        self.grad_accumulation_steps = self.global_batch_size // self.per_device_batch_size // overwatch.world_size()\n\n        # FSDP-Specific Parameters\n        if sharding_strategy == \"shard-grad-op\":\n            self.fsdp_sharding_strategy = ShardingStrategy._HYBRID_SHARD_ZERO2\n        elif sharding_strategy == \"full-shard\":\n            self.fsdp_sharding_strategy = ShardingStrategy.HYBRID_SHARD\n        else:\n            raise ValueError(f\"FSDP Sharding Strategy {sharding_strategy} is not supported!\")\n\n        assert state_dict_type == StateDictType.FULL_STATE_DICT, \"Sharded state saving is not yet implemented!\"\n        self.fsdp_state_dict_type = state_dict_type\n        self.fsdp_save_policy = FullStateDictConfig(offload_to_cpu=True, rank0_only=True)\n\n    def run_training(\n        self,\n        dataset: Dataset,\n        collator: PaddedCollatorForLanguageModeling,\n        metrics: Metrics,\n        stage: str = \"finetune\",\n        batch_construction_strategy: str = \"split-modality\",\n        seed: int = 7,\n    ) -> None:\n        \"\"\"Run the training loop for the given `dataset` and `collator`; log losses, results to `metrics`\"\"\"\n        if \"finetune\" in stage and batch_construction_strategy == \"split-modality\":\n            # Instantiate the split-modality sampler; if you want to extend with other batch construction schemes,\n            #   (e.g., grouping by length) =>> can easily add them here!\n            modality_lengths = dataset.get_modality_lengths()\n            sampler = SplitModalitySampler(\n                dataset,\n                modality_lengths,\n                global_batch_size=self.global_batch_size,\n                num_replicas=overwatch.world_size(),\n                rank=overwatch.rank(),\n                seed=seed,\n                drop_last=False,\n            )\n\n        else:\n            sampler = DistributedSampler(\n                dataset,\n                num_replicas=overwatch.world_size(),\n                rank=overwatch.rank(),\n                shuffle=True,\n                seed=seed,\n                drop_last=False,\n            )\n\n        # Create a DataLoader with the initialized sampler, per-device-bsz, and collator\n        dataloader = DataLoader(\n            dataset,\n            batch_size=self.per_device_batch_size,\n            sampler=sampler,\n            collate_fn=collator,\n            worker_init_fn=None,\n        )\n\n        # Max Steps vs. Epochs Computation\n        steps_per_epoch = len(dataloader) // self.grad_accumulation_steps\n        if self.max_steps is not None and steps_per_epoch < self.max_steps:\n            # Just set `epochs` to some large number --> we'll short-circuit based on steps anyway\n            self.epochs = 100\n\n        # === Train ===\n        status = metrics.get_status()\n        with tqdm(\n            total=(\n                (self.epochs * (len(dataloader) // self.grad_accumulation_steps))\n                if self.max_steps is None\n                else self.max_steps\n            ),\n            desc=status,\n            leave=False,\n            disable=not overwatch.is_rank_zero(),\n        ) as progress:\n            for epoch in range(self.epochs):\n                self.vlm.train()\n                sampler.set_epoch(epoch)\n\n                # Zero-Gradients (just in case)\n                self.optimizer.zero_grad()\n\n                # Note that we'll unpack batch (and let AMP/FSDP do its thing) in the VLM.forward() call\n                #   => Basically, if we're using mixed precision (or not), autocast()/FSDP will move to device!\n                for train_idx, batch in enumerate(dataloader):\n                    # [Contract] self.vlm.forward() must automatically compute `loss` and return!\n                    with torch.autocast(\n                        \"cuda\",\n                        dtype=self.mixed_precision_dtype,\n                        enabled=self.enable_mixed_precision_training,\n                    ):\n                        output: CausalLMOutputWithPast = self.vlm(\n                            input_ids=batch[\"input_ids\"],\n                            attention_mask=batch[\"attention_mask\"],\n                            pixel_values=batch[\"pixel_values\"],\n                            labels=batch[\"labels\"],\n                            multimodal_indices=batch[\"multimodal_indices\"],\n                        )\n                        loss = output.loss\n\n                    # Commit Loss (Prior to Gradient Accumulation Normalization)\n                    metrics.commit(loss=loss)\n\n                    # Normalize Loss to account for Gradient Accumulation --> Backward!\n                    # [IMPORTANT] Technically speaking, doing gradient accumulation in this way is \"incorrect\"; this is\n                    #             because in general, each batch has a *different number of masked out tokens* (because\n                    #             we're instruct-tuning). Taking the mean over two unbalanced means != the right thing!\n                    #\n                    #             HOWEVER -- at least at the 7B scale, the \"naive\" approach is just as performant as\n                    #             the \"correct\" implementation, without adding extra complexity.\n                    #\n                    # That being said =>> at the 13B scale, *no matter what we tried, ANY gradient accumulation is just\n                    #   really bad for downstream performance. Initial investigation shows that BF16 accumulation\n                    #   just really tanks in precision... and don't have a good/clean way to fix this. Would love for\n                    #   someone to PR and fix this (and I'd greatly appreciate it!!!)\n                    normalized_loss = loss / self.grad_accumulation_steps\n                    normalized_loss.backward()\n\n                    # Step =>> Only if Done w/ Gradient Accumulation\n                    if (train_idx + 1) % self.grad_accumulation_steps == 0:\n                        metrics.commit(update_step_time=True)\n\n                        # Clip Gradients --> this is custom, per-strategy because of DDP vs. FSDP locality-assumptions\n                        self.clip_grad_norm()\n\n                        # Optimizer & LR Scheduler Step\n                        self.optimizer.step()\n                        self.lr_scheduler.step()\n                        self.optimizer.zero_grad()\n\n                        # Push Metrics\n                        metrics.commit(global_step=metrics.global_step + 1, lr=self.lr_scheduler.get_last_lr()[0])\n                        status = metrics.push()\n\n                        # Check for Termination & Save Final Checkpoint (in case `max_steps` is not None)\n                        if self.max_steps is not None and metrics.global_step >= self.max_steps:\n                            self.save_checkpoint(metrics.run_dir, metrics.global_step, epoch, loss.item())\n                            dist.barrier()\n\n                            return\n\n                        # Update Progress Bar\n                        progress.update()\n                        progress.set_description(status)\n\n            # Save checkpoint at end each epoch (if `self.max_steps` is None)\n            if self.max_steps is None:\n                self.save_checkpoint(metrics.run_dir, metrics.global_step, epoch, loss.item())\n                dist.barrier()\n\n    def save_checkpoint(\n            self,\n            run_dir: Path,\n            global_step: int,\n            epoch: int,\n            train_loss: Optional[float] = None,\n            only_trainable: bool = True,\n        ) -> None:\n            \"\"\"Save a checkpoint to the `run_dir` only containing the state_dicts for trainable parameters by default.\"\"\"\n            assert isinstance(self.vlm, FSDP), \"FSDPStrategy.save_checkpoint assumes VLM is already wrapped in FSDP!\"\n\n            # Summon Full State Dictionary =>> Reconstitute from Shards\n            with FSDP.state_dict_type(self.vlm, self.fsdp_state_dict_type, self.fsdp_save_policy):\n                full_vlm_state_dict = self.vlm.state_dict()\n                model_state_dicts = {\n                    mkey: OrderedDict() for mkey in (self.trainable_module_keys if only_trainable else self.all_module_keys)\n                }\n\n                # Iterate through `full_vlm_state_dict` and split `mkey.{full_dotted_path}` -> `mkey: {full_dotted_path}`\n                for key, param in full_vlm_state_dict.items():\n                    for mkey in model_state_dicts:\n                        if key.startswith(mprefix := f\"{mkey}.\"):\n                            model_state_dicts[mkey][key.removeprefix(mprefix)] = param\n\n                # Save on rank zero *only*\n                if overwatch.is_rank_zero():\n                    checkpoint_dir = run_dir / \"checkpoints\"\n                    if train_loss is None:\n                        checkpoint_path = checkpoint_dir / f\"step-{global_step:06d}-epoch-{epoch:02d}-loss=inf.pt\"\n                    else:\n                        checkpoint_path = (\n                            checkpoint_dir / f\"step-{global_step:06d}-epoch-{epoch:02d}-loss={train_loss:.4f}.pt\"\n                        )\n\n                    # Save Checkpoint & Copy Latest to `latest-checkpoint.pt`\n                    torch.save({\"model\": model_state_dicts}, checkpoint_path)\n\n                    # TODO (siddk) :: This breaks w/ Sagemaker default permissions (root vs. <user>)... skip?\n                    # shutil.copy(checkpoint_path, checkpoint_dir / \"latest-checkpoint.pt\")\n\n    def run_setup(self, run_dir: Path, n_train_examples: int) -> None:\n        # Iteratively Assemble FSDP Wrapping Policy by fetching the wrapping policies for each backbone/constituent\n        vlm_fsdp_wrapping_policy = self.vlm.get_fsdp_wrapping_policy()\n\n        # Assemble the Default FSDP Mixed Precision Policy\n        if self.enable_mixed_precision_training and self.mixed_precision_dtype == torch.bfloat16:\n            # MixedPrecision `param_dtype` specifies *compute* dtype (for forward/backward only)\n            #   => Reference: https://pytorch.org/docs/stable/fsdp.html#torch.distributed.fsdp.MixedPrecision\n            reduce_buffer_dtype = torch.bfloat16 if not self.reduce_in_full_precision else torch.float32\n            fsdp_precision_policy = MixedPrecision(\n                param_dtype=torch.bfloat16, reduce_dtype=reduce_buffer_dtype, buffer_dtype=reduce_buffer_dtype\n            )\n\n            # When running FSDP with a frozen vision backbone --> move to half precision!\n            if self.stage not in {\"full-finetune\", \"vla-full-train\", \"vla-sandwich-train\"}:\n                overwatch.info(\"Casting Vision Backbone to *Half Precision* via `.to(dtype=...)`\")\n                self.vlm.vision_backbone.to(dtype=self.vlm.vision_backbone.half_precision_dtype)\n\n        else:\n            # If we're not using mixed precision, everything is in default full precision!\n            fsdp_precision_policy = MixedPrecision(\n                param_dtype=torch.float32, reduce_dtype=torch.float32, buffer_dtype=torch.float32\n            )\n\n        # <FSDP> => note that FSDP will automatically take care of device placement (similar to `autocast`)\n        self.vlm = FSDP(\n            self.vlm,\n            auto_wrap_policy=vlm_fsdp_wrapping_policy,\n            mixed_precision=fsdp_precision_policy,\n            sharding_strategy=self.fsdp_sharding_strategy,\n            device_id=torch.cuda.current_device(),\n            limit_all_gathers=True,\n            use_orig_params=True,\n        )\n\n        # Gradient Checkpoint Setup\n        if self.enable_gradient_checkpointing:\n            # For Gradient Checkpointing under FSDP --> we make the same assumption as in the DDP/other strategies; the\n            #   bulk of activation memory is taken up by the LLM activations. However, unlike other strategies, we\n            #   cannot rely on the HF Transformers default `gradient_checkpointing_enable()` --> FSDP breaks semantics!\n            #\n            # Instead, we need to write our own *NO-REENTRANT* wrapper, and apply it to the LLM's Transformer Layer.\n            non_reentrant_wrapper = partial(checkpoint_wrapper, checkpoint_impl=CheckpointImpl.NO_REENTRANT)\n\n            def check_fn(submodule: nn.Module) -> bool:\n                return isinstance(submodule, self.llm_transformer_layer_cls)\n\n            # Note that the terms \"activation checkpointing\" and \"gradient checkpointing\" are synonymous!\n            apply_activation_checkpointing(self.vlm, checkpoint_wrapper_fn=non_reentrant_wrapper, check_fn=check_fn)\n\n        # Barrier =>> Sharding takes a minute?\n        dist.barrier()\n\n        # Create Optimizer and LR Scheduler =>> note that most of the LR Schedulers we use require `max_steps/epochs`\n        #   => Optimizer should only operate on parameters that are *unfrozen* / trainable!\n        n_train_examples = math.ceil(n_train_examples / self.global_batch_size) * self.global_batch_size\n        if self.max_steps is None:\n            num_training_steps = (n_train_examples * self.epochs) // self.global_batch_size\n        else:\n            num_training_steps = self.max_steps\n\n        if self.lr_scheduler_type == \"linear-warmup+cosine-decay\":\n            # Set warmup steps (floor) based on `warmup_ratio` (should be 0.03 - 0.05)\n            num_warmup_steps = int(num_training_steps * self.warmup_ratio)\n\n            # Default AdamW w/ specified LR & Linear Warmup / Cosine Decay & Weight Decay\n            #   => Create Parameter Groups --> bias terms, normalization layer parameters shouldn't be decayed!\n            decay, no_decay = [], []\n            for name, param in self.vlm.named_parameters():\n                if not param.requires_grad:\n                    continue\n\n                # Check on any parameters with fewer than 2 dimensions or with \"bias\" in the name\n                if param.ndim <= 1 or name.endswith(\".bias\"):\n                    no_decay.append(param)\n                else:\n                    decay.append(param)\n\n            # Build Parameter Groups\n            groups = [{\"params\": decay, \"weight_decay\": self.weight_decay}, {\"params\": no_decay, \"weight_decay\": 0.0}]\n\n            # Create Optimizer & LR Scheduler\n            self.optimizer = AdamW(groups, lr=self.learning_rate)\n            self.lr_scheduler = get_cosine_schedule_with_warmup(self.optimizer, num_warmup_steps, num_training_steps)\n            for param_group in self.optimizer.param_groups:\n                param_group[\"lr\"] = 0.0\n\n        elif self.lr_scheduler_type == \"constant\":\n            num_warmup_steps = 0\n\n            # Default AdamW w/ specified LR & Linear Warmup / Cosine Decay & Weight Decay\n            #   => Create Parameter Groups --> bias terms, normalization layer parameters shouldn't be decayed!\n            decay, no_decay = [], []\n            for name, param in self.vlm.named_parameters():\n                if not param.requires_grad:\n                    continue\n\n                # Check on any parameters with fewer than 2 dimensions or with \"bias\" in the name\n                if param.ndim <= 1 or name.endswith(\".bias\"):\n                    no_decay.append(param)\n                else:\n                    decay.append(param)\n\n            # Build Parameter Groups\n            groups = [{\"params\": decay, \"weight_decay\": self.weight_decay}, {\"params\": no_decay, \"weight_decay\": 0.0}]\n\n            # Create Optimizer & LR Scheduler\n            self.optimizer = AdamW(groups, lr=self.learning_rate)\n            self.lr_scheduler = get_constant_schedule(self.optimizer)\n\n        else:\n            raise ValueError(f\"Learning Rate Schedule with type `{self.lr_scheduler_type}` is not supported!\")\n\n        # Finalize Setup =>> Log!\n        overwatch.info(\n            \"FSDP Full-Shard Strategy =>> Finalized Training Setup:\\n\"\n            f\"         |-> Global (Effective) Batch Size = {self.global_batch_size}\\n\"\n            f\"         |-> Per-Device Batch Size = {self.per_device_batch_size}\\n\"\n            f\"         |-> Distributed World Size = {overwatch.world_size()}\\n\"\n            f\"         |-> Gradient Accumulation Steps = {self.grad_accumulation_steps}\\n\\n\"\n            f\"         |-> LLM Backbone FSDP Gradient Checkpointing = {self.enable_gradient_checkpointing}\\n\"\n            f\"         |-> Use FSDP Mixed Precision = {self.enable_mixed_precision_training}\\n\"\n            f\"                 |-> Parameter Precision = {fsdp_precision_policy.param_dtype}\\n\"\n            f\"                 |-> Reduction Precision = {fsdp_precision_policy.reduce_dtype}\\n\"\n            f\"                 |-> Buffer Precision = {fsdp_precision_policy.buffer_dtype}\\n\\n\"\n            f\"         |-> Default AdamW LR = {self.learning_rate}\\n\"\n            f\"         |-> AdamW Weight Decay = {self.weight_decay}\\n\"\n            f\"         |-> LR Scheduler Type = {self.lr_scheduler_type}\\n\"\n            f\"         |-> LR Scheduler Warmup Steps (Ratio) = {num_warmup_steps} ({self.warmup_ratio})\\n\"\n            f\"         |-> Dataset Size = {n_train_examples} Examples\\n\"\n            f\"         |-> Max Steps = {num_training_steps}\\n\"\n        )\n\n    def clip_grad_norm(self) -> None:\n        # Note =>> FSDP uses a custom `clip_grad_norm_` function; requires *uniform grad dtype*\n        self.vlm.clip_grad_norm_(max_norm=self.max_grad_norm)\n\n    def run_vla_training(\n        self,\n        vla_dataset: IterableDataset,\n        collator: PaddedCollatorForActionPrediction,\n        action_tokenizer: ActionTokenizer,\n        metrics: VLAMetrics,\n        save_interval: int = 2500,\n        save_full_model: bool = True,\n        history_frames: int = 2,\n        grid_size: int = 16\n    ) -> None:\n        \"\"\"Run the VLA training loop for the given `dataset` and `collator`; log losses, action metrics to `metrics`.\"\"\"\n        assert isinstance(vla_dataset, IterableDataset), \"VLA training expects an IterableDataset!\"\n        assert self.grad_accumulation_steps == 1, \"VLA training does not support gradient accumulation!\"\n\n        # Create a DataLoader =>> Set `num_workers` to 0; RLDS loader handles parallelism!\n        dataloader = DataLoader(\n            vla_dataset,\n            batch_size=self.per_device_batch_size,\n            sampler=None,\n            collate_fn=collator,\n            worker_init_fn=None,\n        )\n\n        # === Train ===\n        status = metrics.get_status()\n\n        with tqdm(\n            total=(self.epochs * len(dataloader)) if self.max_steps is None else self.max_steps,\n            desc=status,\n            leave=False,\n            disable=not overwatch.is_rank_zero(),\n        ) as progress:\n            self.vlm.train()\n\n            # Zero Gradients (just in case)\n            self.optimizer.zero_grad()\n            # [Contract] DataLoader wraps RLDS Loader (`.as_numpy_iterator() =>> implicit `.repeat()`)\n            #   => This means looping over the DataLoader is basically \"infinite\" (so no outer loop over epochs).\n            #      Slightly breaks default PyTorch semantics, which is why we adaptively compute `epoch` below.\n            for batch in dataloader:\n                # Note that we'll unpack batch (and let AMP/FSDP do its thing) in the VLM.forward() call\n                #   => Basically, if we're using mixed precision (or not), autocast()/FSDP will move to device!\n                with torch.autocast(\n                    \"cuda\", dtype=self.mixed_precision_dtype, enabled=self.enable_mixed_precision_training\n                ):\n                    # [Contract] self.vlm.forward() must automatically compute `loss` and return!\n                       \n                    output: CausalLMOutputWithPast = self.vlm(\n                        input_ids=batch[\"input_ids\"],\n                        attention_mask=batch[\"attention_mask\"],\n                        pixel_values=batch[\"pixel_values\"],\n                        labels=batch[\"labels\"],\n                    )\n                    loss = output.loss\n                # Commit Loss =>> Backward!\n                metrics.commit(loss=loss)\n                loss.backward()\n\n                # === Compute Action Token Accuracy & L1 Loss ===\n\n                # To compute action token accuracy, we need to identify the locations of the action tokens\n                # in both `output.logits` and `batch[\"labels\"]`. We know that when \"right\" padding, we\n                # insert `self.vlm.vision_backbone.num_patches` at index 1.\n                #\n                # Computing `action_prediction_accuracy` is then pretty straightforward:\n                #   1) Extract \"aligned\" predictions & labels\n                #   2) Compute boolean \"mask\" where \"labels > 2\" (where 2 is ID for `EOS_TOKEN`)\n                #           => If masking out EOS, then it's just \"labels != -100 (IGNORE_INDEX)\n                #   3) Compute masked accuracy as `(preds == logits) & mask` --> sum/divide by # unmasked\n                action_preds = output.logits[:, int(history_frames * 256 / grid_size / grid_size + 256): -1].argmax(dim=2)\n                action_gt = batch[\"labels\"][:, 1:].to(action_preds.device)\n                mask = action_gt > action_tokenizer.action_token_begin_idx\n\n                # Compute Accuracy\n                correct_preds = (action_preds == action_gt) & mask\n                action_accuracy = correct_preds.sum().float() / mask.sum().float()\n\n                # Compute L1 Loss on Predicted (Continuous) Actions\n                continuous_actions_pred = torch.tensor(\n                    action_tokenizer.decode_token_ids_to_actions(action_preds[mask].cpu().numpy())\n                )\n                continuous_actions_gt = torch.tensor(\n                    action_tokenizer.decode_token_ids_to_actions(action_gt[mask].cpu().numpy())\n                )\n                action_l1_loss = torch.nn.functional.l1_loss(continuous_actions_pred, continuous_actions_gt)\n\n                # Commit Metrics\n                metrics.commit(action_accuracy=action_accuracy, l1_loss=action_l1_loss, update_step_time=True)\n\n                # Compute metrics per dataset --> only on rank_zero since we don't log them on other workers anyways\n                if overwatch.is_rank_zero():\n                    datasets = set(batch[\"dataset_names\"])\n                    if len(datasets) > 1:\n                        for ds in datasets:\n                            ds_mask = torch.tensor([elem == ds for elem in batch[\"dataset_names\"]])\n                            action_accuracy_ds = correct_preds[ds_mask].sum().float() / mask[ds_mask].sum().float()\n                            continuous_actions_pred_ds = torch.tensor(\n                                action_tokenizer.decode_token_ids_to_actions(\n                                    action_preds[ds_mask][mask[ds_mask]].cpu().numpy()\n                                )\n                            )\n                            continuous_actions_gt_ds = torch.tensor(\n                                action_tokenizer.decode_token_ids_to_actions(\n                                    action_gt[ds_mask][mask[ds_mask]].cpu().numpy()\n                                )\n                            )\n                            action_l1_loss_ds = torch.nn.functional.l1_loss(\n                                continuous_actions_pred_ds, continuous_actions_gt_ds\n                            )\n\n                            metrics.commit_for_dataset(\n                                dataset_name=ds.decode(), action_accuracy=action_accuracy_ds, l1_loss=action_l1_loss_ds\n                            )\n                \n                # === Gradient Step ===\n\n                # Clip Gradients --> this is custom, per-strategy because of DDP vs. FSDP locality assumptions\n                self.clip_grad_norm()\n\n                # Optimizer & LR Scheduler Step\n                self.optimizer.step()\n                self.lr_scheduler.step()\n                self.optimizer.zero_grad()\n\n                # Compute epoch value using number of completed gradient steps\n                epoch = (metrics.global_step + 1) // (len(vla_dataset) // self.global_batch_size)\n\n                # Push Metrics\n                metrics.commit(global_step=metrics.global_step + 1, epoch=epoch, lr=self.lr_scheduler.get_last_lr()[0])\n                status = metrics.push()\n\n                # Check for Save Interval or Max Steps & Save Checkpoint\n                if (terminate := (self.max_steps is not None and metrics.global_step >= self.max_steps)) or (\n                    (metrics.global_step % save_interval) == 0\n                ):\n                    self.save_checkpoint(\n                        metrics.run_dir, metrics.global_step, epoch, loss.item(), only_trainable=not save_full_model\n                    )\n                    dist.barrier()\n\n                    if terminate:\n                        return\n\n                # Update Progress Bar\n                progress.update()\n                progress.set_description(status)\n"
  },
  {
    "path": "train/model/vision_backbone.py",
    "content": "\"\"\"\ndinosiglip_vit.py\n\nVision backbone that returns concatenated features from both DINOv2 and SigLIP.\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom functools import partial\nfrom typing import Callable, Any, Dict, Optional, Protocol, Tuple, Union\n\nimport timm\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom PIL import Image\nfrom timm.models.vision_transformer import Block, VisionTransformer\nfrom torch.distributed.fsdp.wrap import _module_wrap_policy, _or_policy, transformer_auto_wrap_policy\nfrom torchvision.transforms import Compose, Resize\n\n\n# === Interface for an Image Transform ===\nclass ImageTransform(Protocol):\n    def __call__(self, img: Image, **kwargs: str) -> Union[torch.Tensor, Dict[str, torch.Tensor]]: ...\n\n# === Utility Functions for Monkey-Patching ===\ndef unpack_tuple(fn: Callable[[Any], Tuple[Any]]) -> Callable[[Any], Any]:\n    def wrapper(*args: Any, **kwargs: Any) -> Any:\n        result = fn(*args, **kwargs)\n        return result[0] if isinstance(result, tuple) else result\n\n    return wrapper\n\n@dataclass\nclass DinoSigLIPImageTransform:\n    dino_image_transform: ImageTransform\n    siglip_image_transform: ImageTransform\n    is_prismatic: bool = True\n\n    def __call__(self, img: Image, **kwargs: str) -> Dict[str, torch.Tensor]:\n        return {\"dino\": self.dino_image_transform(img, **kwargs), \"siglip\": self.siglip_image_transform(img, **kwargs)}\n\n\nclass DinoSigLIPViTBackbone(nn.Module):\n    def __init__(self, image_resize_strategy: str, default_image_size: int = 224, grid_size: int = 16) -> None:\n        super().__init__()\n        self.image_resize_strategy: str = image_resize_strategy\n        self.default_image_size: int = default_image_size\n\n        self.dino_timm_path_or_url = \"vit_large_patch14_reg4_dinov2.lvd142m\"\n        self.siglip_timm_path_or_url = \"vit_so400m_patch14_siglip_224\"\n        self.grid_size = grid_size\n\n        # Initialize both Featurizers (ViTs) by downloading from HF / TIMM Hub if necessary\n        self.dino_featurizer: VisionTransformer = timm.create_model(\n            self.dino_timm_path_or_url, pretrained=True, num_classes=0, img_size=self.default_image_size\n        )\n        self.dino_featurizer.eval()\n\n        self.siglip_featurizer: VisionTransformer = timm.create_model(\n            self.siglip_timm_path_or_url, pretrained=True, num_classes=0, img_size=self.default_image_size\n        )\n        self.siglip_featurizer.eval()\n\n        # Monkey-Patch the `forward()` function of the featurizers to ensure FSDP-compatibility\n        #   => Note: By default set `get_intermediate_layers` to return the *SECOND-TO-LAST* layer patches!\n        #   => TODO (siddk) Remove after resolution of https://github.com/pytorch/pytorch/issues/109385\n        self.dino_featurizer.forward = unpack_tuple(\n            partial(self.dino_featurizer.get_intermediate_layers, n={len(self.dino_featurizer.blocks) - 2})\n        )\n        self.siglip_featurizer.forward = unpack_tuple(\n            partial(self.siglip_featurizer.get_intermediate_layers, n={len(self.siglip_featurizer.blocks) - 2})\n        )\n\n        # Get Configs for _both_ Featurizers =>> Note :: Override default image size for larger resolution models\n        self.dino_data_cfg = timm.data.resolve_model_data_config(self.dino_featurizer)\n        self.dino_data_cfg[\"input_size\"] = (3, self.default_image_size, self.default_image_size)\n\n        self.siglip_data_cfg = timm.data.resolve_model_data_config(self.siglip_featurizer)\n        self.siglip_data_cfg[\"input_size\"] = (3, self.default_image_size, self.default_image_size)\n\n        # Initialize *both* Transforms\n        default_dino_transform = timm.data.create_transform(**self.dino_data_cfg, is_training=False)\n        default_siglip_transform = timm.data.create_transform(**self.siglip_data_cfg, is_training=False)\n\n        # Fix =>> SigLIP default transform resizes to *larger* than `self.default_image_size` (crops image)!!\n        assert isinstance(default_siglip_transform, Compose), \"Unexpected `default_image_transform`!\"\n        assert isinstance(default_siglip_transform.transforms[0], Resize)\n        default_siglip_transform = Compose(\n            [\n                Resize(self.default_image_size, interpolation=default_siglip_transform.transforms[0].interpolation),\n                *default_siglip_transform.transforms[1:],\n            ]\n        )\n\n        if self.image_resize_strategy == \"resize-naive\":\n            assert isinstance(default_dino_transform, Compose), \"Unexpected `default_dino_image_transform`!\"\n            assert isinstance(default_siglip_transform, Compose), \"Unexpected `default_siglip_image_transform`!\"\n            assert isinstance(default_dino_transform.transforms[0], Resize)\n            assert isinstance(default_siglip_transform.transforms[0], Resize)\n\n            target_size = (self.default_image_size, self.default_image_size)\n            dino_transform = Compose(\n                [\n                    Resize(target_size, interpolation=default_dino_transform.transforms[0].interpolation),\n                    *default_dino_transform.transforms[1:],\n                ]\n            )\n            siglip_transform = Compose(\n                [\n                    Resize(target_size, interpolation=default_siglip_transform.transforms[0].interpolation),\n                    *default_siglip_transform.transforms[1:],\n                ]\n            )\n\n            self.image_transform = DinoSigLIPImageTransform(dino_transform, siglip_transform)\n\n        elif self.image_resize_strategy == \"resize-crop\":\n            self.image_transform = DinoSigLIPImageTransform(default_dino_transform, default_siglip_transform)\n\n        elif self.image_resize_strategy == \"letterbox\":\n            assert isinstance(default_dino_transform, Compose), \"Unexpected `default_dino_transform`!\"\n            assert isinstance(default_siglip_transform, Compose), \"Unexpected `default_siglip_transform`!\"\n            assert (\n                \"mean\" in self.dino_data_cfg and \"mean\" in self.siglip_data_cfg\n            ), \"DinoSigLIP `data_cfg` missing `mean`!\"\n\n            # Compute Padding Fill Value(s) (rescaled normalization mean if applicable)\n            dino_fill = tuple([int(x * 255) for x in self.dino_data_cfg[\"mean\"]])\n            siglip_fill = tuple([int(x * 255) for x in self.siglip_data_cfg[\"mean\"]])\n\n            # Build New Transform\n            self.image_transform = DinoSigLIPImageTransform(\n                Compose([LetterboxPad(dino_fill), *default_dino_transform.transforms]),\n                Compose([LetterboxPad(siglip_fill), *default_siglip_transform.transforms]),\n            )\n\n        else:\n            raise ValueError(f\"Image Resize Strategy `{self.image_resize_strategy}` is not supported!\")\n\n    def get_fsdp_wrapping_policy(self) -> Callable:\n        \"\"\"Return a simple FSDP policy that wraps each ViT block and then both of the _entire_ featurizers.\"\"\"\n        vit_wrap_policy = partial(_module_wrap_policy, module_classes={VisionTransformer})\n        transformer_block_policy = partial(transformer_auto_wrap_policy, transformer_layer_cls={Block})\n        return partial(_or_policy, policies=[vit_wrap_policy, transformer_block_policy])\n\n    def forward(self, pixel_values: Dict[str, torch.Tensor]) -> torch.Tensor:\n        \"\"\"Runs the transformed image/pixel tensors through each vision backbone, returning concatenated patches.\"\"\"\n        dino_patches = self.post_process(self.dino_featurizer(pixel_values['dino'][:, 0:3]), 1)\n        siglip_patches = self.post_process(self.siglip_featurizer(pixel_values['siglip'][:, 0:3]), 1)\n\n        dino_patches_his1 = self.post_process(self.dino_featurizer(pixel_values['dino'][:, 3:6]), self.grid_size)\n        siglip_patches_his1 = self.post_process(self.siglip_featurizer(pixel_values['siglip'][:, 3:6]), self.grid_size)\n            \n        dino_patches_his2 = self.post_process(self.dino_featurizer(pixel_values['dino'][:, 6:9]), self.grid_size)\n        siglip_patches_his2 = self.post_process(self.siglip_featurizer(pixel_values['siglip'][:, 6:9]), self.grid_size)\n\n        return [torch.cat([dino_patches, siglip_patches], dim=-1), torch.cat([dino_patches_his1, siglip_patches_his1], dim=-1), torch.cat([dino_patches_his2, siglip_patches_his2], dim=-1)] \n\n\n    @property\n    def default_image_resolution(self) -> Tuple[int, int, int]:\n        return self.dino_data_cfg[\"input_size\"]\n\n    @property\n    def embed_dim(self) -> int:\n        return self.dino_featurizer.embed_dim + self.siglip_featurizer.embed_dim\n\n    @property\n    def num_patches(self) -> int:\n        assert self.dino_featurizer.patch_embed.num_patches == self.siglip_featurizer.patch_embed.num_patches\n        return self.dino_featurizer.patch_embed.num_patches\n\n    @property\n    def half_precision_dtype(self) -> torch.dtype:\n        return torch.bfloat16\n\n    def post_process(self, tensorA, grid_size, HW=16):\n        B, _, C = tensorA.size()\n        tensorA_like = tensorA.view(B, HW, HW, C)\n        pooled_tensorA = F.avg_pool2d(tensorA_like.permute(0, 3, 1, 2), padding=0, kernel_size=grid_size)\n        result = pooled_tensorA.permute(0, 2, 3, 1).flatten(1,2)\n        return result\n\n    def get_image_transform(self) -> ImageTransform:\n        return self.image_transform\n\n"
  },
  {
    "path": "train/train.py",
    "content": "import json\nimport os\nimport re\nfrom dataclasses import dataclass, field\nfrom pathlib import Path\nfrom typing import Optional, Tuple, Union\nimport datetime\nimport draccus\nimport torch\nimport torch.distributed as dist\nimport yaml\nimport transformers\nfrom transformers import AutoConfig, AutoTokenizer, PreTrainedModel, PreTrainedTokenizerBase\nfrom transformers import AutoModelForVision2Seq, AutoProcessor, BitsAndBytesConfig\nfrom transformers import HfArgumentParser, set_seed\nfrom transformers.modeling_outputs import CausalLMOutputWithPast\nfrom model.config import OpenFlyConfig\nfrom model.overwatch import initialize_overwatch\nfrom model.load_model import load_vla\nfrom datasets import save_dataset_statistics\nfrom datasets import get_vla_dataset_and_collator, make_dataset_from_rlds, make_interleaved_dataset, get_oxe_dataset_kwargs_and_weights\n\nfrom model.strategy import TrainingStrategy\nfrom model.metrics import VLAMetrics\n\n\n\n@dataclass\nclass DataArguments:\n    \n    # Directory Paths\n    data_root_dir: Path = Path(                                 \n        \"tensorflow_dataset\"\n    )\n    run_root_dir: Path = Path(\"runs\")                                                              \n\n    data_mix: str = \"vln_mix\"        \n\n    shuffle_buffer_size: int = 256_000  \n    resume_step: Optional[int] = None                           \n    resume_epoch: Optional[int] = None                          \n\n    # Run Arguments                    \n    save_interval: int = 5000                                   \n    image_aug: bool = False                                           \n\n    # Tracking Parameters\n    trackers: Tuple[str, ...] = (\"jsonl\", \"wandb\")              \n    wandb_project: str = \"openvla\"                              \n    wandb_entity: str = \"stanford-voltron\"                      \n\n\n@dataclass\nclass TrainingArguments:\n    \n    seed: int = 7                                            \n    pretrained_checkpoint: Optional[Path] = None   \n    hf_token = \"\"                 \n    is_resume: bool = True   \n\n    freeze_vision_backbone: bool = False                   \n    freeze_llm_backbone: bool = False                \n    unfreeze_last_llm_layer: bool = False\n    \n    epochs: int = 1000                      \n    max_steps: Optional[int] = None         \n\n    expected_world_size: int = 8            \n    global_batch_size: int = 128            \n    per_device_batch_size: int = 16                     \n    learning_rate: float = 2e-5 \n    weight_decay: float = 0.0  \n    max_grad_norm: float = 1.0 \n    lr_scheduler_type: str = \"constant\"\n    warmup_ratio: float = 0.0  \n\n    train_strategy: str = \"fsdp-full-shard\"  \n    enable_gradient_checkpointing: bool = True      \n    enable_mixed_precision_training: bool = True    \n    reduce_in_full_precision: bool = True       \n    mixed_precision_dtype: torch.dtype = torch.bfloat16\n\n    grid_size: int = 16\n    history_frames: int = 2\n    # fmt: on\n\n\n\ndef main(data_args=None, training_args=None):\n\n    # Initialize Overwatch =>> Wraps `logging.Logger`\n    overwatch = initialize_overwatch(__name__)\n    \n    torch.cuda.set_device(device_id := overwatch.local_rank())\n    torch.cuda.empty_cache()\n    run_id = (\n        f\"n{training_args.expected_world_size // 8}+b{training_args.per_device_batch_size}+x{training_args.seed}\"\n    )\n\n\n    worker_init_fn = None\n\n    os.makedirs(run_dir := (data_args.run_root_dir / run_id), exist_ok=True)\n    os.makedirs(data_args.run_root_dir / run_id / \"checkpoints\", exist_ok=True)\n\n    model = load_vla(training_args.pretrained_checkpoint, hf_token=training_args.hf_token, load_for_training=True, grid_size=training_args.grid_size)\n\n    for param in model.parameters():\n        assert param.dtype == torch.float32, f\"Loaded VLM parameter not in full precision: {param}\"\n\n    # Determine training \"stage\" based on frozen vs unfrozen parameters --> supports different fine-tuning schemes!\n    if not training_args.freeze_vision_backbone and not training_args.freeze_llm_backbone:\n        stage = \"vla-full-train\"  # Full fine-tuning\n    elif training_args.freeze_vision_backbone and not training_args.freeze_llm_backbone:\n        stage = \"vla-train\"  # Frozen vision encoder\n    elif not training_args.freeze_vision_backbone and training_args.freeze_llm_backbone:\n        assert training_args.unfreeze_last_llm_layer, \"You should unfreeze at least the last layer of your LLM!\"\n        stage = \"vla-sandwich-train\"  # Fine-tuning vision encoder, projector, and LLM last layer\n    elif training_args.freeze_vision_backbone and training_args.freeze_llm_backbone:\n        assert training_args.unfreeze_last_llm_layer, \"Need to unfreeze at least last LLM layer to train!\"\n        stage = \"vla-last-layer-train\"  # Fine-tuning LLM last layer only\n    else:\n        raise ValueError(\n            \"Weight freezing configuration not supported. VLA config has the following parameters: \"\n            f\"freeze_vision_backbone: {training_args.freeze_vision_backbone}\"\n            f\"freeze_llm_backbone: {training_args.freeze_llm_backbone}\"\n            f\"unfreeze_last_llm_layer: {training_args.unfreeze_last_llm_layer}\"\n        )\n\n    # [Explicit] Call to `freeze_backbones` here for clarity =>> will log exactly what is/is not frozen\n    overwatch.info(f\"Stage Info: \")\n    model.freeze_backbones(stage)\n\n    # Print number of total/trainable model parameters\n    num_params = sum(p.numel() for p in model.parameters())\n    num_trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n    overwatch.info(\n        f\"# Parameters (in millions): {num_params / 10**6:.3f} Total, {num_trainable_params / 10**6:.3f} Trainable\"\n    )\n\n    vla_dataset, action_tokenizer, collator = get_vla_dataset_and_collator(\n        data_args.data_root_dir,\n        data_args.data_mix,\n        image_transform=model.vision_backbone.get_image_transform(),\n        tokenizer=model.llm_backbone.get_tokenizer(),\n        default_image_resolution=model.vision_backbone.default_image_resolution,\n        shuffle_buffer_size=data_args.shuffle_buffer_size,\n        image_aug=data_args.image_aug,\n    )\n\n    if overwatch.is_rank_zero():\n        save_dataset_statistics(vla_dataset.dataset_statistics, run_dir)\n\n    train_strategy = TrainingStrategy(\n        vlm=model,\n        device_id=device_id,\n        stage=stage,\n        epochs=training_args.epochs,\n        max_steps=training_args.max_steps,\n        global_batch_size=training_args.global_batch_size,\n        per_device_batch_size=training_args.per_device_batch_size,\n        learning_rate=training_args.learning_rate,\n        weight_decay=training_args.weight_decay,\n        max_grad_norm=training_args.max_grad_norm,\n        lr_scheduler_type=training_args.lr_scheduler_type,\n        warmup_ratio=training_args.warmup_ratio,\n        enable_gradient_checkpointing=training_args.enable_gradient_checkpointing,\n        enable_mixed_precision_training=training_args.enable_mixed_precision_training,\n        reduce_in_full_precision=training_args.reduce_in_full_precision,\n        mixed_precision_dtype=training_args.mixed_precision_dtype,\n        worker_init_fn=worker_init_fn,\n        sharding_strategy=\"full-shard\",\n    )\n    train_strategy.run_setup(run_dir=run_dir, n_train_examples=len(vla_dataset))\n\n    metrics = VLAMetrics(\n        data_args.trackers,\n        run_id,\n        run_dir,\n        draccus.encode(data_args),\n        wandb_project=data_args.wandb_project,\n        wandb_entity=data_args.wandb_entity,\n        resume_step=data_args.resume_step,\n        resume_epoch=data_args.resume_epoch,\n    )\n\n    train_strategy.run_vla_training(\n        vla_dataset,\n        collator,\n        action_tokenizer,\n        metrics,\n        save_interval=data_args.save_interval,\n        history_frames=training_args.history_frames,\n        grid_size=training_args.grid_size,\n    )\n\n    metrics.finalize()\n    dist.barrier()\n    dist.destroy_process_group()\n    \n\nif __name__ == '__main__':\n    parser = transformers.HfArgumentParser(\n        (DataArguments, TrainingArguments))\n    data_args, training_args = parser.parse_args_into_dataclasses()\n    main(data_args, training_args)\n"
  },
  {
    "path": "train/train.sh",
    "content": "#! /bin/bash\n\ntorchrun --standalone --nnodes 1 --nproc-per-node 8 train.py \\\n  --grid_size 16 \\\n  --history_frames 2 \\\n  --pretrained_checkpoint YOUR_CHECKPOINT_PATH \\\n\n"
  }
]